Skip to content

Commit 082f3ef

Browse files
committed
extend autodocumenter instead of monkeypatching (cleaner approach), add back docs/ to pyright ignored
1 parent 1eadd02 commit 082f3ef

2 files changed

Lines changed: 32 additions & 41 deletions

File tree

docs/conf.py

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# pyright: reportMissingImports=false
21
# Configuration file for the Sphinx documentation builder.
32
#
43
# For the full list of built-in configuration values, see the documentation:
@@ -67,50 +66,45 @@
6766
autodoc_typehints_description_target = "documented"
6867

6968

70-
# -- Patches -----------------------------------------------------------------
69+
# -- Autodocumenter extensions -----------------------------------------------
7170

7271

73-
def _patch_autotypeddict_inherited_docstrings() -> None:
74-
"""Patch autotypeddict to include field docstrings from parent TypedDicts.
72+
def _collect_field_docstrings(cls: type) -> dict[str, list[str]]:
73+
"""Collect field docstrings from cls and all TypedDict ancestors via __orig_bases__."""
74+
result: dict[str, list[str]] = {}
75+
# Parents first — child docstrings overwrite via later assignment
76+
for base in getattr(cls, "__orig_bases__", ()):
77+
origin = getattr(base, "__origin__", base)
78+
if isinstance(origin, type) and origin is not cls:
79+
result.update(_collect_field_docstrings(origin))
80+
try:
81+
attr_docs = ModuleAnalyzer.for_module(cls.__module__).find_attr_docs()
82+
for (_, attr_name), doc_lines in attr_docs.items():
83+
result[attr_name] = doc_lines
84+
except PycodeError:
85+
pass
86+
return result
7587

76-
sphinx_toolbox's sort_members only scans self.object.__module__ for field
77-
docstrings (autotypeddict.py:381-384). SDK TypedDicts like SDKDevboxCreateParams
78-
inherit fields from types in other modules (e.g. DevboxCreateParams), so those
79-
descriptions are lost. This patch collects docstrings from the full
80-
__orig_bases__ chain.
8188

89+
class _InheritedDocsTypedDictDocumenter(TypedDictDocumenter):
90+
"""TypedDictDocumenter that collects field docstrings from parent TypedDicts.
91+
92+
Upstream only scans self.object.__module__ for field docstrings, so
93+
inherited descriptions are lost. This subclass traverses __orig_bases__.
8294
Upstream bug: https://github.com/sphinx-doc/sphinx/issues/9290
95+
Patching sphinx_toolbox 4.1.2.
8396
"""
8497

85-
def _collect_field_docstrings(cls: type) -> dict[str, list[str]]:
86-
result: dict[str, list[str]] = {}
87-
visited: set[type] = set()
88-
89-
def _walk(klass: type) -> None:
90-
if klass in visited or not hasattr(klass, "__annotations__"):
91-
return
92-
visited.add(klass)
93-
try:
94-
for (_, fname), doc in ModuleAnalyzer.for_module(klass.__module__).find_attr_docs().items():
95-
if fname not in result:
96-
result[fname] = doc
97-
except PycodeError:
98-
pass
99-
for base in getattr(klass, "__orig_bases__", []):
100-
if isinstance(base, type):
101-
_walk(base)
102-
103-
_walk(cls)
104-
return result
105-
106-
def _patched_sort_members(
107-
self: TypedDictDocumenter,
98+
def sort_members(
99+
self,
108100
documenters: list[tuple[Documenter, bool]],
109101
order: str,
110102
) -> list[tuple[Documenter, bool]]:
103+
# Skip TypedDictDocumenter.sort_members (returns [] after adding
104+
# lines with wrong docstrings). Call ClassDocumenter.sort_members
105+
# to get the properly sorted documenters list.
111106
documenters = super(TypedDictDocumenter, self).sort_members(documenters, order)
112107
docstrings = _collect_field_docstrings(self.object)
113-
114108
required_keys: list[str] = []
115109
optional_keys: list[str] = []
116110
types = get_type_hints(self.object)
@@ -126,21 +120,16 @@ def _patched_sort_members(
126120
if required_keys:
127121
self.add_line("", sourcename)
128122
self.add_line(":Required Keys:", sourcename)
129-
self.document_keys(required_keys, types, docstrings) # pyright: ignore[reportUnknownMemberType]
123+
self.document_keys(required_keys, types, docstrings)
130124
self.add_line("", sourcename)
131125
if optional_keys:
132126
self.add_line("", sourcename)
133127
self.add_line(":Optional Keys:", sourcename)
134-
self.document_keys(optional_keys, types, docstrings) # pyright: ignore[reportUnknownMemberType]
128+
self.document_keys(optional_keys, types, docstrings)
135129
self.add_line("", sourcename)
136130

137131
return []
138132

139-
TypedDictDocumenter.sort_members = _patched_sort_members # type: ignore[assignment]
140-
141-
142-
_patch_autotypeddict_inherited_docstrings()
143-
144133

145134
# -- Dynamic type documentation ----------------------------------------------
146135

@@ -170,7 +159,8 @@ def _inject_type_submodules(_app: Sphinx, docname: str, source: list[str]) -> No
170159

171160

172161
def setup(app: Sphinx) -> None:
173-
app.connect("source-read", _inject_type_submodules) # pyright: ignore[reportUnknownMemberType]
162+
app.add_autodocumenter(_InheritedDocsTypedDictDocumenter, override=True)
163+
app.connect("source-read", _inject_type_submodules)
174164

175165

176166
# Intersphinx mapping

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ exclude = [
138138
"_dev",
139139
".venv",
140140
".git",
141+
"docs",
141142
]
142143

143144
reportImplicitOverride = true

0 commit comments

Comments
 (0)