Sphinx 7.2.5__py3-none-any.whl → 7.3.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 -9
- sphinx/addnodes.py +31 -28
- sphinx/application.py +9 -15
- sphinx/builders/__init__.py +5 -6
- sphinx/builders/_epub_base.py +17 -9
- sphinx/builders/changes.py +10 -5
- sphinx/builders/dirhtml.py +4 -2
- sphinx/builders/dummy.py +3 -2
- sphinx/builders/epub3.py +5 -3
- sphinx/builders/gettext.py +24 -7
- sphinx/builders/html/__init__.py +88 -96
- sphinx/builders/html/_assets.py +16 -16
- sphinx/builders/html/transforms.py +4 -2
- sphinx/builders/latex/__init__.py +40 -33
- sphinx/builders/latex/nodes.py +6 -2
- sphinx/builders/latex/transforms.py +17 -8
- sphinx/builders/latex/util.py +1 -1
- sphinx/builders/linkcheck.py +86 -27
- sphinx/builders/manpage.py +8 -6
- sphinx/builders/singlehtml.py +5 -4
- sphinx/builders/texinfo.py +18 -14
- sphinx/builders/text.py +3 -2
- sphinx/builders/xml.py +5 -2
- sphinx/cmd/build.py +119 -76
- sphinx/cmd/make_mode.py +21 -20
- sphinx/cmd/quickstart.py +13 -16
- sphinx/config.py +432 -250
- sphinx/deprecation.py +23 -13
- sphinx/directives/__init__.py +8 -8
- sphinx/directives/code.py +7 -7
- sphinx/directives/other.py +23 -13
- sphinx/directives/patches.py +7 -6
- sphinx/domains/__init__.py +2 -2
- sphinx/domains/c/__init__.py +796 -0
- sphinx/domains/c/_ast.py +1421 -0
- sphinx/domains/c/_ids.py +65 -0
- sphinx/domains/c/_parser.py +1048 -0
- sphinx/domains/c/_symbol.py +700 -0
- sphinx/domains/changeset.py +11 -7
- sphinx/domains/citation.py +5 -2
- sphinx/domains/cpp/__init__.py +1089 -0
- sphinx/domains/cpp/_ast.py +3635 -0
- sphinx/domains/cpp/_ids.py +537 -0
- sphinx/domains/cpp/_parser.py +2117 -0
- sphinx/domains/cpp/_symbol.py +1092 -0
- sphinx/domains/index.py +6 -4
- sphinx/domains/javascript.py +16 -13
- sphinx/domains/math.py +9 -4
- sphinx/domains/python/__init__.py +890 -0
- sphinx/domains/python/_annotations.py +507 -0
- sphinx/domains/python/_object.py +426 -0
- sphinx/domains/rst.py +12 -7
- sphinx/domains/{std.py → std/__init__.py} +19 -16
- sphinx/environment/__init__.py +21 -19
- sphinx/environment/adapters/indexentries.py +2 -2
- sphinx/environment/adapters/toctree.py +10 -9
- sphinx/environment/collectors/__init__.py +6 -3
- sphinx/environment/collectors/asset.py +4 -3
- sphinx/environment/collectors/dependencies.py +3 -2
- sphinx/environment/collectors/metadata.py +6 -5
- sphinx/environment/collectors/title.py +3 -2
- sphinx/environment/collectors/toctree.py +5 -4
- sphinx/errors.py +13 -2
- sphinx/events.py +14 -9
- sphinx/ext/apidoc.py +9 -11
- sphinx/ext/autodoc/__init__.py +105 -71
- sphinx/ext/autodoc/directive.py +7 -6
- sphinx/ext/autodoc/importer.py +132 -52
- sphinx/ext/autodoc/mock.py +7 -5
- sphinx/ext/autodoc/preserve_defaults.py +4 -3
- sphinx/ext/autodoc/type_comment.py +2 -1
- sphinx/ext/autodoc/typehints.py +5 -4
- sphinx/ext/autosectionlabel.py +3 -2
- sphinx/ext/autosummary/__init__.py +21 -17
- sphinx/ext/autosummary/generate.py +9 -9
- sphinx/ext/coverage.py +26 -20
- sphinx/ext/doctest.py +38 -33
- sphinx/ext/duration.py +1 -0
- sphinx/ext/extlinks.py +4 -3
- sphinx/ext/githubpages.py +3 -2
- sphinx/ext/graphviz.py +10 -7
- sphinx/ext/ifconfig.py +5 -5
- sphinx/ext/imgconverter.py +6 -5
- sphinx/ext/imgmath.py +9 -8
- sphinx/ext/inheritance_diagram.py +31 -31
- sphinx/ext/intersphinx.py +140 -23
- sphinx/ext/linkcode.py +3 -2
- sphinx/ext/mathjax.py +2 -1
- sphinx/ext/napoleon/__init__.py +12 -7
- sphinx/ext/napoleon/docstring.py +34 -32
- sphinx/ext/todo.py +10 -7
- sphinx/ext/viewcode.py +12 -11
- sphinx/extension.py +18 -8
- sphinx/highlighting.py +39 -20
- sphinx/io.py +17 -8
- sphinx/jinja2glue.py +16 -15
- sphinx/locale/__init__.py +30 -23
- sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ar/LC_MESSAGES/sphinx.po +818 -761
- sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bg/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bn/LC_MESSAGES/sphinx.po +835 -778
- sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca/LC_MESSAGES/sphinx.po +864 -807
- sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.po +816 -759
- sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cs/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cy/LC_MESSAGES/sphinx.po +819 -762
- sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/da/LC_MESSAGES/sphinx.po +838 -781
- sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de/LC_MESSAGES/sphinx.po +838 -781
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/el/LC_MESSAGES/sphinx.po +853 -796
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +856 -799
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eo/LC_MESSAGES/sphinx.po +820 -763
- sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es/LC_MESSAGES/sphinx.po +856 -799
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/et/LC_MESSAGES/sphinx.po +845 -788
- sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eu/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fa/LC_MESSAGES/sphinx.po +854 -797
- sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fi/LC_MESSAGES/sphinx.po +816 -759
- sphinx/locale/fr/LC_MESSAGES/sphinx.js +1 -1
- sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr/LC_MESSAGES/sphinx.po +904 -847
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/gl/LC_MESSAGES/sphinx.js +54 -54
- sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.po +1506 -1449
- sphinx/locale/he/LC_MESSAGES/sphinx.js +1 -1
- sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.po +823 -766
- sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi/LC_MESSAGES/sphinx.po +853 -796
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hr/LC_MESSAGES/sphinx.po +844 -787
- sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hu/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/id/LC_MESSAGES/sphinx.po +854 -797
- sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/is/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/it/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ja/LC_MESSAGES/sphinx.po +853 -796
- sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ka/LC_MESSAGES/sphinx.po +848 -791
- sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ko/LC_MESSAGES/sphinx.po +855 -798
- sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lt/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lv/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/mk/LC_MESSAGES/sphinx.po +825 -768
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.js +27 -27
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +876 -818
- sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ne/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nl/LC_MESSAGES/sphinx.po +844 -787
- sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pl/LC_MESSAGES/sphinx.po +845 -788
- sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +908 -851
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ro/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ru/LC_MESSAGES/sphinx.po +838 -781
- sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/si/LC_MESSAGES/sphinx.po +823 -766
- sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sk/LC_MESSAGES/sphinx.po +854 -797
- sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sl/LC_MESSAGES/sphinx.po +832 -775
- sphinx/locale/sphinx.pot +813 -755
- sphinx/locale/sq/LC_MESSAGES/sphinx.js +1 -1
- sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sq/LC_MESSAGES/sphinx.po +865 -808
- sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr/LC_MESSAGES/sphinx.po +835 -778
- sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/ta/LC_MESSAGES/sphinx.js +54 -54
- sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ta/LC_MESSAGES/sphinx.po +1530 -1473
- sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/te/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/tr/LC_MESSAGES/sphinx.po +853 -796
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +833 -776
- sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ur/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/vi/LC_MESSAGES/sphinx.po +837 -780
- sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/yue/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +855 -798
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +811 -754
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.js +1 -1
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +879 -822
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +811 -754
- sphinx/parsers.py +7 -5
- sphinx/project.py +18 -11
- sphinx/pycode/__init__.py +6 -5
- sphinx/pycode/ast.py +23 -8
- sphinx/pycode/parser.py +6 -5
- sphinx/registry.py +12 -6
- sphinx/roles.py +103 -57
- sphinx/search/__init__.py +17 -18
- sphinx/search/da.py +2 -2
- sphinx/search/de.py +2 -2
- sphinx/search/en.py +1 -1
- sphinx/search/es.py +2 -2
- sphinx/search/fi.py +2 -2
- sphinx/search/fr.py +2 -2
- sphinx/search/hu.py +2 -2
- sphinx/search/it.py +2 -2
- sphinx/search/ja.py +13 -22
- sphinx/search/nl.py +2 -2
- sphinx/search/no.py +2 -2
- sphinx/search/pt.py +2 -2
- sphinx/search/ro.py +1 -1
- sphinx/search/ru.py +2 -2
- sphinx/search/sv.py +2 -2
- sphinx/search/tr.py +1 -1
- sphinx/search/zh.py +2 -3
- sphinx/templates/graphviz/graphviz.css +1 -1
- sphinx/testing/fixtures.py +41 -24
- sphinx/testing/path.py +1 -1
- sphinx/testing/util.py +142 -53
- sphinx/texinputs/sphinx.xdy +1 -1
- sphinx/texinputs/sphinxlatextables.sty +1 -1
- sphinx/texinputs/sphinxpackagesubstitutefont.sty +21 -0
- sphinx/themes/agogo/layout.html +4 -4
- sphinx/themes/agogo/static/agogo.css_t +1 -1
- sphinx/themes/agogo/theme.toml +22 -0
- sphinx/themes/basic/defindex.html +1 -1
- sphinx/themes/basic/domainindex.html +1 -1
- sphinx/themes/basic/genindex-single.html +1 -1
- sphinx/themes/basic/genindex-split.html +1 -1
- sphinx/themes/basic/genindex.html +1 -1
- sphinx/themes/basic/globaltoc.html +1 -1
- sphinx/themes/basic/layout.html +1 -1
- sphinx/themes/basic/localtoc.html +1 -1
- sphinx/themes/basic/page.html +1 -1
- sphinx/themes/basic/relations.html +1 -1
- sphinx/themes/basic/search.html +5 -20
- sphinx/themes/basic/searchbox.html +3 -3
- sphinx/themes/basic/searchfield.html +3 -3
- sphinx/themes/basic/sourcelink.html +1 -1
- sphinx/themes/basic/static/basic.css_t +1 -1
- sphinx/themes/basic/static/doctools.js +1 -1
- sphinx/themes/basic/static/language_data.js_t +2 -2
- sphinx/themes/basic/static/searchtools.js +105 -60
- sphinx/themes/basic/theme.toml +23 -0
- sphinx/themes/bizstyle/layout.html +1 -6
- sphinx/themes/bizstyle/static/bizstyle.css_t +1 -1
- sphinx/themes/bizstyle/static/bizstyle.js_t +1 -1
- sphinx/themes/bizstyle/static/css3-mediaqueries_src.js +3 -3
- sphinx/themes/bizstyle/theme.toml +12 -0
- sphinx/themes/classic/layout.html +1 -1
- sphinx/themes/classic/static/classic.css_t +1 -1
- sphinx/themes/classic/static/sidebar.js_t +1 -1
- sphinx/themes/classic/theme.toml +34 -0
- sphinx/themes/default/theme.toml +2 -0
- sphinx/themes/epub/epub-cover.html +1 -1
- sphinx/themes/epub/layout.html +1 -1
- sphinx/themes/epub/static/epub.css_t +1 -1
- sphinx/themes/epub/theme.toml +10 -0
- sphinx/themes/haiku/layout.html +3 -3
- sphinx/themes/haiku/static/haiku.css_t +2 -2
- sphinx/themes/haiku/theme.toml +16 -0
- sphinx/themes/nature/static/nature.css_t +1 -1
- sphinx/themes/nature/theme.toml +6 -0
- sphinx/themes/nonav/layout.html +1 -1
- sphinx/themes/nonav/static/nonav.css_t +1 -1
- sphinx/themes/nonav/theme.toml +10 -0
- sphinx/themes/pyramid/static/epub.css_t +1 -1
- sphinx/themes/pyramid/static/pyramid.css_t +1 -1
- sphinx/themes/pyramid/theme.toml +6 -0
- sphinx/themes/scrolls/artwork/logo.svg +1 -1
- sphinx/themes/scrolls/layout.html +2 -2
- sphinx/themes/scrolls/static/scrolls.css_t +1 -1
- sphinx/themes/scrolls/theme.toml +15 -0
- sphinx/themes/sphinxdoc/static/sphinxdoc.css_t +1 -1
- sphinx/themes/sphinxdoc/theme.toml +6 -0
- sphinx/themes/traditional/static/traditional.css_t +1 -1
- sphinx/themes/traditional/theme.toml +9 -0
- sphinx/theming.py +427 -131
- sphinx/transforms/__init__.py +21 -24
- sphinx/transforms/compact_bullet_list.py +5 -5
- sphinx/transforms/i18n.py +30 -28
- sphinx/transforms/post_transforms/__init__.py +9 -7
- sphinx/transforms/post_transforms/code.py +4 -1
- sphinx/transforms/post_transforms/images.py +17 -13
- sphinx/transforms/references.py +3 -1
- sphinx/util/__init__.py +15 -11
- sphinx/util/_io.py +34 -0
- sphinx/util/_pathlib.py +23 -18
- sphinx/util/build_phase.py +1 -0
- sphinx/util/cfamily.py +19 -11
- sphinx/util/console.py +101 -21
- sphinx/util/display.py +3 -2
- sphinx/util/docfields.py +12 -8
- sphinx/util/docutils.py +21 -35
- sphinx/util/exceptions.py +3 -2
- sphinx/util/fileutil.py +5 -5
- sphinx/util/http_date.py +9 -2
- sphinx/util/i18n.py +40 -9
- sphinx/util/inspect.py +317 -245
- sphinx/util/inventory.py +22 -5
- sphinx/util/logging.py +81 -7
- sphinx/util/matching.py +2 -1
- sphinx/util/math.py +1 -2
- sphinx/util/nodes.py +39 -29
- sphinx/util/osutil.py +25 -6
- sphinx/util/parallel.py +6 -1
- sphinx/util/requests.py +8 -5
- sphinx/util/rst.py +8 -6
- sphinx/util/tags.py +3 -3
- sphinx/util/template.py +8 -3
- sphinx/util/typing.py +76 -42
- sphinx/versioning.py +6 -2
- sphinx/writers/html.py +1 -1
- sphinx/writers/html5.py +17 -13
- sphinx/writers/latex.py +12 -12
- sphinx/writers/manpage.py +13 -7
- sphinx/writers/texinfo.py +13 -10
- sphinx/writers/text.py +13 -23
- sphinx/writers/xml.py +1 -1
- sphinx-7.2.5.dist-info/LICENSE → sphinx-7.3.0.dist-info/LICENSE.rst +1 -1
- {sphinx-7.2.5.dist-info → sphinx-7.3.0.dist-info}/METADATA +13 -12
- sphinx-7.3.0.dist-info/RECORD +581 -0
- sphinx/domains/c.py +0 -3906
- sphinx/domains/cpp.py +0 -8233
- sphinx/domains/python.py +0 -1769
- sphinx/themes/agogo/theme.conf +0 -20
- sphinx/themes/basic/theme.conf +0 -16
- sphinx/themes/bizstyle/theme.conf +0 -10
- sphinx/themes/classic/theme.conf +0 -32
- sphinx/themes/default/theme.conf +0 -2
- sphinx/themes/epub/theme.conf +0 -8
- sphinx/themes/haiku/theme.conf +0 -14
- sphinx/themes/nature/theme.conf +0 -4
- sphinx/themes/nonav/theme.conf +0 -8
- sphinx/themes/pyramid/theme.conf +0 -4
- sphinx/themes/scrolls/theme.conf +0 -13
- sphinx/themes/sphinxdoc/theme.conf +0 -4
- sphinx/themes/traditional/theme.conf +0 -7
- sphinx-7.2.5.dist-info/RECORD +0 -569
- {sphinx-7.2.5.dist-info → sphinx-7.3.0.dist-info}/WHEEL +0 -0
- {sphinx-7.2.5.dist-info → sphinx-7.3.0.dist-info}/entry_points.txt +0 -0
sphinx/util/inspect.py
CHANGED
|
@@ -11,41 +11,45 @@ import re
|
|
|
11
11
|
import sys
|
|
12
12
|
import types
|
|
13
13
|
import typing
|
|
14
|
-
from collections.abc import Mapping
|
|
14
|
+
from collections.abc import Mapping
|
|
15
15
|
from functools import cached_property, partial, partialmethod, singledispatchmethod
|
|
16
16
|
from importlib import import_module
|
|
17
|
-
from inspect import
|
|
18
|
-
Parameter,
|
|
19
|
-
isasyncgenfunction,
|
|
20
|
-
isclass,
|
|
21
|
-
ismethod,
|
|
22
|
-
ismethoddescriptor,
|
|
23
|
-
ismodule,
|
|
24
|
-
)
|
|
17
|
+
from inspect import Parameter, Signature
|
|
25
18
|
from io import StringIO
|
|
26
|
-
from types import
|
|
27
|
-
|
|
28
|
-
MethodDescriptorType,
|
|
29
|
-
MethodType,
|
|
30
|
-
ModuleType,
|
|
31
|
-
WrapperDescriptorType,
|
|
32
|
-
)
|
|
33
|
-
from typing import Any, Callable, cast
|
|
19
|
+
from types import ClassMethodDescriptorType, MethodDescriptorType, WrapperDescriptorType
|
|
20
|
+
from typing import TYPE_CHECKING, Any
|
|
34
21
|
|
|
35
22
|
from sphinx.pycode.ast import unparse as ast_unparse
|
|
36
23
|
from sphinx.util import logging
|
|
37
24
|
from sphinx.util.typing import ForwardRef, stringify_annotation
|
|
38
25
|
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Callable, Sequence
|
|
28
|
+
from inspect import _ParameterKind
|
|
29
|
+
from types import MethodType, ModuleType
|
|
30
|
+
from typing import Final
|
|
31
|
+
|
|
39
32
|
logger = logging.getLogger(__name__)
|
|
40
33
|
|
|
41
34
|
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
|
|
42
35
|
|
|
36
|
+
# re-export as is
|
|
37
|
+
isasyncgenfunction = inspect.isasyncgenfunction
|
|
38
|
+
ismethod = inspect.ismethod
|
|
39
|
+
ismethoddescriptor = inspect.ismethoddescriptor
|
|
40
|
+
isclass = inspect.isclass
|
|
41
|
+
ismodule = inspect.ismodule
|
|
42
|
+
|
|
43
43
|
|
|
44
44
|
def unwrap(obj: Any) -> Any:
|
|
45
|
-
"""Get an original object from wrapped object (wrapped functions).
|
|
45
|
+
"""Get an original object from wrapped object (wrapped functions).
|
|
46
|
+
|
|
47
|
+
Mocked objects are returned as is.
|
|
48
|
+
"""
|
|
46
49
|
if hasattr(obj, '__sphinx_mock__'):
|
|
47
50
|
# Skip unwrapping mock object to avoid RecursionError
|
|
48
51
|
return obj
|
|
52
|
+
|
|
49
53
|
try:
|
|
50
54
|
return inspect.unwrap(obj)
|
|
51
55
|
except ValueError:
|
|
@@ -53,14 +57,28 @@ def unwrap(obj: Any) -> Any:
|
|
|
53
57
|
return obj
|
|
54
58
|
|
|
55
59
|
|
|
56
|
-
def unwrap_all(obj: Any, *, stop: Callable | None = None) -> Any:
|
|
57
|
-
"""
|
|
58
|
-
|
|
59
|
-
functions,
|
|
60
|
+
def unwrap_all(obj: Any, *, stop: Callable[[Any], bool] | None = None) -> Any:
|
|
61
|
+
"""Get an original object from wrapped object.
|
|
62
|
+
|
|
63
|
+
Unlike :func:`unwrap`, this unwraps partial functions, wrapped functions,
|
|
64
|
+
class methods and static methods.
|
|
65
|
+
|
|
66
|
+
When specified, *stop* is a predicate indicating whether an object should
|
|
67
|
+
be unwrapped or not.
|
|
60
68
|
"""
|
|
69
|
+
if callable(stop):
|
|
70
|
+
while not stop(obj):
|
|
71
|
+
if ispartial(obj):
|
|
72
|
+
obj = obj.func
|
|
73
|
+
elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'):
|
|
74
|
+
obj = obj.__wrapped__
|
|
75
|
+
elif isclassmethod(obj) or isstaticmethod(obj):
|
|
76
|
+
obj = obj.__func__
|
|
77
|
+
else:
|
|
78
|
+
return obj
|
|
79
|
+
return obj # in case the while loop never starts
|
|
80
|
+
|
|
61
81
|
while True:
|
|
62
|
-
if stop and stop(obj):
|
|
63
|
-
return obj
|
|
64
82
|
if ispartial(obj):
|
|
65
83
|
obj = obj.func
|
|
66
84
|
elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'):
|
|
@@ -72,10 +90,11 @@ def unwrap_all(obj: Any, *, stop: Callable | None = None) -> Any:
|
|
|
72
90
|
|
|
73
91
|
|
|
74
92
|
def getall(obj: Any) -> Sequence[str] | None:
|
|
75
|
-
"""Get __all__ attribute of
|
|
93
|
+
"""Get the ``__all__`` attribute of an object as sequence.
|
|
76
94
|
|
|
77
|
-
|
|
78
|
-
|
|
95
|
+
This returns ``None`` if the given ``obj.__all__`` does not exist and
|
|
96
|
+
raises :exc:`ValueError` if ``obj.__all__`` is not a list or tuple of
|
|
97
|
+
strings.
|
|
79
98
|
"""
|
|
80
99
|
__all__ = safe_getattr(obj, '__all__', None)
|
|
81
100
|
if __all__ is None:
|
|
@@ -86,35 +105,42 @@ def getall(obj: Any) -> Sequence[str] | None:
|
|
|
86
105
|
|
|
87
106
|
|
|
88
107
|
def getannotations(obj: Any) -> Mapping[str, Any]:
|
|
89
|
-
"""
|
|
90
|
-
|
|
108
|
+
"""Safely get the ``__annotations__`` attribute of an object."""
|
|
109
|
+
if sys.version_info >= (3, 10, 0) or not isinstance(obj, type):
|
|
110
|
+
__annotations__ = safe_getattr(obj, '__annotations__', None)
|
|
111
|
+
else:
|
|
112
|
+
# Workaround for bugfix not available until python 3.10 as recommended by docs
|
|
113
|
+
# https://docs.python.org/3.10/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
|
|
114
|
+
__dict__ = safe_getattr(obj, '__dict__', {})
|
|
115
|
+
__annotations__ = __dict__.get('__annotations__', None)
|
|
91
116
|
if isinstance(__annotations__, Mapping):
|
|
92
117
|
return __annotations__
|
|
93
|
-
|
|
94
|
-
return {}
|
|
118
|
+
return {}
|
|
95
119
|
|
|
96
120
|
|
|
97
121
|
def getglobals(obj: Any) -> Mapping[str, Any]:
|
|
98
|
-
"""
|
|
122
|
+
"""Safely get :attr:`obj.__globals__ <function.__globals__>`."""
|
|
99
123
|
__globals__ = safe_getattr(obj, '__globals__', None)
|
|
100
124
|
if isinstance(__globals__, Mapping):
|
|
101
125
|
return __globals__
|
|
102
|
-
|
|
103
|
-
return {}
|
|
126
|
+
return {}
|
|
104
127
|
|
|
105
128
|
|
|
106
129
|
def getmro(obj: Any) -> tuple[type, ...]:
|
|
107
|
-
"""
|
|
130
|
+
"""Safely get :attr:`obj.__mro__ <class.__mro__>`."""
|
|
108
131
|
__mro__ = safe_getattr(obj, '__mro__', None)
|
|
109
132
|
if isinstance(__mro__, tuple):
|
|
110
133
|
return __mro__
|
|
111
|
-
|
|
112
|
-
return ()
|
|
134
|
+
return ()
|
|
113
135
|
|
|
114
136
|
|
|
115
137
|
def getorigbases(obj: Any) -> tuple[Any, ...] | None:
|
|
116
|
-
"""
|
|
117
|
-
|
|
138
|
+
"""Safely get ``obj.__orig_bases__``.
|
|
139
|
+
|
|
140
|
+
This returns ``None`` if the object is not a class or if ``__orig_bases__``
|
|
141
|
+
is not well-defined (e.g., a non-tuple object or an empty sequence).
|
|
142
|
+
"""
|
|
143
|
+
if not isclass(obj):
|
|
118
144
|
return None
|
|
119
145
|
|
|
120
146
|
# Get __orig_bases__ from obj.__dict__ to avoid accessing the parent's __orig_bases__.
|
|
@@ -123,18 +149,17 @@ def getorigbases(obj: Any) -> tuple[Any, ...] | None:
|
|
|
123
149
|
__orig_bases__ = __dict__.get('__orig_bases__')
|
|
124
150
|
if isinstance(__orig_bases__, tuple) and len(__orig_bases__) > 0:
|
|
125
151
|
return __orig_bases__
|
|
126
|
-
|
|
127
|
-
return None
|
|
152
|
+
return None
|
|
128
153
|
|
|
129
154
|
|
|
130
|
-
def getslots(obj: Any) -> dict[str, Any] | None:
|
|
131
|
-
"""
|
|
155
|
+
def getslots(obj: Any) -> dict[str, Any] | dict[str, None] | None:
|
|
156
|
+
"""Safely get :term:`obj.__slots__ <__slots__>` as a dictionary if any.
|
|
132
157
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
158
|
+
- This returns ``None`` if ``obj.__slots__`` does not exist.
|
|
159
|
+
- This raises a :exc:`TypeError` if *obj* is not a class.
|
|
160
|
+
- This raises a :exc:`ValueError` if ``obj.__slots__`` is invalid.
|
|
136
161
|
"""
|
|
137
|
-
if not
|
|
162
|
+
if not isclass(obj):
|
|
138
163
|
raise TypeError
|
|
139
164
|
|
|
140
165
|
__slots__ = safe_getattr(obj, '__slots__', None)
|
|
@@ -151,7 +176,7 @@ def getslots(obj: Any) -> dict[str, Any] | None:
|
|
|
151
176
|
|
|
152
177
|
|
|
153
178
|
def isNewType(obj: Any) -> bool:
|
|
154
|
-
"""Check the if object is a kind of NewType
|
|
179
|
+
"""Check the if object is a kind of :class:`~typing.NewType`."""
|
|
155
180
|
if sys.version_info[:2] >= (3, 10):
|
|
156
181
|
return isinstance(obj, typing.NewType)
|
|
157
182
|
__module__ = safe_getattr(obj, '__module__', None)
|
|
@@ -160,72 +185,71 @@ def isNewType(obj: Any) -> bool:
|
|
|
160
185
|
|
|
161
186
|
|
|
162
187
|
def isenumclass(x: Any) -> bool:
|
|
163
|
-
"""Check if the object is
|
|
164
|
-
return
|
|
188
|
+
"""Check if the object is an :class:`enumeration class <enum.Enum>`."""
|
|
189
|
+
return isclass(x) and issubclass(x, enum.Enum)
|
|
165
190
|
|
|
166
191
|
|
|
167
192
|
def isenumattribute(x: Any) -> bool:
|
|
168
|
-
"""Check if the object is
|
|
193
|
+
"""Check if the object is an enumeration attribute."""
|
|
169
194
|
return isinstance(x, enum.Enum)
|
|
170
195
|
|
|
171
196
|
|
|
172
197
|
def unpartial(obj: Any) -> Any:
|
|
173
|
-
"""Get an original object from partial object.
|
|
198
|
+
"""Get an original object from a partial-like object.
|
|
174
199
|
|
|
175
|
-
|
|
200
|
+
If *obj* is not a partial object, it is returned as is.
|
|
201
|
+
|
|
202
|
+
.. seealso:: :func:`ispartial`
|
|
176
203
|
"""
|
|
177
204
|
while ispartial(obj):
|
|
178
205
|
obj = obj.func
|
|
179
|
-
|
|
180
206
|
return obj
|
|
181
207
|
|
|
182
208
|
|
|
183
209
|
def ispartial(obj: Any) -> bool:
|
|
184
|
-
"""Check if the object is partial."""
|
|
210
|
+
"""Check if the object is a partial function or method."""
|
|
185
211
|
return isinstance(obj, (partial, partialmethod))
|
|
186
212
|
|
|
187
213
|
|
|
188
214
|
def isclassmethod(obj: Any, cls: Any = None, name: str | None = None) -> bool:
|
|
189
|
-
"""Check if the object is classmethod
|
|
215
|
+
"""Check if the object is a :class:`classmethod`."""
|
|
190
216
|
if isinstance(obj, classmethod):
|
|
191
217
|
return True
|
|
192
|
-
if
|
|
218
|
+
if ismethod(obj) and obj.__self__ is not None and isclass(obj.__self__):
|
|
193
219
|
return True
|
|
194
220
|
if cls and name:
|
|
195
|
-
|
|
221
|
+
# trace __mro__ if the method is defined in parent class
|
|
222
|
+
sentinel = object()
|
|
196
223
|
for basecls in getmro(cls):
|
|
197
|
-
meth = basecls.__dict__.get(name,
|
|
198
|
-
if meth is not
|
|
224
|
+
meth = basecls.__dict__.get(name, sentinel)
|
|
225
|
+
if meth is not sentinel:
|
|
199
226
|
return isclassmethod(meth)
|
|
200
|
-
|
|
201
227
|
return False
|
|
202
228
|
|
|
203
229
|
|
|
204
230
|
def isstaticmethod(obj: Any, cls: Any = None, name: str | None = None) -> bool:
|
|
205
|
-
"""Check if the object is staticmethod
|
|
231
|
+
"""Check if the object is a :class:`staticmethod`."""
|
|
206
232
|
if isinstance(obj, staticmethod):
|
|
207
233
|
return True
|
|
208
234
|
if cls and name:
|
|
209
235
|
# trace __mro__ if the method is defined in parent class
|
|
210
|
-
|
|
211
|
-
# .. note:: This only works well with new style classes.
|
|
236
|
+
sentinel = object()
|
|
212
237
|
for basecls in getattr(cls, '__mro__', [cls]):
|
|
213
|
-
meth = basecls.__dict__.get(name)
|
|
214
|
-
if meth:
|
|
238
|
+
meth = basecls.__dict__.get(name, sentinel)
|
|
239
|
+
if meth is not sentinel:
|
|
215
240
|
return isinstance(meth, staticmethod)
|
|
216
241
|
return False
|
|
217
242
|
|
|
218
243
|
|
|
219
244
|
def isdescriptor(x: Any) -> bool:
|
|
220
|
-
"""Check if the object is
|
|
245
|
+
"""Check if the object is a :external+python:term:`descriptor`."""
|
|
221
246
|
return any(
|
|
222
|
-
callable(safe_getattr(x, item, None))
|
|
223
|
-
for item in ['__get__', '__set__', '__delete__']
|
|
247
|
+
callable(safe_getattr(x, item, None)) for item in ('__get__', '__set__', '__delete__')
|
|
224
248
|
)
|
|
225
249
|
|
|
226
250
|
|
|
227
251
|
def isabstractmethod(obj: Any) -> bool:
|
|
228
|
-
"""Check if the object is an abstractmethod
|
|
252
|
+
"""Check if the object is an :func:`abstractmethod`."""
|
|
229
253
|
return safe_getattr(obj, '__isabstractmethod__', False) is True
|
|
230
254
|
|
|
231
255
|
|
|
@@ -242,86 +266,106 @@ def is_cython_function_or_method(obj: Any) -> bool:
|
|
|
242
266
|
return False
|
|
243
267
|
|
|
244
268
|
|
|
269
|
+
_DESCRIPTOR_LIKE: Final[tuple[type, ...]] = (
|
|
270
|
+
ClassMethodDescriptorType,
|
|
271
|
+
MethodDescriptorType,
|
|
272
|
+
WrapperDescriptorType,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
245
276
|
def isattributedescriptor(obj: Any) -> bool:
|
|
246
|
-
"""Check if the object is an attribute
|
|
277
|
+
"""Check if the object is an attribute-like descriptor."""
|
|
247
278
|
if inspect.isdatadescriptor(obj):
|
|
248
279
|
# data descriptor is kind of attribute
|
|
249
280
|
return True
|
|
250
281
|
if isdescriptor(obj):
|
|
251
282
|
# non data descriptor
|
|
252
283
|
unwrapped = unwrap(obj)
|
|
253
|
-
if isfunction(unwrapped) or isbuiltin(unwrapped) or
|
|
284
|
+
if isfunction(unwrapped) or isbuiltin(unwrapped) or ismethod(unwrapped):
|
|
254
285
|
# attribute must not be either function, builtin and method
|
|
255
286
|
return False
|
|
256
287
|
if is_cython_function_or_method(unwrapped):
|
|
257
288
|
# attribute must not be either function and method (for cython)
|
|
258
289
|
return False
|
|
259
|
-
if
|
|
290
|
+
if isclass(unwrapped):
|
|
260
291
|
# attribute must not be a class
|
|
261
292
|
return False
|
|
262
|
-
if isinstance(unwrapped,
|
|
263
|
-
MethodDescriptorType,
|
|
264
|
-
WrapperDescriptorType)):
|
|
293
|
+
if isinstance(unwrapped, _DESCRIPTOR_LIKE):
|
|
265
294
|
# attribute must not be a method descriptor
|
|
266
295
|
return False
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return False
|
|
270
|
-
return True
|
|
296
|
+
# attribute must not be an instancemethod (C-API)
|
|
297
|
+
return type(unwrapped).__name__ != 'instancemethod'
|
|
271
298
|
return False
|
|
272
299
|
|
|
273
300
|
|
|
274
301
|
def is_singledispatch_function(obj: Any) -> bool:
|
|
275
|
-
"""Check if the object is singledispatch function."""
|
|
276
|
-
return (
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
302
|
+
"""Check if the object is a :func:`~functools.singledispatch` function."""
|
|
303
|
+
return (
|
|
304
|
+
inspect.isfunction(obj)
|
|
305
|
+
and hasattr(obj, 'dispatch')
|
|
306
|
+
and hasattr(obj, 'register')
|
|
307
|
+
and obj.dispatch.__module__ == 'functools'
|
|
308
|
+
)
|
|
280
309
|
|
|
281
310
|
|
|
282
311
|
def is_singledispatch_method(obj: Any) -> bool:
|
|
283
|
-
"""Check if the object is
|
|
312
|
+
"""Check if the object is a :class:`~functools.singledispatchmethod`."""
|
|
284
313
|
return isinstance(obj, singledispatchmethod)
|
|
285
314
|
|
|
286
315
|
|
|
287
316
|
def isfunction(obj: Any) -> bool:
|
|
288
|
-
"""Check if the object is function.
|
|
317
|
+
"""Check if the object is a user-defined function.
|
|
318
|
+
|
|
319
|
+
Partial objects are unwrapped before checking them.
|
|
320
|
+
|
|
321
|
+
.. seealso:: :external+python:func:`inspect.isfunction`
|
|
322
|
+
"""
|
|
289
323
|
return inspect.isfunction(unpartial(obj))
|
|
290
324
|
|
|
291
325
|
|
|
292
326
|
def isbuiltin(obj: Any) -> bool:
|
|
293
|
-
"""Check if the object is function.
|
|
327
|
+
"""Check if the object is a built-in function or method.
|
|
328
|
+
|
|
329
|
+
Partial objects are unwrapped before checking them.
|
|
330
|
+
|
|
331
|
+
.. seealso:: :external+python:func:`inspect.isbuiltin`
|
|
332
|
+
"""
|
|
294
333
|
return inspect.isbuiltin(unpartial(obj))
|
|
295
334
|
|
|
296
335
|
|
|
297
336
|
def isroutine(obj: Any) -> bool:
|
|
298
|
-
"""Check is
|
|
337
|
+
"""Check if the object is a kind of function or method.
|
|
338
|
+
|
|
339
|
+
Partial objects are unwrapped before checking them.
|
|
340
|
+
|
|
341
|
+
.. seealso:: :external+python:func:`inspect.isroutine`
|
|
342
|
+
"""
|
|
299
343
|
return inspect.isroutine(unpartial(obj))
|
|
300
344
|
|
|
301
345
|
|
|
302
346
|
def iscoroutinefunction(obj: Any) -> bool:
|
|
303
|
-
"""Check if the object is coroutine
|
|
304
|
-
|
|
305
|
-
"""Check if the object is wrapped coroutine-function."""
|
|
306
|
-
if isstaticmethod(obj) or isclassmethod(obj) or ispartial(obj):
|
|
307
|
-
# staticmethod, classmethod and partial method are not a wrapped coroutine-function
|
|
308
|
-
# Note: Since 3.10, staticmethod and classmethod becomes a kind of wrappers
|
|
309
|
-
return False
|
|
310
|
-
return hasattr(obj, '__wrapped__')
|
|
311
|
-
|
|
312
|
-
obj = unwrap_all(obj, stop=iswrappedcoroutine)
|
|
347
|
+
"""Check if the object is a :external+python:term:`coroutine` function."""
|
|
348
|
+
obj = unwrap_all(obj, stop=_is_wrapped_coroutine)
|
|
313
349
|
return inspect.iscoroutinefunction(obj)
|
|
314
350
|
|
|
315
351
|
|
|
352
|
+
def _is_wrapped_coroutine(obj: Any) -> bool:
|
|
353
|
+
"""Check if the object is wrapped coroutine-function."""
|
|
354
|
+
if isstaticmethod(obj) or isclassmethod(obj) or ispartial(obj):
|
|
355
|
+
# staticmethod, classmethod and partial method are not a wrapped coroutine-function
|
|
356
|
+
# Note: Since 3.10, staticmethod and classmethod becomes a kind of wrappers
|
|
357
|
+
return False
|
|
358
|
+
return hasattr(obj, '__wrapped__')
|
|
359
|
+
|
|
360
|
+
|
|
316
361
|
def isproperty(obj: Any) -> bool:
|
|
317
|
-
"""Check if the object is property."""
|
|
362
|
+
"""Check if the object is property (possibly cached)."""
|
|
318
363
|
return isinstance(obj, (property, cached_property))
|
|
319
364
|
|
|
320
365
|
|
|
321
366
|
def isgenericalias(obj: Any) -> bool:
|
|
322
|
-
"""Check if the object is
|
|
323
|
-
return isinstance(
|
|
324
|
-
obj, (types.GenericAlias, typing._BaseGenericAlias)) # type: ignore[attr-defined]
|
|
367
|
+
"""Check if the object is a generic alias."""
|
|
368
|
+
return isinstance(obj, (types.GenericAlias, typing._BaseGenericAlias)) # type: ignore[attr-defined]
|
|
325
369
|
|
|
326
370
|
|
|
327
371
|
def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
|
|
@@ -346,7 +390,7 @@ def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
|
|
|
346
390
|
raise AttributeError(name) from exc
|
|
347
391
|
|
|
348
392
|
|
|
349
|
-
def object_description(obj: Any, *, _seen: frozenset = frozenset()) -> str:
|
|
393
|
+
def object_description(obj: Any, *, _seen: frozenset[int] = frozenset()) -> str:
|
|
350
394
|
"""A repr() implementation that returns text safe to use in reST context.
|
|
351
395
|
|
|
352
396
|
Maintains a set of 'seen' object IDs to detect and avoid infinite recursion.
|
|
@@ -362,8 +406,10 @@ def object_description(obj: Any, *, _seen: frozenset = frozenset()) -> str:
|
|
|
362
406
|
# Cannot sort dict keys, fall back to using descriptions as a sort key
|
|
363
407
|
sorted_keys = sorted(obj, key=lambda k: object_description(k, _seen=seen))
|
|
364
408
|
|
|
365
|
-
items = (
|
|
366
|
-
|
|
409
|
+
items = (
|
|
410
|
+
(object_description(key, _seen=seen), object_description(obj[key], _seen=seen))
|
|
411
|
+
for key in sorted_keys
|
|
412
|
+
)
|
|
367
413
|
return '{%s}' % ', '.join(f'{key}: {value}' for (key, value) in items)
|
|
368
414
|
elif isinstance(obj, set):
|
|
369
415
|
if id(obj) in seen:
|
|
@@ -384,15 +430,18 @@ def object_description(obj: Any, *, _seen: frozenset = frozenset()) -> str:
|
|
|
384
430
|
except TypeError:
|
|
385
431
|
# Cannot sort frozenset values, fall back to using descriptions as a sort key
|
|
386
432
|
sorted_values = sorted(obj, key=lambda x: object_description(x, _seen=seen))
|
|
387
|
-
return 'frozenset({%s})' % ', '.join(
|
|
388
|
-
|
|
433
|
+
return 'frozenset({%s})' % ', '.join(
|
|
434
|
+
object_description(x, _seen=seen) for x in sorted_values
|
|
435
|
+
)
|
|
389
436
|
elif isinstance(obj, enum.Enum):
|
|
437
|
+
if obj.__repr__.__func__ is not enum.Enum.__repr__: # type: ignore[attr-defined]
|
|
438
|
+
return repr(obj)
|
|
390
439
|
return f'{obj.__class__.__name__}.{obj.name}'
|
|
391
440
|
elif isinstance(obj, tuple):
|
|
392
441
|
if id(obj) in seen:
|
|
393
442
|
return 'tuple(...)'
|
|
394
443
|
seen |= frozenset([id(obj)])
|
|
395
|
-
return '(
|
|
444
|
+
return '({}{})'.format(
|
|
396
445
|
', '.join(object_description(x, _seen=seen) for x in obj),
|
|
397
446
|
',' * (len(obj) == 1),
|
|
398
447
|
)
|
|
@@ -413,16 +462,18 @@ def object_description(obj: Any, *, _seen: frozenset = frozenset()) -> str:
|
|
|
413
462
|
|
|
414
463
|
|
|
415
464
|
def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
|
|
416
|
-
"""
|
|
465
|
+
"""Check whether *attr_name* is implemented on a builtin class.
|
|
417
466
|
|
|
418
467
|
>>> is_builtin_class_method(int, '__init__')
|
|
419
468
|
True
|
|
420
469
|
|
|
421
|
-
|
|
422
|
-
|
|
470
|
+
|
|
471
|
+
This function is needed since CPython implements ``int.__init__`` via
|
|
472
|
+
descriptors, but PyPy implementation is written in pure Python code.
|
|
423
473
|
"""
|
|
474
|
+
mro = getmro(obj)
|
|
475
|
+
|
|
424
476
|
try:
|
|
425
|
-
mro = getmro(obj)
|
|
426
477
|
cls = next(c for c in mro if attr_name in safe_getattr(c, '__dict__', {}))
|
|
427
478
|
except StopIteration:
|
|
428
479
|
return False
|
|
@@ -449,10 +500,11 @@ class DefaultValue:
|
|
|
449
500
|
|
|
450
501
|
|
|
451
502
|
class TypeAliasForwardRef:
|
|
452
|
-
"""Pseudo typing class for autodoc_type_aliases
|
|
503
|
+
"""Pseudo typing class for :confval:`autodoc_type_aliases`.
|
|
453
504
|
|
|
454
|
-
This avoids the error on evaluating the type inside
|
|
505
|
+
This avoids the error on evaluating the type inside :func:`typing.get_type_hints()`.
|
|
455
506
|
"""
|
|
507
|
+
|
|
456
508
|
def __init__(self, name: str) -> None:
|
|
457
509
|
self.name = name
|
|
458
510
|
|
|
@@ -471,9 +523,9 @@ class TypeAliasForwardRef:
|
|
|
471
523
|
|
|
472
524
|
|
|
473
525
|
class TypeAliasModule:
|
|
474
|
-
"""Pseudo module class for autodoc_type_aliases
|
|
526
|
+
"""Pseudo module class for :confval:`autodoc_type_aliases`."""
|
|
475
527
|
|
|
476
|
-
def __init__(self, modname: str, mapping:
|
|
528
|
+
def __init__(self, modname: str, mapping: Mapping[str, str]) -> None:
|
|
477
529
|
self.__modname = modname
|
|
478
530
|
self.__mapping = mapping
|
|
479
531
|
|
|
@@ -504,12 +556,13 @@ class TypeAliasModule:
|
|
|
504
556
|
|
|
505
557
|
|
|
506
558
|
class TypeAliasNamespace(dict[str, Any]):
|
|
507
|
-
"""Pseudo namespace class for autodoc_type_aliases
|
|
559
|
+
"""Pseudo namespace class for :confval:`autodoc_type_aliases`.
|
|
508
560
|
|
|
509
|
-
|
|
561
|
+
Useful for looking up nested objects via ``namespace.foo.bar.Class``.
|
|
510
562
|
"""
|
|
511
563
|
|
|
512
|
-
def __init__(self, mapping:
|
|
564
|
+
def __init__(self, mapping: Mapping[str, str]) -> None:
|
|
565
|
+
super().__init__()
|
|
513
566
|
self.__mapping = mapping
|
|
514
567
|
|
|
515
568
|
def __getitem__(self, key: str) -> Any:
|
|
@@ -526,19 +579,21 @@ class TypeAliasNamespace(dict[str, Any]):
|
|
|
526
579
|
raise KeyError
|
|
527
580
|
|
|
528
581
|
|
|
529
|
-
def _should_unwrap(subject: Callable) -> bool:
|
|
582
|
+
def _should_unwrap(subject: Callable[..., Any]) -> bool:
|
|
530
583
|
"""Check the function should be unwrapped on getting signature."""
|
|
531
584
|
__globals__ = getglobals(subject)
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
return False
|
|
585
|
+
# contextmanger should be unwrapped
|
|
586
|
+
return (
|
|
587
|
+
__globals__.get('__name__') == 'contextlib'
|
|
588
|
+
and __globals__.get('__file__') == contextlib.__file__
|
|
589
|
+
)
|
|
538
590
|
|
|
539
591
|
|
|
540
|
-
def signature(
|
|
541
|
-
|
|
592
|
+
def signature(
|
|
593
|
+
subject: Callable[..., Any],
|
|
594
|
+
bound_method: bool = False,
|
|
595
|
+
type_aliases: Mapping[str, str] | None = None,
|
|
596
|
+
) -> Signature:
|
|
542
597
|
"""Return a Signature object for the given *subject*.
|
|
543
598
|
|
|
544
599
|
:param bound_method: Specify *subject* is a bound method or not
|
|
@@ -591,37 +646,17 @@ def signature(subject: Callable, bound_method: bool = False, type_aliases: dict
|
|
|
591
646
|
#
|
|
592
647
|
# For example, this helps a function having a default value `inspect._empty`.
|
|
593
648
|
# refs: https://github.com/sphinx-doc/sphinx/issues/7935
|
|
594
|
-
return
|
|
595
|
-
|
|
649
|
+
return Signature(
|
|
650
|
+
parameters, return_annotation=return_annotation, __validate_parameters__=False
|
|
651
|
+
)
|
|
596
652
|
|
|
597
653
|
|
|
598
|
-
def evaluate_signature(
|
|
599
|
-
|
|
600
|
-
|
|
654
|
+
def evaluate_signature(
|
|
655
|
+
sig: Signature,
|
|
656
|
+
globalns: dict[str, Any] | None = None,
|
|
657
|
+
localns: dict[str, Any] | None = None,
|
|
658
|
+
) -> Signature:
|
|
601
659
|
"""Evaluate unresolved type annotations in a signature object."""
|
|
602
|
-
def evaluate_forwardref(ref: ForwardRef, globalns: dict, localns: dict) -> Any:
|
|
603
|
-
"""Evaluate a forward reference."""
|
|
604
|
-
return ref._evaluate(globalns, localns, frozenset())
|
|
605
|
-
|
|
606
|
-
def evaluate(annotation: Any, globalns: dict, localns: dict) -> Any:
|
|
607
|
-
"""Evaluate unresolved type annotation."""
|
|
608
|
-
try:
|
|
609
|
-
if isinstance(annotation, str):
|
|
610
|
-
ref = ForwardRef(annotation, True)
|
|
611
|
-
annotation = evaluate_forwardref(ref, globalns, localns)
|
|
612
|
-
|
|
613
|
-
if isinstance(annotation, ForwardRef):
|
|
614
|
-
annotation = evaluate_forwardref(ref, globalns, localns)
|
|
615
|
-
elif isinstance(annotation, str):
|
|
616
|
-
# might be a ForwardRef'ed annotation in overloaded functions
|
|
617
|
-
ref = ForwardRef(annotation, True)
|
|
618
|
-
annotation = evaluate_forwardref(ref, globalns, localns)
|
|
619
|
-
except (NameError, TypeError):
|
|
620
|
-
# failed to evaluate type. skipped.
|
|
621
|
-
pass
|
|
622
|
-
|
|
623
|
-
return annotation
|
|
624
|
-
|
|
625
660
|
if globalns is None:
|
|
626
661
|
globalns = {}
|
|
627
662
|
if localns is None:
|
|
@@ -630,20 +665,56 @@ def evaluate_signature(sig: inspect.Signature, globalns: dict | None = None,
|
|
|
630
665
|
parameters = list(sig.parameters.values())
|
|
631
666
|
for i, param in enumerate(parameters):
|
|
632
667
|
if param.annotation:
|
|
633
|
-
annotation =
|
|
668
|
+
annotation = _evaluate(param.annotation, globalns, localns)
|
|
634
669
|
parameters[i] = param.replace(annotation=annotation)
|
|
635
670
|
|
|
636
671
|
return_annotation = sig.return_annotation
|
|
637
672
|
if return_annotation:
|
|
638
|
-
return_annotation =
|
|
673
|
+
return_annotation = _evaluate(return_annotation, globalns, localns)
|
|
639
674
|
|
|
640
675
|
return sig.replace(parameters=parameters, return_annotation=return_annotation)
|
|
641
676
|
|
|
642
677
|
|
|
643
|
-
def
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
678
|
+
def _evaluate_forwardref(
|
|
679
|
+
ref: ForwardRef,
|
|
680
|
+
globalns: dict[str, Any] | None,
|
|
681
|
+
localns: dict[str, Any] | None,
|
|
682
|
+
) -> Any:
|
|
683
|
+
"""Evaluate a forward reference."""
|
|
684
|
+
return ref._evaluate(globalns, localns, frozenset())
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def _evaluate(
|
|
688
|
+
annotation: Any,
|
|
689
|
+
globalns: dict[str, Any],
|
|
690
|
+
localns: dict[str, Any],
|
|
691
|
+
) -> Any:
|
|
692
|
+
"""Evaluate unresolved type annotation."""
|
|
693
|
+
try:
|
|
694
|
+
if isinstance(annotation, str):
|
|
695
|
+
ref = ForwardRef(annotation, True)
|
|
696
|
+
annotation = _evaluate_forwardref(ref, globalns, localns)
|
|
697
|
+
|
|
698
|
+
if isinstance(annotation, ForwardRef):
|
|
699
|
+
annotation = _evaluate_forwardref(ref, globalns, localns)
|
|
700
|
+
elif isinstance(annotation, str):
|
|
701
|
+
# might be a ForwardRef'ed annotation in overloaded functions
|
|
702
|
+
ref = ForwardRef(annotation, True)
|
|
703
|
+
annotation = _evaluate_forwardref(ref, globalns, localns)
|
|
704
|
+
except (NameError, TypeError):
|
|
705
|
+
# failed to evaluate type. skipped.
|
|
706
|
+
pass
|
|
707
|
+
|
|
708
|
+
return annotation
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
def stringify_signature(
|
|
712
|
+
sig: Signature,
|
|
713
|
+
show_annotation: bool = True,
|
|
714
|
+
show_return_annotation: bool = True,
|
|
715
|
+
unqualified_typehints: bool = False,
|
|
716
|
+
) -> str:
|
|
717
|
+
"""Stringify a :class:`~inspect.Signature` object.
|
|
647
718
|
|
|
648
719
|
:param show_annotation: If enabled, show annotations on the signature
|
|
649
720
|
:param show_return_annotation: If enabled, show annotation of the return value
|
|
@@ -655,31 +726,35 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
|
|
|
655
726
|
else:
|
|
656
727
|
mode = 'fully-qualified'
|
|
657
728
|
|
|
729
|
+
EMPTY = Parameter.empty
|
|
730
|
+
|
|
658
731
|
args = []
|
|
659
732
|
last_kind = None
|
|
660
733
|
for param in sig.parameters.values():
|
|
661
|
-
if param.kind !=
|
|
734
|
+
if param.kind != Parameter.POSITIONAL_ONLY and last_kind == Parameter.POSITIONAL_ONLY:
|
|
662
735
|
# PEP-570: Separator for Positional Only Parameter: /
|
|
663
736
|
args.append('/')
|
|
664
|
-
if param.kind ==
|
|
665
|
-
|
|
666
|
-
|
|
737
|
+
if param.kind == Parameter.KEYWORD_ONLY and last_kind in (
|
|
738
|
+
Parameter.POSITIONAL_OR_KEYWORD,
|
|
739
|
+
Parameter.POSITIONAL_ONLY,
|
|
740
|
+
None,
|
|
741
|
+
):
|
|
667
742
|
# PEP-3102: Separator for Keyword Only Parameter: *
|
|
668
743
|
args.append('*')
|
|
669
744
|
|
|
670
745
|
arg = StringIO()
|
|
671
|
-
if param.kind
|
|
746
|
+
if param.kind is Parameter.VAR_POSITIONAL:
|
|
672
747
|
arg.write('*' + param.name)
|
|
673
|
-
elif param.kind
|
|
748
|
+
elif param.kind is Parameter.VAR_KEYWORD:
|
|
674
749
|
arg.write('**' + param.name)
|
|
675
750
|
else:
|
|
676
751
|
arg.write(param.name)
|
|
677
752
|
|
|
678
|
-
if show_annotation and param.annotation is not
|
|
753
|
+
if show_annotation and param.annotation is not EMPTY:
|
|
679
754
|
arg.write(': ')
|
|
680
755
|
arg.write(stringify_annotation(param.annotation, mode))
|
|
681
|
-
if param.default is not
|
|
682
|
-
if show_annotation and param.annotation is not
|
|
756
|
+
if param.default is not EMPTY:
|
|
757
|
+
if show_annotation and param.annotation is not EMPTY:
|
|
683
758
|
arg.write(' = ')
|
|
684
759
|
else:
|
|
685
760
|
arg.write('=')
|
|
@@ -688,91 +763,86 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
|
|
|
688
763
|
args.append(arg.getvalue())
|
|
689
764
|
last_kind = param.kind
|
|
690
765
|
|
|
691
|
-
if last_kind
|
|
766
|
+
if last_kind is Parameter.POSITIONAL_ONLY:
|
|
692
767
|
# PEP-570: Separator for Positional Only Parameter: /
|
|
693
768
|
args.append('/')
|
|
694
769
|
|
|
695
770
|
concatenated_args = ', '.join(args)
|
|
696
|
-
if
|
|
697
|
-
show_annotation is False or
|
|
698
|
-
show_return_annotation is False):
|
|
771
|
+
if sig.return_annotation is EMPTY or not show_annotation or not show_return_annotation:
|
|
699
772
|
return f'({concatenated_args})'
|
|
700
773
|
else:
|
|
701
|
-
|
|
702
|
-
return f'({concatenated_args}) -> {
|
|
774
|
+
retann = stringify_annotation(sig.return_annotation, mode)
|
|
775
|
+
return f'({concatenated_args}) -> {retann}'
|
|
703
776
|
|
|
704
777
|
|
|
705
|
-
def signature_from_str(signature: str) ->
|
|
706
|
-
"""Create a Signature object from string."""
|
|
778
|
+
def signature_from_str(signature: str) -> Signature:
|
|
779
|
+
"""Create a :class:`~inspect.Signature` object from a string."""
|
|
707
780
|
code = 'def func' + signature + ': pass'
|
|
708
781
|
module = ast.parse(code)
|
|
709
|
-
function = cast(ast.FunctionDef, module.body[0])
|
|
782
|
+
function = typing.cast(ast.FunctionDef, module.body[0])
|
|
710
783
|
|
|
711
784
|
return signature_from_ast(function, code)
|
|
712
785
|
|
|
713
786
|
|
|
714
|
-
def signature_from_ast(node: ast.FunctionDef, code: str = '') ->
|
|
715
|
-
"""Create a Signature object from AST
|
|
716
|
-
|
|
717
|
-
defaults = list(args.defaults)
|
|
718
|
-
params = []
|
|
719
|
-
if hasattr(args, "posonlyargs"):
|
|
720
|
-
posonlyargs = len(args.posonlyargs)
|
|
721
|
-
positionals = posonlyargs + len(args.args)
|
|
722
|
-
else:
|
|
723
|
-
posonlyargs = 0
|
|
724
|
-
positionals = len(args.args)
|
|
725
|
-
|
|
726
|
-
for _ in range(len(defaults), positionals):
|
|
727
|
-
defaults.insert(0, Parameter.empty) # type: ignore[arg-type]
|
|
728
|
-
|
|
729
|
-
if hasattr(args, "posonlyargs"):
|
|
730
|
-
for i, arg in enumerate(args.posonlyargs):
|
|
731
|
-
if defaults[i] is Parameter.empty:
|
|
732
|
-
default = Parameter.empty
|
|
733
|
-
else:
|
|
734
|
-
default = DefaultValue(
|
|
735
|
-
ast_unparse(defaults[i], code)) # type: ignore[assignment]
|
|
736
|
-
|
|
737
|
-
annotation = ast_unparse(arg.annotation, code) or Parameter.empty
|
|
738
|
-
params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY,
|
|
739
|
-
default=default, annotation=annotation))
|
|
740
|
-
|
|
741
|
-
for i, arg in enumerate(args.args):
|
|
742
|
-
if defaults[i + posonlyargs] is Parameter.empty:
|
|
743
|
-
default = Parameter.empty
|
|
744
|
-
else:
|
|
745
|
-
default = DefaultValue(
|
|
746
|
-
ast_unparse(defaults[i + posonlyargs], code), # type: ignore[assignment]
|
|
747
|
-
)
|
|
748
|
-
|
|
749
|
-
annotation = ast_unparse(arg.annotation, code) or Parameter.empty
|
|
750
|
-
params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD,
|
|
751
|
-
default=default, annotation=annotation))
|
|
787
|
+
def signature_from_ast(node: ast.FunctionDef, code: str = '') -> Signature:
|
|
788
|
+
"""Create a :class:`~inspect.Signature` object from an AST node."""
|
|
789
|
+
EMPTY = Parameter.empty
|
|
752
790
|
|
|
791
|
+
args: ast.arguments = node.args
|
|
792
|
+
defaults: tuple[ast.expr | None, ...] = tuple(args.defaults)
|
|
793
|
+
pos_only_offset = len(args.posonlyargs)
|
|
794
|
+
defaults_offset = pos_only_offset + len(args.args) - len(defaults)
|
|
795
|
+
# The sequence ``D = args.defaults`` contains non-None AST expressions,
|
|
796
|
+
# so we can use ``None`` as a sentinel value for that to indicate that
|
|
797
|
+
# there is no default value for a specific parameter.
|
|
798
|
+
#
|
|
799
|
+
# Let *p* be the number of positional-only and positional-or-keyword
|
|
800
|
+
# arguments. Note that ``0 <= len(D) <= p`` and ``D[0]`` is the default
|
|
801
|
+
# value corresponding to a positional-only *or* a positional-or-keyword
|
|
802
|
+
# argument. Since a non-default argument cannot follow a default argument,
|
|
803
|
+
# the sequence *D* can be completed on the left by adding None sentinels
|
|
804
|
+
# so that ``len(D) == p`` and ``D[i]`` is the *i*-th default argument.
|
|
805
|
+
defaults = (None,) * defaults_offset + defaults
|
|
806
|
+
|
|
807
|
+
# construct the parameter list
|
|
808
|
+
params: list[Parameter] = []
|
|
809
|
+
|
|
810
|
+
# positional-only arguments (introduced in Python 3.8)
|
|
811
|
+
for arg, defexpr in zip(args.posonlyargs, defaults):
|
|
812
|
+
params.append(_define(Parameter.POSITIONAL_ONLY, arg, code, defexpr=defexpr))
|
|
813
|
+
|
|
814
|
+
# normal arguments
|
|
815
|
+
for arg, defexpr in zip(args.args, defaults[pos_only_offset:]):
|
|
816
|
+
params.append(_define(Parameter.POSITIONAL_OR_KEYWORD, arg, code, defexpr=defexpr))
|
|
817
|
+
|
|
818
|
+
# variadic positional argument (no possible default expression)
|
|
753
819
|
if args.vararg:
|
|
754
|
-
|
|
755
|
-
params.append(Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL,
|
|
756
|
-
annotation=annotation))
|
|
820
|
+
params.append(_define(Parameter.VAR_POSITIONAL, args.vararg, code, defexpr=None))
|
|
757
821
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
else:
|
|
762
|
-
default = DefaultValue(
|
|
763
|
-
ast_unparse(args.kw_defaults[i], code)) # type: ignore[arg-type,assignment]
|
|
764
|
-
annotation = ast_unparse(arg.annotation, code) or Parameter.empty
|
|
765
|
-
params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default,
|
|
766
|
-
annotation=annotation))
|
|
822
|
+
# keyword-only arguments
|
|
823
|
+
for arg, defexpr in zip(args.kwonlyargs, args.kw_defaults):
|
|
824
|
+
params.append(_define(Parameter.KEYWORD_ONLY, arg, code, defexpr=defexpr))
|
|
767
825
|
|
|
826
|
+
# variadic keyword argument (no possible default expression)
|
|
768
827
|
if args.kwarg:
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
828
|
+
params.append(_define(Parameter.VAR_KEYWORD, args.kwarg, code, defexpr=None))
|
|
829
|
+
|
|
830
|
+
return_annotation = ast_unparse(node.returns, code) or EMPTY
|
|
831
|
+
return Signature(params, return_annotation=return_annotation)
|
|
832
|
+
|
|
772
833
|
|
|
773
|
-
|
|
834
|
+
def _define(
|
|
835
|
+
kind: _ParameterKind,
|
|
836
|
+
arg: ast.arg,
|
|
837
|
+
code: str,
|
|
838
|
+
*,
|
|
839
|
+
defexpr: ast.expr | None,
|
|
840
|
+
) -> Parameter:
|
|
841
|
+
EMPTY = Parameter.empty
|
|
774
842
|
|
|
775
|
-
|
|
843
|
+
default = EMPTY if defexpr is None else DefaultValue(ast_unparse(defexpr, code))
|
|
844
|
+
annotation = ast_unparse(arg.annotation, code) or EMPTY
|
|
845
|
+
return Parameter(arg.arg, kind, default=default, annotation=annotation)
|
|
776
846
|
|
|
777
847
|
|
|
778
848
|
def getdoc(
|
|
@@ -790,13 +860,6 @@ def getdoc(
|
|
|
790
860
|
* inherited docstring
|
|
791
861
|
* inherited decorated methods
|
|
792
862
|
"""
|
|
793
|
-
def getdoc_internal(obj: Any, attrgetter: Callable = safe_getattr) -> str | None:
|
|
794
|
-
doc = attrgetter(obj, '__doc__', None)
|
|
795
|
-
if isinstance(doc, str):
|
|
796
|
-
return doc
|
|
797
|
-
else:
|
|
798
|
-
return None
|
|
799
|
-
|
|
800
863
|
if cls and name and isclassmethod(obj, cls, name):
|
|
801
864
|
for basecls in getmro(cls):
|
|
802
865
|
meth = basecls.__dict__.get(name)
|
|
@@ -805,7 +868,7 @@ def getdoc(
|
|
|
805
868
|
if doc is not None or not allow_inherited:
|
|
806
869
|
return doc
|
|
807
870
|
|
|
808
|
-
doc =
|
|
871
|
+
doc = _getdoc_internal(obj)
|
|
809
872
|
if ispartial(obj) and doc == obj.__class__.__doc__:
|
|
810
873
|
return getdoc(obj.func)
|
|
811
874
|
elif doc is None and allow_inherited:
|
|
@@ -814,7 +877,7 @@ def getdoc(
|
|
|
814
877
|
for basecls in getmro(cls):
|
|
815
878
|
meth = safe_getattr(basecls, name, None)
|
|
816
879
|
if meth is not None:
|
|
817
|
-
doc =
|
|
880
|
+
doc = _getdoc_internal(meth)
|
|
818
881
|
if doc is not None:
|
|
819
882
|
break
|
|
820
883
|
|
|
@@ -831,3 +894,12 @@ def getdoc(
|
|
|
831
894
|
doc = inspect.getdoc(obj)
|
|
832
895
|
|
|
833
896
|
return doc
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
def _getdoc_internal(
|
|
900
|
+
obj: Any, attrgetter: Callable[[Any, str, Any], Any] = safe_getattr
|
|
901
|
+
) -> str | None:
|
|
902
|
+
doc = attrgetter(obj, '__doc__', None)
|
|
903
|
+
if isinstance(doc, str):
|
|
904
|
+
return doc
|
|
905
|
+
return None
|