Sphinx 8.1.2__py3-none-any.whl → 8.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of Sphinx might be problematic. Click here for more details.
- sphinx/__init__.py +8 -4
- sphinx/__main__.py +2 -0
- sphinx/_cli/__init__.py +2 -5
- sphinx/_cli/util/colour.py +34 -11
- sphinx/_cli/util/errors.py +128 -61
- sphinx/addnodes.py +51 -35
- sphinx/application.py +362 -230
- sphinx/builders/__init__.py +87 -64
- sphinx/builders/_epub_base.py +65 -56
- sphinx/builders/changes.py +17 -23
- sphinx/builders/dirhtml.py +8 -13
- sphinx/builders/epub3.py +70 -38
- sphinx/builders/gettext.py +93 -73
- sphinx/builders/html/__init__.py +240 -186
- sphinx/builders/html/_assets.py +9 -2
- sphinx/builders/html/_build_info.py +3 -0
- sphinx/builders/latex/__init__.py +64 -54
- sphinx/builders/latex/constants.py +14 -11
- sphinx/builders/latex/nodes.py +2 -0
- sphinx/builders/latex/theming.py +8 -9
- sphinx/builders/latex/transforms.py +7 -5
- sphinx/builders/linkcheck.py +193 -149
- sphinx/builders/manpage.py +17 -17
- sphinx/builders/singlehtml.py +28 -16
- sphinx/builders/texinfo.py +28 -21
- sphinx/builders/text.py +10 -15
- sphinx/builders/xml.py +10 -19
- sphinx/cmd/build.py +49 -119
- sphinx/cmd/make_mode.py +35 -31
- sphinx/cmd/quickstart.py +78 -62
- sphinx/config.py +265 -163
- sphinx/directives/__init__.py +51 -54
- sphinx/directives/admonitions.py +107 -0
- sphinx/directives/code.py +24 -19
- sphinx/directives/other.py +21 -42
- sphinx/directives/patches.py +28 -16
- sphinx/domains/__init__.py +54 -31
- sphinx/domains/_domains_container.py +22 -17
- sphinx/domains/_index.py +5 -8
- sphinx/domains/c/__init__.py +366 -245
- sphinx/domains/c/_ast.py +378 -256
- sphinx/domains/c/_ids.py +89 -31
- sphinx/domains/c/_parser.py +283 -214
- sphinx/domains/c/_symbol.py +269 -198
- sphinx/domains/changeset.py +39 -24
- sphinx/domains/citation.py +54 -24
- sphinx/domains/cpp/__init__.py +517 -362
- sphinx/domains/cpp/_ast.py +999 -682
- sphinx/domains/cpp/_ids.py +133 -65
- sphinx/domains/cpp/_parser.py +746 -588
- sphinx/domains/cpp/_symbol.py +692 -489
- sphinx/domains/index.py +10 -8
- sphinx/domains/javascript.py +152 -74
- sphinx/domains/math.py +50 -40
- sphinx/domains/python/__init__.py +402 -211
- sphinx/domains/python/_annotations.py +134 -61
- sphinx/domains/python/_object.py +155 -68
- sphinx/domains/rst.py +94 -49
- sphinx/domains/std/__init__.py +510 -249
- sphinx/environment/__init__.py +345 -61
- sphinx/environment/adapters/asset.py +7 -1
- sphinx/environment/adapters/indexentries.py +15 -20
- sphinx/environment/adapters/toctree.py +19 -9
- sphinx/environment/collectors/__init__.py +3 -1
- sphinx/environment/collectors/asset.py +18 -15
- sphinx/environment/collectors/dependencies.py +8 -10
- sphinx/environment/collectors/metadata.py +6 -4
- sphinx/environment/collectors/title.py +3 -1
- sphinx/environment/collectors/toctree.py +4 -4
- sphinx/errors.py +1 -3
- sphinx/events.py +4 -4
- sphinx/ext/apidoc/__init__.py +66 -0
- sphinx/ext/apidoc/__main__.py +9 -0
- sphinx/ext/apidoc/_cli.py +356 -0
- sphinx/ext/apidoc/_extension.py +262 -0
- sphinx/ext/apidoc/_generate.py +356 -0
- sphinx/ext/apidoc/_shared.py +99 -0
- sphinx/ext/autodoc/__init__.py +837 -483
- sphinx/ext/autodoc/directive.py +57 -21
- sphinx/ext/autodoc/importer.py +184 -67
- sphinx/ext/autodoc/mock.py +25 -10
- sphinx/ext/autodoc/preserve_defaults.py +17 -9
- sphinx/ext/autodoc/type_comment.py +56 -29
- sphinx/ext/autodoc/typehints.py +49 -26
- sphinx/ext/autosectionlabel.py +28 -11
- sphinx/ext/autosummary/__init__.py +281 -142
- sphinx/ext/autosummary/generate.py +121 -51
- sphinx/ext/coverage.py +152 -91
- sphinx/ext/doctest.py +169 -101
- sphinx/ext/duration.py +12 -6
- sphinx/ext/extlinks.py +33 -21
- sphinx/ext/githubpages.py +8 -8
- sphinx/ext/graphviz.py +175 -109
- sphinx/ext/ifconfig.py +11 -6
- sphinx/ext/imgconverter.py +48 -25
- sphinx/ext/imgmath.py +127 -97
- sphinx/ext/inheritance_diagram.py +177 -103
- sphinx/ext/intersphinx/__init__.py +22 -13
- sphinx/ext/intersphinx/__main__.py +3 -1
- sphinx/ext/intersphinx/_cli.py +18 -14
- sphinx/ext/intersphinx/_load.py +91 -82
- sphinx/ext/intersphinx/_resolve.py +108 -74
- sphinx/ext/intersphinx/_shared.py +2 -2
- sphinx/ext/linkcode.py +28 -12
- sphinx/ext/mathjax.py +60 -29
- sphinx/ext/napoleon/__init__.py +19 -7
- sphinx/ext/napoleon/docstring.py +229 -231
- sphinx/ext/todo.py +44 -49
- sphinx/ext/viewcode.py +105 -57
- sphinx/extension.py +3 -1
- sphinx/highlighting.py +13 -7
- sphinx/io.py +9 -13
- sphinx/jinja2glue.py +29 -26
- sphinx/locale/__init__.py +8 -9
- sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ar/LC_MESSAGES/sphinx.po +2155 -2050
- sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bg/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bn/LC_MESSAGES/sphinx.po +2175 -2070
- sphinx/locale/ca/LC_MESSAGES/sphinx.js +3 -3
- sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca/LC_MESSAGES/sphinx.po +2690 -2585
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.js +63 -0
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.po +4216 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.po +2096 -1991
- sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cs/LC_MESSAGES/sphinx.po +2248 -2143
- sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cy/LC_MESSAGES/sphinx.po +2201 -2096
- sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/da/LC_MESSAGES/sphinx.po +2282 -2177
- sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de/LC_MESSAGES/sphinx.po +2261 -2156
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/el/LC_MESSAGES/sphinx.po +2604 -2499
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2631 -2526
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eo/LC_MESSAGES/sphinx.po +2078 -1973
- sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es/LC_MESSAGES/sphinx.po +2633 -2528
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/et/LC_MESSAGES/sphinx.po +2449 -2344
- sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eu/LC_MESSAGES/sphinx.po +2241 -2136
- sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fa/LC_MESSAGES/sphinx.po +504 -500
- sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fi/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr/LC_MESSAGES/sphinx.po +513 -509
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.po +2644 -2539
- sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi/LC_MESSAGES/sphinx.po +504 -500
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hr/LC_MESSAGES/sphinx.po +501 -497
- sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hu/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/id/LC_MESSAGES/sphinx.po +2609 -2504
- sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/is/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/it/LC_MESSAGES/sphinx.po +2265 -2160
- sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ja/LC_MESSAGES/sphinx.po +2621 -2516
- sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ka/LC_MESSAGES/sphinx.po +2567 -2462
- sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ko/LC_MESSAGES/sphinx.po +2631 -2526
- sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lt/LC_MESSAGES/sphinx.po +2214 -2109
- sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lv/LC_MESSAGES/sphinx.po +2218 -2113
- sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/mk/LC_MESSAGES/sphinx.po +2088 -1983
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2247 -2142
- sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ne/LC_MESSAGES/sphinx.po +2227 -2122
- sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nl/LC_MESSAGES/sphinx.po +2316 -2211
- sphinx/locale/pl/LC_MESSAGES/sphinx.js +2 -2
- sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pl/LC_MESSAGES/sphinx.po +2442 -2336
- sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2657 -2552
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2243 -2138
- sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ro/LC_MESSAGES/sphinx.po +2244 -2139
- sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
- sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ru/LC_MESSAGES/sphinx.po +2660 -2555
- sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/si/LC_MESSAGES/sphinx.po +2134 -2029
- sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sk/LC_MESSAGES/sphinx.po +2614 -2509
- sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sl/LC_MESSAGES/sphinx.po +2167 -2062
- sphinx/locale/sphinx.pot +2069 -1964
- sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sq/LC_MESSAGES/sphinx.po +2661 -2556
- sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr/LC_MESSAGES/sphinx.po +2213 -2108
- sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.po +2229 -2124
- sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/te/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/tr/LC_MESSAGES/sphinx.po +2608 -2503
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2167 -2062
- sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ur/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/vi/LC_MESSAGES/sphinx.po +2204 -2099
- sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/yue/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2659 -2554
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/parsers.py +8 -7
- sphinx/project.py +2 -2
- sphinx/pycode/__init__.py +31 -21
- sphinx/pycode/ast.py +6 -3
- sphinx/pycode/parser.py +14 -8
- sphinx/pygments_styles.py +4 -5
- sphinx/registry.py +192 -92
- sphinx/roles.py +58 -7
- sphinx/search/__init__.py +75 -54
- sphinx/search/en.py +11 -13
- sphinx/search/fi.py +1 -1
- sphinx/search/ja.py +8 -6
- sphinx/search/nl.py +1 -1
- sphinx/search/zh.py +19 -21
- sphinx/testing/fixtures.py +26 -29
- sphinx/testing/path.py +26 -62
- sphinx/testing/restructuredtext.py +14 -8
- sphinx/testing/util.py +21 -19
- sphinx/texinputs/make.bat.jinja +50 -50
- sphinx/texinputs/sphinx.sty +4 -3
- sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
- sphinx/texinputs/sphinxlatexobjects.sty +29 -10
- sphinx/themes/basic/static/searchtools.js +8 -5
- sphinx/theming.py +49 -61
- sphinx/transforms/__init__.py +17 -38
- sphinx/transforms/compact_bullet_list.py +5 -3
- sphinx/transforms/i18n.py +8 -21
- sphinx/transforms/post_transforms/__init__.py +142 -93
- sphinx/transforms/post_transforms/code.py +5 -5
- sphinx/transforms/post_transforms/images.py +28 -24
- sphinx/transforms/references.py +3 -1
- sphinx/util/__init__.py +109 -60
- sphinx/util/_files.py +39 -23
- sphinx/util/_importer.py +4 -1
- sphinx/util/_inventory_file_reader.py +76 -0
- sphinx/util/_io.py +2 -2
- sphinx/util/_lines.py +6 -3
- sphinx/util/_pathlib.py +40 -2
- sphinx/util/build_phase.py +2 -0
- sphinx/util/cfamily.py +19 -14
- sphinx/util/console.py +44 -179
- sphinx/util/display.py +9 -10
- sphinx/util/docfields.py +140 -122
- sphinx/util/docstrings.py +1 -1
- sphinx/util/docutils.py +118 -77
- sphinx/util/fileutil.py +25 -26
- sphinx/util/http_date.py +2 -0
- sphinx/util/i18n.py +77 -64
- sphinx/util/images.py +8 -6
- sphinx/util/inspect.py +147 -38
- sphinx/util/inventory.py +215 -116
- sphinx/util/logging.py +33 -33
- sphinx/util/matching.py +12 -4
- sphinx/util/nodes.py +18 -13
- sphinx/util/osutil.py +38 -39
- sphinx/util/parallel.py +22 -13
- sphinx/util/parsing.py +2 -1
- sphinx/util/png.py +6 -2
- sphinx/util/requests.py +33 -2
- sphinx/util/rst.py +3 -2
- sphinx/util/tags.py +1 -1
- sphinx/util/template.py +18 -10
- sphinx/util/texescape.py +8 -6
- sphinx/util/typing.py +148 -122
- sphinx/versioning.py +3 -3
- sphinx/writers/html.py +3 -1
- sphinx/writers/html5.py +63 -52
- sphinx/writers/latex.py +83 -67
- sphinx/writers/manpage.py +19 -38
- sphinx/writers/texinfo.py +47 -47
- sphinx/writers/text.py +50 -32
- sphinx/writers/xml.py +11 -8
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/LICENSE.rst +1 -1
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/METADATA +25 -15
- sphinx-8.2.0.dist-info/RECORD +606 -0
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/WHEEL +1 -1
- sphinx/builders/html/transforms.py +0 -90
- sphinx/ext/apidoc.py +0 -721
- sphinx/util/exceptions.py +0 -74
- sphinx-8.1.2.dist-info/RECORD +0 -598
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/entry_points.txt +0 -0
sphinx/ext/autodoc/directive.py
CHANGED
|
@@ -1,35 +1,55 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from collections.abc import Callable
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
6
|
from docutils import nodes
|
|
7
7
|
from docutils.statemachine import StringList
|
|
8
|
-
from docutils.utils import
|
|
8
|
+
from docutils.utils import assemble_option_dict
|
|
9
9
|
|
|
10
|
-
from sphinx.ext.autodoc import
|
|
10
|
+
from sphinx.ext.autodoc import Options
|
|
11
11
|
from sphinx.util import logging
|
|
12
12
|
from sphinx.util.docutils import SphinxDirective, switch_source_input
|
|
13
13
|
from sphinx.util.parsing import nested_parse_to_nodes
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
16
18
|
from docutils.nodes import Node
|
|
17
19
|
from docutils.parsers.rst.states import RSTState
|
|
20
|
+
from docutils.utils import Reporter
|
|
18
21
|
|
|
19
22
|
from sphinx.config import Config
|
|
20
23
|
from sphinx.environment import BuildEnvironment
|
|
24
|
+
from sphinx.ext.autodoc import Documenter
|
|
21
25
|
|
|
22
26
|
logger = logging.getLogger(__name__)
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
# common option names for autodoc directives
|
|
26
|
-
AUTODOC_DEFAULT_OPTIONS = [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
AUTODOC_DEFAULT_OPTIONS = [
|
|
31
|
+
'members',
|
|
32
|
+
'undoc-members',
|
|
33
|
+
'no-index',
|
|
34
|
+
'no-index-entry',
|
|
35
|
+
'inherited-members',
|
|
36
|
+
'show-inheritance',
|
|
37
|
+
'private-members',
|
|
38
|
+
'special-members',
|
|
39
|
+
'ignore-module-all',
|
|
40
|
+
'exclude-members',
|
|
41
|
+
'member-order',
|
|
42
|
+
'imported-members',
|
|
43
|
+
'class-doc-from',
|
|
44
|
+
'no-value',
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
AUTODOC_EXTENDABLE_OPTIONS = frozenset({
|
|
48
|
+
'members',
|
|
49
|
+
'private-members',
|
|
50
|
+
'special-members',
|
|
51
|
+
'exclude-members',
|
|
52
|
+
})
|
|
33
53
|
|
|
34
54
|
|
|
35
55
|
class DummyOptionSpec(dict[str, Callable[[str], str]]):
|
|
@@ -46,8 +66,14 @@ class DummyOptionSpec(dict[str, Callable[[str], str]]):
|
|
|
46
66
|
class DocumenterBridge:
|
|
47
67
|
"""A parameters container for Documenters."""
|
|
48
68
|
|
|
49
|
-
def __init__(
|
|
50
|
-
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
env: BuildEnvironment,
|
|
72
|
+
reporter: Reporter | None,
|
|
73
|
+
options: Options,
|
|
74
|
+
lineno: int,
|
|
75
|
+
state: Any,
|
|
76
|
+
) -> None:
|
|
51
77
|
self.env = env
|
|
52
78
|
self._reporter = reporter
|
|
53
79
|
self.genopt = options
|
|
@@ -58,7 +84,7 @@ class DocumenterBridge:
|
|
|
58
84
|
|
|
59
85
|
|
|
60
86
|
def process_documenter_options(
|
|
61
|
-
documenter: type[Documenter], config: Config, options: dict[str, str]
|
|
87
|
+
documenter: type[Documenter], config: Config, options: dict[str, str]
|
|
62
88
|
) -> Options:
|
|
63
89
|
"""Recognize options of Documenter from user input."""
|
|
64
90
|
default_options = config.autodoc_default_options
|
|
@@ -83,8 +109,9 @@ def process_documenter_options(
|
|
|
83
109
|
return Options(assemble_option_dict(options.items(), documenter.option_spec))
|
|
84
110
|
|
|
85
111
|
|
|
86
|
-
def parse_generated_content(
|
|
87
|
-
|
|
112
|
+
def parse_generated_content(
|
|
113
|
+
state: RSTState, content: StringList, documenter: Documenter
|
|
114
|
+
) -> list[Node]:
|
|
88
115
|
"""Parse an item of content generated by Documenter."""
|
|
89
116
|
with switch_source_input(state, content):
|
|
90
117
|
if documenter.titles_allowed:
|
|
@@ -115,26 +142,35 @@ class AutodocDirective(SphinxDirective):
|
|
|
115
142
|
|
|
116
143
|
try:
|
|
117
144
|
source, lineno = reporter.get_source_and_line( # type: ignore[attr-defined]
|
|
118
|
-
self.lineno
|
|
145
|
+
self.lineno
|
|
146
|
+
)
|
|
119
147
|
except AttributeError:
|
|
120
148
|
source, lineno = (None, None)
|
|
121
149
|
logger.debug('[autodoc] %s:%s: input:\n%s', source, lineno, self.block_text)
|
|
122
150
|
|
|
123
151
|
# look up target Documenter
|
|
124
152
|
objtype = self.name[4:] # strip prefix (auto-).
|
|
125
|
-
doccls = self.env.
|
|
153
|
+
doccls = self.env._registry.documenters[objtype]
|
|
126
154
|
|
|
127
155
|
# process the options with the selected documenter's option_spec
|
|
128
156
|
try:
|
|
129
|
-
documenter_options = process_documenter_options(
|
|
157
|
+
documenter_options = process_documenter_options(
|
|
158
|
+
doccls, self.config, self.options
|
|
159
|
+
)
|
|
130
160
|
except (KeyError, ValueError, TypeError) as exc:
|
|
131
161
|
# an option is either unknown or has a wrong type
|
|
132
|
-
logger.error(
|
|
133
|
-
|
|
162
|
+
logger.error( # NoQA: TRY400
|
|
163
|
+
'An option to %s is either unknown or has an invalid value: %s',
|
|
164
|
+
self.name,
|
|
165
|
+
exc,
|
|
166
|
+
location=(self.env.docname, lineno),
|
|
167
|
+
)
|
|
134
168
|
return []
|
|
135
169
|
|
|
136
170
|
# generate the output
|
|
137
|
-
params = DocumenterBridge(
|
|
171
|
+
params = DocumenterBridge(
|
|
172
|
+
self.env, reporter, documenter_options, lineno, self.state
|
|
173
|
+
)
|
|
138
174
|
documenter = doccls(params, self.arguments[0])
|
|
139
175
|
documenter.generate(more_content=self.content)
|
|
140
176
|
if not params.result:
|
sphinx/ext/autodoc/importer.py
CHANGED
|
@@ -9,6 +9,10 @@ import sys
|
|
|
9
9
|
import traceback
|
|
10
10
|
import typing
|
|
11
11
|
from enum import Enum
|
|
12
|
+
from importlib.abc import FileLoader
|
|
13
|
+
from importlib.machinery import EXTENSION_SUFFIXES
|
|
14
|
+
from importlib.util import decode_source, find_spec, module_from_spec, spec_from_loader
|
|
15
|
+
from pathlib import Path
|
|
12
16
|
from typing import TYPE_CHECKING, NamedTuple
|
|
13
17
|
|
|
14
18
|
from sphinx.errors import PycodeError
|
|
@@ -26,18 +30,24 @@ from sphinx.util.inspect import (
|
|
|
26
30
|
)
|
|
27
31
|
|
|
28
32
|
if TYPE_CHECKING:
|
|
29
|
-
from collections.abc import
|
|
33
|
+
from collections.abc import Iterator, Mapping
|
|
34
|
+
from importlib.machinery import ModuleSpec
|
|
30
35
|
from types import ModuleType
|
|
31
|
-
from typing import Any
|
|
36
|
+
from typing import Any, Protocol
|
|
32
37
|
|
|
33
38
|
from sphinx.ext.autodoc import ObjectMember
|
|
34
39
|
|
|
40
|
+
class _AttrGetter(Protocol):
|
|
41
|
+
def __call__(self, obj: Any, name: str, default: Any = ..., /) -> Any: ...
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_NATIVE_SUFFIXES: frozenset[str] = frozenset({'.pyx', *EXTENSION_SUFFIXES})
|
|
35
45
|
logger = logging.getLogger(__name__)
|
|
36
46
|
|
|
37
47
|
|
|
38
48
|
def _filter_enum_dict(
|
|
39
49
|
enum_class: type[Enum],
|
|
40
|
-
attrgetter:
|
|
50
|
+
attrgetter: _AttrGetter,
|
|
41
51
|
enum_class_dict: Mapping[str, object],
|
|
42
52
|
) -> Iterator[tuple[str, type, Any]]:
|
|
43
53
|
"""Find the attributes to document of an enumeration class.
|
|
@@ -51,19 +61,21 @@ def _filter_enum_dict(
|
|
|
51
61
|
candidate_in_mro: set[str] = set()
|
|
52
62
|
# sunder names that were picked up (and thereby allowed to be redefined)
|
|
53
63
|
# see: https://docs.python.org/3/howto/enum.html#supported-dunder-names
|
|
54
|
-
sunder_names = {
|
|
64
|
+
sunder_names = {
|
|
65
|
+
'_name_',
|
|
66
|
+
'_value_',
|
|
67
|
+
'_missing_',
|
|
68
|
+
'_order_',
|
|
69
|
+
'_generate_next_value_',
|
|
70
|
+
}
|
|
55
71
|
# attributes that can be picked up on a mixin type or the enum's data type
|
|
56
72
|
public_names = {'name', 'value', *object.__dict__, *sunder_names}
|
|
57
73
|
# names that are ignored by default
|
|
58
74
|
ignore_names = Enum.__dict__.keys() - public_names
|
|
59
75
|
|
|
60
|
-
def is_native_api(obj: object, name: str) -> bool:
|
|
61
|
-
"""Check whether *obj* is the same as ``Enum.__dict__[name]``."""
|
|
62
|
-
return unwrap_all(obj) is unwrap_all(Enum.__dict__[name])
|
|
63
|
-
|
|
64
76
|
def should_ignore(name: str, value: Any) -> bool:
|
|
65
77
|
if name in sunder_names:
|
|
66
|
-
return
|
|
78
|
+
return _is_native_enum_api(value, name)
|
|
67
79
|
return name in ignore_names
|
|
68
80
|
|
|
69
81
|
sentinel = object()
|
|
@@ -71,7 +83,7 @@ def _filter_enum_dict(
|
|
|
71
83
|
def query(name: str, defining_class: type) -> tuple[str, type, Any] | None:
|
|
72
84
|
value = attrgetter(enum_class, name, sentinel)
|
|
73
85
|
if value is not sentinel:
|
|
74
|
-
return
|
|
86
|
+
return name, defining_class, value
|
|
75
87
|
return None
|
|
76
88
|
|
|
77
89
|
# attributes defined on a parent type, possibly shadowed later by
|
|
@@ -92,8 +104,14 @@ def _filter_enum_dict(
|
|
|
92
104
|
# exclude members coming from the native Enum unless
|
|
93
105
|
# they were redefined on a mixin type or the data type
|
|
94
106
|
excluded_members = Enum.__dict__.keys() - candidate_in_mro
|
|
95
|
-
yield from filter(
|
|
96
|
-
|
|
107
|
+
yield from filter(
|
|
108
|
+
None,
|
|
109
|
+
(
|
|
110
|
+
query(name, enum_class)
|
|
111
|
+
for name in enum_class_dict
|
|
112
|
+
if name not in excluded_members
|
|
113
|
+
),
|
|
114
|
+
)
|
|
97
115
|
|
|
98
116
|
# check if allowed members from ``Enum`` were redefined at the enum level
|
|
99
117
|
special_names = sunder_names | public_names
|
|
@@ -101,17 +119,22 @@ def _filter_enum_dict(
|
|
|
101
119
|
special_names &= Enum.__dict__.keys()
|
|
102
120
|
for name in special_names:
|
|
103
121
|
if (
|
|
104
|
-
not
|
|
122
|
+
not _is_native_enum_api(enum_class_dict[name], name)
|
|
105
123
|
and (item := query(name, enum_class)) is not None
|
|
106
124
|
):
|
|
107
125
|
yield item
|
|
108
126
|
|
|
109
127
|
|
|
128
|
+
def _is_native_enum_api(obj: object, name: str) -> bool:
|
|
129
|
+
"""Check whether *obj* is the same as ``Enum.__dict__[name]``."""
|
|
130
|
+
return unwrap_all(obj) is unwrap_all(Enum.__dict__[name])
|
|
131
|
+
|
|
132
|
+
|
|
110
133
|
def mangle(subject: Any, name: str) -> str:
|
|
111
134
|
"""Mangle the given name."""
|
|
112
135
|
try:
|
|
113
136
|
if isclass(subject) and name.startswith('__') and not name.endswith('__'):
|
|
114
|
-
return f
|
|
137
|
+
return f'_{subject.__name__}{name}'
|
|
115
138
|
except AttributeError:
|
|
116
139
|
pass
|
|
117
140
|
|
|
@@ -122,12 +145,12 @@ def unmangle(subject: Any, name: str) -> str | None:
|
|
|
122
145
|
"""Unmangle the given name."""
|
|
123
146
|
try:
|
|
124
147
|
if isclass(subject) and not name.endswith('__'):
|
|
125
|
-
prefix =
|
|
148
|
+
prefix = f'_{subject.__name__}__'
|
|
126
149
|
if name.startswith(prefix):
|
|
127
|
-
return name.replace(prefix,
|
|
150
|
+
return name.replace(prefix, '__', 1)
|
|
128
151
|
else:
|
|
129
152
|
for cls in subject.__mro__:
|
|
130
|
-
prefix =
|
|
153
|
+
prefix = f'_{cls.__name__}__'
|
|
131
154
|
if name.startswith(prefix):
|
|
132
155
|
# mangled attribute defined in parent class
|
|
133
156
|
return None
|
|
@@ -137,20 +160,87 @@ def unmangle(subject: Any, name: str) -> str | None:
|
|
|
137
160
|
return name
|
|
138
161
|
|
|
139
162
|
|
|
140
|
-
def import_module(modname: str) -> Any:
|
|
141
|
-
|
|
163
|
+
def import_module(modname: str, try_reload: bool = False) -> Any:
|
|
164
|
+
if modname in sys.modules:
|
|
165
|
+
return sys.modules[modname]
|
|
166
|
+
|
|
167
|
+
original_module_names = frozenset(sys.modules)
|
|
142
168
|
try:
|
|
143
|
-
|
|
169
|
+
spec = find_spec(modname)
|
|
170
|
+
if spec is None:
|
|
171
|
+
msg = f'No module named {modname!r}'
|
|
172
|
+
raise ModuleNotFoundError(msg, name=modname) # NoQA: TRY301
|
|
173
|
+
spec, pyi_path = _find_type_stub_spec(spec, modname)
|
|
174
|
+
if pyi_path is None:
|
|
175
|
+
module = importlib.import_module(modname)
|
|
176
|
+
else:
|
|
177
|
+
if spec.loader is None:
|
|
178
|
+
msg = 'missing loader'
|
|
179
|
+
raise ImportError(msg, name=spec.name) # NoQA: TRY301
|
|
180
|
+
sys.modules[modname] = module = module_from_spec(spec)
|
|
181
|
+
spec.loader.exec_module(module)
|
|
182
|
+
except ImportError:
|
|
183
|
+
raise
|
|
144
184
|
except BaseException as exc:
|
|
145
185
|
# Importing modules may cause any side effects, including
|
|
146
186
|
# SystemExit, so we need to catch all errors.
|
|
147
187
|
raise ImportError(exc, traceback.format_exc()) from exc
|
|
188
|
+
if try_reload and os.environ.get('SPHINX_AUTODOC_RELOAD_MODULES'):
|
|
189
|
+
new_modules = [m for m in sys.modules if m not in original_module_names]
|
|
190
|
+
# Try reloading modules with ``typing.TYPE_CHECKING == True``.
|
|
191
|
+
try:
|
|
192
|
+
typing.TYPE_CHECKING = True # type: ignore[misc]
|
|
193
|
+
# Ignore failures; we've already successfully loaded these modules
|
|
194
|
+
with contextlib.suppress(ImportError, KeyError):
|
|
195
|
+
for m in new_modules:
|
|
196
|
+
mod_path = getattr(sys.modules[m], '__file__', '')
|
|
197
|
+
if mod_path and mod_path.endswith('.pyi'):
|
|
198
|
+
continue
|
|
199
|
+
_reload_module(sys.modules[m])
|
|
200
|
+
finally:
|
|
201
|
+
typing.TYPE_CHECKING = False # type: ignore[misc]
|
|
202
|
+
module = sys.modules[modname]
|
|
203
|
+
return module
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _find_type_stub_spec(
|
|
207
|
+
spec: ModuleSpec, modname: str
|
|
208
|
+
) -> tuple[ModuleSpec, Path | None]:
|
|
209
|
+
"""Try finding a spec for a PEP 561 '.pyi' stub file for native modules."""
|
|
210
|
+
if spec.origin is None:
|
|
211
|
+
return spec, None
|
|
212
|
+
|
|
213
|
+
for suffix in _NATIVE_SUFFIXES:
|
|
214
|
+
if not spec.origin.endswith(suffix):
|
|
215
|
+
continue
|
|
216
|
+
pyi_path = Path(spec.origin.removesuffix(suffix) + '.pyi')
|
|
217
|
+
if not pyi_path.is_file():
|
|
218
|
+
continue
|
|
219
|
+
pyi_loader = _StubFileLoader(modname, path=str(pyi_path))
|
|
220
|
+
pyi_spec = spec_from_loader(modname, loader=pyi_loader)
|
|
221
|
+
if pyi_spec is not None:
|
|
222
|
+
return pyi_spec, pyi_path
|
|
223
|
+
return spec, None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class _StubFileLoader(FileLoader):
|
|
227
|
+
"""Load modules from ``.pyi`` stub files."""
|
|
228
|
+
|
|
229
|
+
def get_source(self, fullname: str) -> str:
|
|
230
|
+
path = self.get_filename(fullname)
|
|
231
|
+
for suffix in _NATIVE_SUFFIXES:
|
|
232
|
+
if not path.endswith(suffix):
|
|
233
|
+
continue
|
|
234
|
+
path = path.removesuffix(suffix) + '.pyi'
|
|
235
|
+
try:
|
|
236
|
+
source_bytes = self.get_data(path)
|
|
237
|
+
except OSError as exc:
|
|
238
|
+
raise ImportError from exc
|
|
239
|
+
return decode_source(source_bytes)
|
|
148
240
|
|
|
149
241
|
|
|
150
242
|
def _reload_module(module: ModuleType) -> Any:
|
|
151
|
-
"""
|
|
152
|
-
Call importlib.reload(module), convert exceptions to ImportError
|
|
153
|
-
"""
|
|
243
|
+
"""Call importlib.reload(module), convert exceptions to ImportError"""
|
|
154
244
|
try:
|
|
155
245
|
return importlib.reload(module)
|
|
156
246
|
except BaseException as exc:
|
|
@@ -159,8 +249,12 @@ def _reload_module(module: ModuleType) -> Any:
|
|
|
159
249
|
raise ImportError(exc, traceback.format_exc()) from exc
|
|
160
250
|
|
|
161
251
|
|
|
162
|
-
def import_object(
|
|
163
|
-
|
|
252
|
+
def import_object(
|
|
253
|
+
modname: str,
|
|
254
|
+
objpath: list[str],
|
|
255
|
+
objtype: str = '',
|
|
256
|
+
attrgetter: _AttrGetter = safe_getattr,
|
|
257
|
+
) -> Any:
|
|
164
258
|
if objpath:
|
|
165
259
|
logger.debug('[autodoc] from %s import %s', modname, '.'.join(objpath))
|
|
166
260
|
else:
|
|
@@ -172,20 +266,7 @@ def import_object(modname: str, objpath: list[str], objtype: str = '',
|
|
|
172
266
|
objpath = objpath.copy()
|
|
173
267
|
while module is None:
|
|
174
268
|
try:
|
|
175
|
-
|
|
176
|
-
module = import_module(modname)
|
|
177
|
-
if os.environ.get('SPHINX_AUTODOC_RELOAD_MODULES'):
|
|
178
|
-
new_modules = [m for m in sys.modules if m not in original_module_names]
|
|
179
|
-
# Try reloading modules with ``typing.TYPE_CHECKING == True``.
|
|
180
|
-
try:
|
|
181
|
-
typing.TYPE_CHECKING = True
|
|
182
|
-
# Ignore failures; we've already successfully loaded these modules
|
|
183
|
-
with contextlib.suppress(ImportError, KeyError):
|
|
184
|
-
for m in new_modules:
|
|
185
|
-
_reload_module(sys.modules[m])
|
|
186
|
-
finally:
|
|
187
|
-
typing.TYPE_CHECKING = False
|
|
188
|
-
module = sys.modules[modname]
|
|
269
|
+
module = import_module(modname, try_reload=True)
|
|
189
270
|
logger.debug('[autodoc] import %s => %r', modname, module)
|
|
190
271
|
except ImportError as exc:
|
|
191
272
|
logger.debug('[autodoc] import %s => failed', modname)
|
|
@@ -210,7 +291,7 @@ def import_object(modname: str, objpath: list[str], objtype: str = '',
|
|
|
210
291
|
logger.debug('[autodoc] => %r', obj)
|
|
211
292
|
except TypeError:
|
|
212
293
|
# fallback of failure on logging for broken object
|
|
213
|
-
#
|
|
294
|
+
# See: https://github.com/sphinx-doc/sphinx/issues/9095
|
|
214
295
|
logger.debug('[autodoc] => %r', (obj,))
|
|
215
296
|
|
|
216
297
|
object_name = attrname
|
|
@@ -221,24 +302,32 @@ def import_object(modname: str, objpath: list[str], objtype: str = '',
|
|
|
221
302
|
exc = exc_on_importing
|
|
222
303
|
|
|
223
304
|
if objpath:
|
|
224
|
-
errmsg =
|
|
225
|
-
|
|
305
|
+
errmsg = 'autodoc: failed to import %s %r from module %r' % (
|
|
306
|
+
objtype,
|
|
307
|
+
'.'.join(objpath),
|
|
308
|
+
modname,
|
|
309
|
+
)
|
|
226
310
|
else:
|
|
227
311
|
errmsg = f'autodoc: failed to import {objtype} {modname!r}'
|
|
228
312
|
|
|
229
313
|
if isinstance(exc, ImportError):
|
|
230
314
|
# import_module() raises ImportError having real exception obj and
|
|
231
315
|
# traceback
|
|
232
|
-
real_exc
|
|
316
|
+
real_exc = exc.args[0]
|
|
317
|
+
traceback_msg = traceback.format_exception(exc)
|
|
233
318
|
if isinstance(real_exc, SystemExit):
|
|
234
|
-
errmsg += (
|
|
235
|
-
|
|
319
|
+
errmsg += (
|
|
320
|
+
'; the module executes module level statement '
|
|
321
|
+
'and it might call sys.exit().'
|
|
322
|
+
)
|
|
236
323
|
elif isinstance(real_exc, ImportError) and real_exc.args:
|
|
237
324
|
errmsg += '; the following exception was raised:\n%s' % real_exc.args[0]
|
|
238
325
|
else:
|
|
239
326
|
errmsg += '; the following exception was raised:\n%s' % traceback_msg
|
|
240
327
|
else:
|
|
241
|
-
errmsg +=
|
|
328
|
+
errmsg += (
|
|
329
|
+
'; the following exception was raised:\n%s' % traceback.format_exc()
|
|
330
|
+
)
|
|
242
331
|
|
|
243
332
|
logger.debug(errmsg)
|
|
244
333
|
raise ImportError(errmsg) from exc
|
|
@@ -253,7 +342,7 @@ class Attribute(NamedTuple):
|
|
|
253
342
|
def get_object_members(
|
|
254
343
|
subject: Any,
|
|
255
344
|
objpath: list[str],
|
|
256
|
-
attrgetter:
|
|
345
|
+
attrgetter: _AttrGetter,
|
|
257
346
|
analyzer: ModuleAnalyzer | None = None,
|
|
258
347
|
) -> dict[str, Attribute]:
|
|
259
348
|
"""Get members and attributes of target object."""
|
|
@@ -266,11 +355,17 @@ def get_object_members(
|
|
|
266
355
|
|
|
267
356
|
# enum members
|
|
268
357
|
if isenumclass(subject):
|
|
269
|
-
for name, defining_class, value in _filter_enum_dict(
|
|
358
|
+
for name, defining_class, value in _filter_enum_dict(
|
|
359
|
+
subject, attrgetter, obj_dict
|
|
360
|
+
):
|
|
270
361
|
# the order of occurrence of *name* matches the subject's MRO,
|
|
271
362
|
# allowing inherited attributes to be shadowed correctly
|
|
272
363
|
if unmangled := unmangle(defining_class, name):
|
|
273
|
-
members[unmangled] = Attribute(
|
|
364
|
+
members[unmangled] = Attribute(
|
|
365
|
+
name=unmangled,
|
|
366
|
+
directly_defined=defining_class is subject,
|
|
367
|
+
value=value,
|
|
368
|
+
)
|
|
274
369
|
|
|
275
370
|
# members in __slots__
|
|
276
371
|
try:
|
|
@@ -279,7 +374,9 @@ def get_object_members(
|
|
|
279
374
|
from sphinx.ext.autodoc import SLOTSATTR
|
|
280
375
|
|
|
281
376
|
for name in subject___slots__:
|
|
282
|
-
members[name] = Attribute(
|
|
377
|
+
members[name] = Attribute(
|
|
378
|
+
name=name, directly_defined=True, value=SLOTSATTR
|
|
379
|
+
)
|
|
283
380
|
except (TypeError, ValueError):
|
|
284
381
|
pass
|
|
285
382
|
|
|
@@ -290,7 +387,9 @@ def get_object_members(
|
|
|
290
387
|
directly_defined = name in obj_dict
|
|
291
388
|
unmangled = unmangle(subject, name)
|
|
292
389
|
if unmangled and unmangled not in members:
|
|
293
|
-
members[unmangled] = Attribute(
|
|
390
|
+
members[unmangled] = Attribute(
|
|
391
|
+
name=unmangled, directly_defined=directly_defined, value=value
|
|
392
|
+
)
|
|
294
393
|
except AttributeError:
|
|
295
394
|
continue
|
|
296
395
|
|
|
@@ -299,20 +398,25 @@ def get_object_members(
|
|
|
299
398
|
for name in getannotations(cls):
|
|
300
399
|
unmangled = unmangle(cls, name)
|
|
301
400
|
if unmangled and unmangled not in members:
|
|
302
|
-
members[unmangled] = Attribute(
|
|
401
|
+
members[unmangled] = Attribute(
|
|
402
|
+
name=unmangled, directly_defined=cls is subject, value=INSTANCEATTR
|
|
403
|
+
)
|
|
303
404
|
|
|
304
405
|
if analyzer:
|
|
305
406
|
# append instance attributes (cf. self.attr1) if analyzer knows
|
|
306
407
|
namespace = '.'.join(objpath)
|
|
307
|
-
for
|
|
408
|
+
for ns, name in analyzer.find_attr_docs():
|
|
308
409
|
if namespace == ns and name not in members:
|
|
309
|
-
members[name] = Attribute(
|
|
410
|
+
members[name] = Attribute(
|
|
411
|
+
name=name, directly_defined=True, value=INSTANCEATTR
|
|
412
|
+
)
|
|
310
413
|
|
|
311
414
|
return members
|
|
312
415
|
|
|
313
416
|
|
|
314
|
-
def get_class_members(
|
|
315
|
-
|
|
417
|
+
def get_class_members(
|
|
418
|
+
subject: Any, objpath: Any, attrgetter: _AttrGetter, inherit_docstrings: bool = True
|
|
419
|
+
) -> dict[str, ObjectMember]:
|
|
316
420
|
"""Get members and attributes of target class."""
|
|
317
421
|
from sphinx.ext.autodoc import INSTANCEATTR, ObjectMember
|
|
318
422
|
|
|
@@ -323,11 +427,15 @@ def get_class_members(subject: Any, objpath: Any, attrgetter: Callable,
|
|
|
323
427
|
|
|
324
428
|
# enum members
|
|
325
429
|
if isenumclass(subject):
|
|
326
|
-
for name, defining_class, value in _filter_enum_dict(
|
|
430
|
+
for name, defining_class, value in _filter_enum_dict(
|
|
431
|
+
subject, attrgetter, obj_dict
|
|
432
|
+
):
|
|
327
433
|
# the order of occurrence of *name* matches the subject's MRO,
|
|
328
434
|
# allowing inherited attributes to be shadowed correctly
|
|
329
435
|
if unmangled := unmangle(defining_class, name):
|
|
330
|
-
members[unmangled] = ObjectMember(
|
|
436
|
+
members[unmangled] = ObjectMember(
|
|
437
|
+
unmangled, value, class_=defining_class
|
|
438
|
+
)
|
|
331
439
|
|
|
332
440
|
# members in __slots__
|
|
333
441
|
try:
|
|
@@ -336,8 +444,9 @@ def get_class_members(subject: Any, objpath: Any, attrgetter: Callable,
|
|
|
336
444
|
from sphinx.ext.autodoc import SLOTSATTR
|
|
337
445
|
|
|
338
446
|
for name, docstring in subject___slots__.items():
|
|
339
|
-
members[name] = ObjectMember(
|
|
340
|
-
|
|
447
|
+
members[name] = ObjectMember(
|
|
448
|
+
name, SLOTSATTR, class_=subject, docstring=docstring
|
|
449
|
+
)
|
|
341
450
|
except (TypeError, ValueError):
|
|
342
451
|
pass
|
|
343
452
|
|
|
@@ -379,19 +488,27 @@ def get_class_members(subject: Any, objpath: Any, attrgetter: Callable,
|
|
|
379
488
|
else:
|
|
380
489
|
docstring = None
|
|
381
490
|
|
|
382
|
-
members[unmangled] = ObjectMember(
|
|
383
|
-
|
|
491
|
+
members[unmangled] = ObjectMember(
|
|
492
|
+
unmangled, INSTANCEATTR, class_=cls, docstring=docstring
|
|
493
|
+
)
|
|
384
494
|
|
|
385
495
|
# append or complete instance attributes (cf. self.attr1) if analyzer knows
|
|
386
496
|
if analyzer:
|
|
387
497
|
for (ns, name), docstring in analyzer.attr_docs.items():
|
|
388
498
|
if ns == qualname and name not in members:
|
|
389
499
|
# otherwise unknown instance attribute
|
|
390
|
-
members[name] = ObjectMember(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
500
|
+
members[name] = ObjectMember(
|
|
501
|
+
name,
|
|
502
|
+
INSTANCEATTR,
|
|
503
|
+
class_=cls,
|
|
504
|
+
docstring='\n'.join(docstring),
|
|
505
|
+
)
|
|
506
|
+
elif (
|
|
507
|
+
ns == qualname
|
|
508
|
+
and docstring
|
|
509
|
+
and isinstance(members[name], ObjectMember)
|
|
510
|
+
and not members[name].docstring
|
|
511
|
+
):
|
|
395
512
|
if cls != subject and not inherit_docstrings:
|
|
396
513
|
# If we are in the MRO of the class and not the class itself,
|
|
397
514
|
# and we do not want to inherit docstrings, then skip setting
|
sphinx/ext/autodoc/mock.py
CHANGED
|
@@ -35,8 +35,12 @@ class _MockObject:
|
|
|
35
35
|
superclass = args[1][-1].__class__
|
|
36
36
|
if superclass is cls:
|
|
37
37
|
# subclassing MockObject
|
|
38
|
-
return _make_subclass(
|
|
39
|
-
|
|
38
|
+
return _make_subclass(
|
|
39
|
+
args[0],
|
|
40
|
+
superclass.__display_name__,
|
|
41
|
+
superclass=superclass,
|
|
42
|
+
attributes=args[2],
|
|
43
|
+
)
|
|
40
44
|
|
|
41
45
|
return super().__new__(cls)
|
|
42
46
|
|
|
@@ -70,12 +74,19 @@ class _MockObject:
|
|
|
70
74
|
return self.__display_name__
|
|
71
75
|
|
|
72
76
|
|
|
73
|
-
def _make_subclass(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
def _make_subclass(
|
|
78
|
+
name: str,
|
|
79
|
+
module: str,
|
|
80
|
+
superclass: Any = _MockObject,
|
|
81
|
+
attributes: Any = None,
|
|
82
|
+
decorator_args: tuple[Any, ...] = (),
|
|
83
|
+
) -> Any:
|
|
84
|
+
attrs = {
|
|
85
|
+
'__module__': module,
|
|
86
|
+
'__display_name__': module + '.' + name,
|
|
87
|
+
'__name__': name,
|
|
88
|
+
'__sphinx_decorator_args__': decorator_args,
|
|
89
|
+
}
|
|
79
90
|
attrs.update(attributes or {})
|
|
80
91
|
|
|
81
92
|
return type(name, (superclass,), attrs)
|
|
@@ -124,8 +135,12 @@ class MockFinder(MetaPathFinder):
|
|
|
124
135
|
self.loader = MockLoader(self)
|
|
125
136
|
self.mocked_modules: list[str] = []
|
|
126
137
|
|
|
127
|
-
def find_spec(
|
|
128
|
-
|
|
138
|
+
def find_spec(
|
|
139
|
+
self,
|
|
140
|
+
fullname: str,
|
|
141
|
+
path: Sequence[bytes | str] | None,
|
|
142
|
+
target: ModuleType | None = None,
|
|
143
|
+
) -> ModuleSpec | None:
|
|
129
144
|
for modname in self.modnames:
|
|
130
145
|
# check if fullname is (or is a descendant of) one of our targets
|
|
131
146
|
if modname == fullname or fullname.startswith(modname + '.'):
|