Skip to content

Commit 7abd95a

Browse files
authored
Use Sphinx 9 class interface (#589)
1 parent 52114b2 commit 7abd95a

File tree

3 files changed

+33
-59
lines changed

3 files changed

+33
-59
lines changed

src/sphinx_autodoc_typehints/attributes_patch.py

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
from unittest.mock import patch
88

99
import sphinx.domains.python
10-
import sphinx.ext.autodoc
1110
from sphinx.domains.python import PyAttribute
12-
from sphinx.ext.autodoc import AttributeDocumenter
1311

1412
from ._parser import parse
1513

@@ -21,45 +19,16 @@
2119
# Defensively check for the things we want to patch
2220
_parse_annotation = getattr(sphinx.domains.python, "_parse_annotation", None)
2321

24-
# We want to patch:
25-
# * sphinx.ext.autodoc.stringify_typehint (in sphinx < 6.1)
26-
# * sphinx.ext.autodoc.stringify_annotation (in sphinx >= 6.1)
27-
STRINGIFY_PATCH_TARGET = ""
28-
for target in ["stringify_typehint", "stringify_annotation"]:
29-
if hasattr(sphinx.ext.autodoc, target):
30-
STRINGIFY_PATCH_TARGET = f"sphinx.ext.autodoc.{target}"
31-
break
32-
33-
# If we didn't locate both patch targets, we will just do nothing.
34-
OKAY_TO_PATCH = bool(_parse_annotation and STRINGIFY_PATCH_TARGET)
22+
# If we didn't locate the patch target, we will just do nothing.
23+
OKAY_TO_PATCH = bool(_parse_annotation)
3524

3625
# A label we inject to the type string so we know not to try to treat it as a
3726
# type annotation
3827
TYPE_IS_RST_LABEL = "--is-rst--"
3928

40-
41-
orig_add_directive_header = AttributeDocumenter.add_directive_header
4229
orig_handle_signature = PyAttribute.handle_signature
4330

4431

45-
def _stringify_annotation(app: Sphinx, annotation: Any, *args: Any, short_literals: bool = False, **kwargs: Any) -> str: # noqa: ARG001
46-
# Format the annotation with sphinx-autodoc-typehints and inject our magic prefix to tell our patched
47-
# PyAttribute.handle_signature to treat it as rst.
48-
from . import format_annotation # noqa: PLC0415
49-
50-
return TYPE_IS_RST_LABEL + format_annotation(annotation, app.config, short_literals=short_literals)
51-
52-
53-
def patch_attribute_documenter(app: Sphinx) -> None:
54-
"""Instead of using stringify_typehint in `AttributeDocumenter.add_directive_header`, use `format_annotation`."""
55-
56-
def add_directive_header(*args: Any, **kwargs: Any) -> Any:
57-
with patch(STRINGIFY_PATCH_TARGET, partial(_stringify_annotation, app)):
58-
return orig_add_directive_header(*args, **kwargs)
59-
60-
AttributeDocumenter.add_directive_header = add_directive_header # type:ignore[method-assign]
61-
62-
6332
def rst_to_docutils(settings: Values, rst: str) -> Any:
6433
"""Convert rst to a sequence of docutils nodes."""
6534
doc = parse(rst, settings)
@@ -84,12 +53,11 @@ def patched_handle_signature(self: PyAttribute, sig: str, signode: desc_signatur
8453
return orig_handle_signature(self, sig, signode)
8554

8655

87-
def patch_attribute_handling(app: Sphinx) -> None:
88-
"""Use format_signature to format class attribute type annotations."""
56+
def patch_attribute_handling(app: Sphinx) -> None: # noqa: ARG001
57+
"""Patch PyAttribute.handle_signature to format class attribute type annotations."""
8958
if not OKAY_TO_PATCH:
9059
return
9160
PyAttribute.handle_signature = patched_handle_signature # type:ignore[method-assign]
92-
patch_attribute_documenter(app)
9361

9462

9563
__all__ = ["patch_attribute_handling"]

src/sphinx_autodoc_typehints/patches.py

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,6 @@
1717
from sphinx.ext.autodoc import Options
1818

1919

20-
@lru_cache # A cute way to make sure the function only runs once.
21-
def fix_autodoc_typehints_for_overloaded_methods() -> None:
22-
"""
23-
sphinx-autodoc-typehints responds to the "autodoc-process-signature" event to remove types from the signature line.
24-
25-
Normally, `FunctionDocumenter.format_signature` and `MethodDocumenter.format_signature` call
26-
`super().format_signature` which ends up going to `Documenter.format_signature`, and this last method emits the
27-
`autodoc-process-signature` event. However, if there are overloads, `FunctionDocumenter.format_signature` does
28-
something else and the event never occurs.
29-
30-
We delete the format_signature methods to force using the parent implementation, which emits the event.
31-
32-
See https://github.com/tox-dev/sphinx-autodoc-typehints/issues/296
33-
"""
34-
from sphinx.ext.autodoc import FunctionDocumenter, MethodDocumenter # noqa: PLC0415
35-
36-
# Delete the format_signature methods that add overloads
37-
# This forces everything to use Documenter.format_signature which emits the event
38-
del FunctionDocumenter.format_signature
39-
del MethodDocumenter.format_signature
4020

4121

4222
def napoleon_numpy_docstring_return_type_processor( # noqa: PLR0913, PLR0917
@@ -138,13 +118,41 @@ def _patch_line_numbers() -> None:
138118
Body.doctest = _patched_body_doctest # type: ignore[method-assign]
139119

140120

121+
@lru_cache
122+
def fix_directive_based_signature_formatting() -> None:
123+
"""
124+
Patch Sphinx 9's new directive-based autodoc to disable overload detection.
125+
126+
The new architecture adds overload signatures without emitting autodoc-process-signature
127+
for each one. By patching ModuleAnalyzer to clear overloads after analysis, we prevent
128+
overload detection while preserving other functionality like attribute discovery.
129+
"""
130+
try:
131+
from sphinx.pycode import ModuleAnalyzer # noqa: PLC0415
132+
except ImportError:
133+
return # Not Sphinx 9+
134+
135+
# Store the original analyze method
136+
original_analyze = ModuleAnalyzer.analyze
137+
138+
def patched_analyze(self: Any) -> None:
139+
# Call the original analyze method
140+
original_analyze(self)
141+
# Then clear the overloads to prevent overload signature generation
142+
self.overloads = {}
143+
144+
# Replace the analyze method
145+
ModuleAnalyzer.analyze = patched_analyze # type: ignore[method-assign]
146+
147+
141148
def install_patches(app: Sphinx) -> None:
142149
"""
143150
Install the patches.
144151
145152
:param app: the Sphinx app
146153
"""
147-
fix_autodoc_typehints_for_overloaded_methods()
154+
# For Sphinx 9+ directive-based architecture
155+
fix_directive_based_signature_formatting()
148156
patch_attribute_handling(app)
149157
_patch_google_docstring_lookup_annotation()
150158
fix_napoleon_numpy_docstring_return_type(app)

tests/roots/test-integration/conf.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,3 @@
1414
"sphinx.ext.napoleon",
1515
"sphinx_autodoc_typehints",
1616
]
17-
18-
autodoc_use_legacy_class_based = True

0 commit comments

Comments
 (0)