From fe06bd9c890588c0b7c1f7292f6208946af877ac Mon Sep 17 00:00:00 2001 From: Bryan Bednarski Date: Fri, 29 May 2026 11:24:17 -0700 Subject: [PATCH 1/4] (fix) public_api pep importatble package fix Signed-off-by: Bryan Bednarski --- .../src/nat/{plugin_api.py => plugin_api/__init__.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/nvidia_nat_core/src/nat/{plugin_api.py => plugin_api/__init__.py} (100%) diff --git a/packages/nvidia_nat_core/src/nat/plugin_api.py b/packages/nvidia_nat_core/src/nat/plugin_api/__init__.py similarity index 100% rename from packages/nvidia_nat_core/src/nat/plugin_api.py rename to packages/nvidia_nat_core/src/nat/plugin_api/__init__.py From 8db1c429d75329d41bcafd0aee359e794f03c4e0 Mon Sep 17 00:00:00 2001 From: Bryan Bednarski Date: Fri, 29 May 2026 13:34:55 -0700 Subject: [PATCH 2/4] fix(docs): skip nat.plugin_api from autoapi to avoid duplicate cross-refs The public plugin-author facade re-exports 71 symbols (Builder, InvocationContext, Function, ...) from their canonical modules. When autoapi generates a reference page for it, every re-export becomes a second cross-reference target alongside the original, and docstrings that reference those classes (e.g. middleware methods typed against ) produce ambiguous-target warnings that in the docs Makefile turns into errors. Previously this was masked: was a top-level .py file at the namespace-package root, and only copies directories, so autoapi never saw it. Converting to (needed to ship the module in the wheel) made it visible to autoapi and surfaced the latent duplicate-target problem. Skip and its members via an callback. Runtime imports and the prose plugin-authoring guide are unaffected; all 71 re-exports remain documented under their canonical modules. A TODO is left for a future docs restructure that would make the canonical reference for plugin authors and invert the resolution direction Signed-off-by: Bryan Bednarski --- docs/source/conf.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index 11a7029717..59b3aecf35 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -574,6 +574,23 @@ def skip_pydantic_special_attrs(app: object, return skip + def skip_plugin_api_module(app: object, + what: str, + name: str, + obj: "PythonObject", + skip: bool, + options: list[str]) -> bool: + # ``nat.plugin_api`` re-exports the public plugin-author facade; its members are + # already documented under their canonical modules, so a second autoapi page here + # produces duplicate cross-reference targets (e.g. ``InvocationContext``) that trip + # the docs build's ``-W`` flag. + # TODO: once the docs are restructured to make ``nat.plugin_api`` the canonical + # reference for plugin authors (with cross-refs pointing here instead of the + # implementation modules), remove this skip and invert the resolution direction. + if name == "nat.plugin_api" or name.startswith("nat.plugin_api."): + return True + return skip + def clean_markdown_from_docstrings(app: object, docname: str, source: list[str]) -> None: """Clean up Markdown syntax that doesn't work in RST. @@ -621,5 +638,7 @@ def convert_code_fence(match: re.Match[str]) -> str: def setup(sphinx): # Work-around for for Pydantic docstrings that trigger parsing warnings sphinx.connect("autoapi-skip-member", skip_pydantic_special_attrs) + # Hide nat.plugin_api facade to avoid duplicate cross-reference targets + sphinx.connect("autoapi-skip-member", skip_plugin_api_module) # Clean up Markdown syntax in auto-generated API docs sphinx.connect("source-read", clean_markdown_from_docstrings) From 2dd764199f514ece31b77faf94c40300a3c0d7d4 Mon Sep 17 00:00:00 2001 From: Bryan Bednarski Date: Fri, 29 May 2026 14:57:26 -0700 Subject: [PATCH 3/4] pre-commit yapf missed Signed-off-by: Bryan Bednarski --- docs/source/conf.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 59b3aecf35..4134f935b6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -574,11 +574,7 @@ def skip_pydantic_special_attrs(app: object, return skip - def skip_plugin_api_module(app: object, - what: str, - name: str, - obj: "PythonObject", - skip: bool, + def skip_plugin_api_module(app: object, what: str, name: str, obj: "PythonObject", skip: bool, options: list[str]) -> bool: # ``nat.plugin_api`` re-exports the public plugin-author facade; its members are # already documented under their canonical modules, so a second autoapi page here From 4a8b0ffda4f5fea8b96ac7dd40033091ff54d91c Mon Sep 17 00:00:00 2001 From: Bryan Bednarski Date: Fri, 29 May 2026 15:55:42 -0700 Subject: [PATCH 4/4] fix logic error on docs skip Signed-off-by: Bryan Bednarski --- docs/source/conf.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 4134f935b6..f1d7007707 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -63,13 +63,21 @@ def _build_api_tree() -> Path: with open(dest_dir / "__init__.py", "w", encoding="utf-8") as f: f.write("") + # Subpackages excluded from the generated API tree. ``nat.plugin_api`` is the public + # plugin-author facade that re-exports symbols from their canonical modules; including it + # here would register duplicate cross-reference targets (e.g. ``InvocationContext`` in both + # ``nat.plugin_api`` and ``nat.middleware.middleware``) and trip the docs build's ``-W``. + # TODO: once the docs are restructured to make ``nat.plugin_api`` the canonical reference + # for plugin authors, remove this exclusion and invert the resolution direction. + skip_from_api_docs = {"plugin_api"} + plugin_dirs = [Path(p) for p in glob.glob(f'{plugins_dir}/nvidia_nat_*')] for plugin_dir in plugin_dirs: src_dir = plugin_dir / 'src/nat' print(f"Copying {src_dir} to {dest_dir}") if src_dir.exists(): for plugin_subdir in src_dir.iterdir(): - if plugin_subdir.is_dir(): + if plugin_subdir.is_dir() and plugin_subdir.name not in skip_from_api_docs: dest_subdir = dest_dir / plugin_subdir.name shutil.copytree(plugin_subdir, dest_subdir, dirs_exist_ok=True) package_file = dest_subdir / "__init__.py" @@ -574,19 +582,6 @@ def skip_pydantic_special_attrs(app: object, return skip - def skip_plugin_api_module(app: object, what: str, name: str, obj: "PythonObject", skip: bool, - options: list[str]) -> bool: - # ``nat.plugin_api`` re-exports the public plugin-author facade; its members are - # already documented under their canonical modules, so a second autoapi page here - # produces duplicate cross-reference targets (e.g. ``InvocationContext``) that trip - # the docs build's ``-W`` flag. - # TODO: once the docs are restructured to make ``nat.plugin_api`` the canonical - # reference for plugin authors (with cross-refs pointing here instead of the - # implementation modules), remove this skip and invert the resolution direction. - if name == "nat.plugin_api" or name.startswith("nat.plugin_api."): - return True - return skip - def clean_markdown_from_docstrings(app: object, docname: str, source: list[str]) -> None: """Clean up Markdown syntax that doesn't work in RST. @@ -634,7 +629,5 @@ def convert_code_fence(match: re.Match[str]) -> str: def setup(sphinx): # Work-around for for Pydantic docstrings that trigger parsing warnings sphinx.connect("autoapi-skip-member", skip_pydantic_special_attrs) - # Hide nat.plugin_api facade to avoid duplicate cross-reference targets - sphinx.connect("autoapi-skip-member", skip_plugin_api_module) # Clean up Markdown syntax in auto-generated API docs sphinx.connect("source-read", clean_markdown_from_docstrings)