From 2299bb7024415bf1ce8c109ccb297efd913a9931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20=C4=8Cech?= Date: Wed, 1 Jul 2026 15:43:00 +0200 Subject: [PATCH] fix(docs): generate method sub-pages for every class in API reference Inherited methods shared across many catalog classes (e.g. client_class, from_api, to_dict) share a name. The API reference builder deduped by the global links dict and skipped page creation for all but the first class, yet every class still rendered a relative link to its own method sub-page (), producing 404s on all other classes. Dedup now applies only to the global links dict used for docstring linkification; each class always gets its own method sub-pages. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/docs/python_ref_builder.py | 16 ++++++++++----- scripts/docs/tests/test_python_ref_builder.py | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/scripts/docs/python_ref_builder.py b/scripts/docs/python_ref_builder.py index f90fc6d65..914b89239 100644 --- a/scripts/docs/python_ref_builder.py +++ b/scripts/docs/python_ref_builder.py @@ -368,12 +368,18 @@ def _pass1(data_root: dict, dir_root: Path, api_ref_root: str, module_import_pat elif name == "functions": for func_name in obj: - if func_name.startswith("_") or func_name in links: + if func_name.startswith("_"): continue - links[func_name] = { - "path": f"{api_ref_root}/{func_name}".lower(), - "kind": "function", - } + # Dedup only the global links dict (used for docstring + # linkification). Page creation must still happen for every + # class, otherwise inherited methods shared across classes + # (e.g. client_class) get a page under only the first class + # while every other class emits a relative link that 404s. + if func_name not in links: + links[func_name] = { + "path": f"{api_ref_root}/{func_name}".lower(), + "kind": "function", + } pages.append( _PageSpec( kind="function", diff --git a/scripts/docs/tests/test_python_ref_builder.py b/scripts/docs/tests/test_python_ref_builder.py index cb4935314..26ba15e22 100644 --- a/scripts/docs/tests/test_python_ref_builder.py +++ b/scripts/docs/tests/test_python_ref_builder.py @@ -398,3 +398,23 @@ def test_duplicate_names_skipped(self, _mod, tmp_path): # Should not raise — second "Shared" is skipped _mod.create_file_structure(data, tmp_path, "/latest/api-reference") assert (tmp_path / "mod1" / "Shared" / "_index.md").exists() + + def test_shared_method_gets_page_under_every_class(self, _mod, tmp_path): + # Methods inherited across many classes (e.g. client_class) share a name. + # Each class lists the method with a relative link, so each class must get + # its own method page — deduping only affects the global links dict. + method = {"kind": "function", "docstring_parsed": None, "signature": {}} + data = { + "mod1": { + "kind": "module", + "ClassA": {"kind": "class", "functions": {"client_class": dict(method)}}, + }, + "mod2": { + "kind": "module", + "ClassB": {"kind": "class", "functions": {"client_class": dict(method)}}, + }, + } + _mod.create_file_structure(data, tmp_path, "/latest/api-reference") + # Both classes must have their own client_class page (neither should 404). + assert (tmp_path / "mod1" / "ClassA" / "client_class.md").exists() + assert (tmp_path / "mod2" / "ClassB" / "client_class.md").exists()