Sphinx 7.1.2__py3-none-any.whl → 7.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 +6 -6
- sphinx/__main__.py +3 -1
- sphinx/addnodes.py +35 -22
- sphinx/application.py +40 -38
- sphinx/builders/__init__.py +16 -12
- sphinx/builders/_epub_base.py +15 -11
- sphinx/builders/changes.py +6 -4
- sphinx/builders/dirhtml.py +4 -2
- sphinx/builders/dummy.py +6 -4
- sphinx/builders/epub3.py +16 -8
- sphinx/builders/gettext.py +40 -43
- sphinx/builders/html/__init__.py +166 -196
- sphinx/builders/html/_assets.py +116 -0
- sphinx/builders/html/transforms.py +4 -2
- sphinx/builders/latex/__init__.py +12 -7
- sphinx/builders/latex/theming.py +5 -2
- sphinx/builders/latex/transforms.py +6 -3
- sphinx/builders/linkcheck.py +18 -11
- sphinx/builders/manpage.py +6 -4
- sphinx/builders/singlehtml.py +16 -9
- sphinx/builders/texinfo.py +11 -6
- sphinx/builders/text.py +8 -3
- sphinx/builders/xml.py +9 -4
- sphinx/cmd/build.py +27 -14
- sphinx/cmd/make_mode.py +13 -4
- sphinx/cmd/quickstart.py +13 -4
- sphinx/config.py +17 -14
- sphinx/deprecation.py +4 -2
- sphinx/directives/__init__.py +44 -12
- sphinx/directives/code.py +5 -4
- sphinx/directives/other.py +92 -44
- sphinx/directives/patches.py +1 -1
- sphinx/domains/__init__.py +11 -8
- sphinx/domains/c.py +67 -57
- sphinx/domains/changeset.py +3 -2
- sphinx/domains/citation.py +2 -1
- sphinx/domains/cpp.py +136 -93
- sphinx/domains/index.py +9 -5
- sphinx/domains/javascript.py +32 -19
- sphinx/domains/math.py +5 -3
- sphinx/domains/python.py +69 -57
- sphinx/domains/rst.py +20 -11
- sphinx/domains/std.py +21 -15
- sphinx/environment/__init__.py +97 -65
- sphinx/environment/adapters/indexentries.py +13 -10
- sphinx/environment/adapters/toctree.py +485 -308
- sphinx/environment/collectors/__init__.py +3 -4
- sphinx/environment/collectors/asset.py +10 -4
- sphinx/environment/collectors/dependencies.py +7 -4
- sphinx/environment/collectors/metadata.py +7 -5
- sphinx/environment/collectors/title.py +5 -3
- sphinx/environment/collectors/toctree.py +13 -8
- sphinx/errors.py +1 -1
- sphinx/events.py +5 -5
- sphinx/ext/apidoc.py +49 -27
- sphinx/ext/autodoc/__init__.py +179 -161
- sphinx/ext/autodoc/directive.py +10 -6
- sphinx/ext/autodoc/importer.py +22 -13
- sphinx/ext/autodoc/mock.py +4 -1
- sphinx/ext/autodoc/preserve_defaults.py +80 -12
- sphinx/ext/autodoc/type_comment.py +14 -10
- sphinx/ext/autodoc/typehints.py +7 -3
- sphinx/ext/autosectionlabel.py +6 -3
- sphinx/ext/autosummary/__init__.py +21 -15
- sphinx/ext/autosummary/generate.py +176 -126
- sphinx/ext/coverage.py +93 -8
- sphinx/ext/doctest.py +28 -17
- sphinx/ext/duration.py +19 -17
- sphinx/ext/extlinks.py +11 -6
- sphinx/ext/githubpages.py +8 -7
- sphinx/ext/graphviz.py +61 -17
- sphinx/ext/ifconfig.py +7 -4
- sphinx/ext/imgconverter.py +4 -2
- sphinx/ext/imgmath.py +29 -23
- sphinx/ext/inheritance_diagram.py +41 -27
- sphinx/ext/intersphinx.py +45 -38
- sphinx/ext/linkcode.py +8 -5
- sphinx/ext/mathjax.py +13 -9
- sphinx/ext/napoleon/__init__.py +3 -3
- sphinx/ext/napoleon/docstring.py +40 -31
- sphinx/ext/todo.py +10 -7
- sphinx/ext/viewcode.py +46 -25
- sphinx/extension.py +1 -1
- sphinx/highlighting.py +20 -12
- sphinx/io.py +5 -4
- sphinx/jinja2glue.py +24 -19
- sphinx/locale/__init__.py +8 -2
- sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ar/LC_MESSAGES/sphinx.po +756 -740
- sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bg/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bn/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cs/LC_MESSAGES/sphinx.po +758 -742
- sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cy/LC_MESSAGES/sphinx.po +759 -743
- sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/da/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de/LC_MESSAGES/sphinx.po +759 -743
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/el/LC_MESSAGES/sphinx.po +763 -747
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eo/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es/LC_MESSAGES/sphinx.po +767 -751
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/et/LC_MESSAGES/sphinx.po +762 -746
- sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eu/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fa/LC_MESSAGES/sphinx.po +766 -750
- sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fi/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/gl/LC_MESSAGES/sphinx.js +60 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.po +3695 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi/LC_MESSAGES/sphinx.po +763 -747
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hr/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hu/LC_MESSAGES/sphinx.po +759 -743
- sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/id/LC_MESSAGES/sphinx.po +765 -749
- sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/is/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/it/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ja/LC_MESSAGES/sphinx.po +767 -751
- sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ka/LC_MESSAGES/sphinx.po +759 -743
- sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ko/LC_MESSAGES/sphinx.po +767 -751
- sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lt/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lv/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/mk/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ne/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nl/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pl/LC_MESSAGES/sphinx.po +762 -745
- sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ro/LC_MESSAGES/sphinx.po +759 -743
- sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ru/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/si/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sk/LC_MESSAGES/sphinx.po +765 -749
- sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sl/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/sphinx.pot +748 -740
- sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sq/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ta/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/te/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/tr/LC_MESSAGES/sphinx.po +763 -747
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +760 -749
- sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ur/LC_MESSAGES/sphinx.po +759 -748
- sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/vi/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/yue/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +767 -751
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +754 -738
- sphinx/parsers.py +5 -4
- sphinx/project.py +52 -34
- sphinx/pycode/__init__.py +2 -1
- sphinx/pycode/ast.py +7 -13
- sphinx/pycode/parser.py +42 -38
- sphinx/registry.py +35 -29
- sphinx/roles.py +9 -4
- sphinx/search/__init__.py +5 -17
- sphinx/search/da.py +1 -1
- sphinx/search/de.py +1 -1
- sphinx/search/en.py +1 -1
- sphinx/search/es.py +1 -1
- sphinx/search/fi.py +1 -1
- sphinx/search/fr.py +1 -1
- sphinx/search/hu.py +1 -1
- sphinx/search/it.py +1 -1
- sphinx/search/ja.py +1 -1
- sphinx/search/nl.py +1 -1
- sphinx/search/no.py +1 -1
- sphinx/search/pt.py +1 -1
- sphinx/search/ro.py +1 -1
- sphinx/search/ru.py +1 -1
- sphinx/search/sv.py +1 -1
- sphinx/search/tr.py +1 -1
- sphinx/search/zh.py +1 -1
- sphinx/testing/fixtures.py +23 -30
- sphinx/testing/path.py +9 -0
- sphinx/testing/restructuredtext.py +13 -5
- sphinx/testing/util.py +20 -63
- sphinx/texinputs/sphinxlatexobjects.sty +15 -15
- sphinx/themes/agogo/static/agogo.css_t +10 -4
- sphinx/themes/basic/layout.html +1 -1
- sphinx/themes/basic/static/basic.css_t +4 -0
- sphinx/themes/basic/static/documentation_options.js_t +1 -2
- sphinx/themes/basic/static/searchtools.js +17 -9
- sphinx/themes/basic/static/sphinx_highlight.js +13 -3
- sphinx/themes/bizstyle/static/bizstyle.css_t +4 -0
- sphinx/themes/classic/theme.conf +1 -1
- sphinx/themes/epub/static/epub.css_t +6 -1
- sphinx/themes/haiku/theme.conf +1 -1
- sphinx/themes/nature/static/nature.css_t +4 -0
- sphinx/themes/nonav/static/nonav.css_t +6 -1
- sphinx/themes/pyramid/static/pyramid.css_t +4 -0
- sphinx/themes/scrolls/static/scrolls.css_t +4 -0
- sphinx/themes/scrolls/theme.conf +1 -1
- sphinx/themes/sphinxdoc/static/sphinxdoc.css_t +4 -0
- sphinx/theming.py +9 -7
- sphinx/transforms/__init__.py +79 -3
- sphinx/transforms/compact_bullet_list.py +6 -3
- sphinx/transforms/i18n.py +26 -10
- sphinx/transforms/post_transforms/__init__.py +21 -8
- sphinx/transforms/post_transforms/code.py +6 -3
- sphinx/transforms/post_transforms/images.py +13 -9
- sphinx/util/__init__.py +21 -92
- sphinx/util/cfamily.py +7 -4
- sphinx/util/display.py +3 -2
- sphinx/util/docfields.py +7 -6
- sphinx/util/docstrings.py +1 -1
- sphinx/util/docutils.py +41 -31
- sphinx/util/fileutil.py +9 -6
- sphinx/util/i18n.py +21 -18
- sphinx/util/images.py +2 -1
- sphinx/util/index_entries.py +27 -0
- sphinx/util/inspect.py +83 -67
- sphinx/util/inventory.py +4 -2
- sphinx/util/logging.py +9 -6
- sphinx/util/matching.py +5 -2
- sphinx/util/math.py +6 -3
- sphinx/util/nodes.py +70 -31
- sphinx/util/osutil.py +22 -40
- sphinx/util/parallel.py +4 -1
- sphinx/util/rst.py +7 -3
- sphinx/util/tags.py +11 -4
- sphinx/util/template.py +17 -14
- sphinx/util/typing.py +61 -20
- sphinx/versioning.py +6 -4
- sphinx/writers/html.py +1 -1
- sphinx/writers/html5.py +32 -24
- sphinx/writers/latex.py +67 -53
- sphinx/writers/manpage.py +9 -5
- sphinx/writers/texinfo.py +11 -9
- sphinx/writers/text.py +14 -9
- sphinx/writers/xml.py +3 -2
- {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/METADATA +7 -5
- sphinx-7.2.0.dist-info/RECORD +568 -0
- sphinx/testing/comparer.py +0 -97
- sphinx-7.1.2.dist-info/RECORD +0 -564
- {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/LICENSE +0 -0
- {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/WHEEL +0 -0
- {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/entry_points.txt +0 -0
|
@@ -15,6 +15,7 @@ Example Makefile rule::
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import argparse
|
|
18
|
+
import importlib
|
|
18
19
|
import inspect
|
|
19
20
|
import locale
|
|
20
21
|
import os
|
|
@@ -23,17 +24,15 @@ import pydoc
|
|
|
23
24
|
import re
|
|
24
25
|
import sys
|
|
25
26
|
from os import path
|
|
26
|
-
from typing import TYPE_CHECKING, Any, NamedTuple
|
|
27
|
+
from typing import TYPE_CHECKING, Any, NamedTuple
|
|
27
28
|
|
|
28
29
|
from jinja2 import TemplateNotFound
|
|
29
30
|
from jinja2.sandbox import SandboxedEnvironment
|
|
30
31
|
|
|
31
32
|
import sphinx.locale
|
|
32
33
|
from sphinx import __display_version__, package_dir
|
|
33
|
-
from sphinx.application import Sphinx
|
|
34
34
|
from sphinx.builders import Builder
|
|
35
35
|
from sphinx.config import Config
|
|
36
|
-
from sphinx.ext.autodoc import Documenter
|
|
37
36
|
from sphinx.ext.autodoc.importer import import_module
|
|
38
37
|
from sphinx.ext.autosummary import (
|
|
39
38
|
ImportExceptionGroup,
|
|
@@ -44,14 +43,18 @@ from sphinx.ext.autosummary import (
|
|
|
44
43
|
from sphinx.locale import __
|
|
45
44
|
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
|
46
45
|
from sphinx.registry import SphinxComponentRegistry
|
|
47
|
-
from sphinx.util import logging, rst
|
|
46
|
+
from sphinx.util import logging, rst
|
|
48
47
|
from sphinx.util.inspect import getall, safe_getattr
|
|
49
48
|
from sphinx.util.osutil import ensuredir
|
|
50
49
|
from sphinx.util.template import SphinxTemplateLoader
|
|
51
50
|
|
|
52
51
|
if TYPE_CHECKING:
|
|
52
|
+
from collections.abc import Sequence, Set
|
|
53
53
|
from gettext import NullTranslations
|
|
54
54
|
|
|
55
|
+
from sphinx.application import Sphinx
|
|
56
|
+
from sphinx.ext.autodoc import Documenter
|
|
57
|
+
|
|
55
58
|
logger = logging.getLogger(__name__)
|
|
56
59
|
|
|
57
60
|
|
|
@@ -79,7 +82,7 @@ class DummyApplication:
|
|
|
79
82
|
|
|
80
83
|
class AutosummaryEntry(NamedTuple):
|
|
81
84
|
name: str
|
|
82
|
-
path: str
|
|
85
|
+
path: str | None
|
|
83
86
|
template: str
|
|
84
87
|
recursive: bool
|
|
85
88
|
|
|
@@ -107,7 +110,8 @@ def setup_documenters(app: Any) -> None:
|
|
|
107
110
|
|
|
108
111
|
def _underline(title: str, line: str = '=') -> str:
|
|
109
112
|
if '\n' in title:
|
|
110
|
-
|
|
113
|
+
msg = 'Can only underline single lines'
|
|
114
|
+
raise ValueError(msg)
|
|
111
115
|
return title + '\n' + line * len(title)
|
|
112
116
|
|
|
113
117
|
|
|
@@ -116,7 +120,8 @@ class AutosummaryRenderer:
|
|
|
116
120
|
|
|
117
121
|
def __init__(self, app: Sphinx) -> None:
|
|
118
122
|
if isinstance(app, Builder):
|
|
119
|
-
|
|
123
|
+
msg = 'Expected a Sphinx application object!'
|
|
124
|
+
raise ValueError(msg)
|
|
120
125
|
|
|
121
126
|
system_templates_path = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')]
|
|
122
127
|
loader = SphinxTemplateLoader(app.srcdir, app.config.templates_path,
|
|
@@ -146,6 +151,36 @@ class AutosummaryRenderer:
|
|
|
146
151
|
return template.render(context)
|
|
147
152
|
|
|
148
153
|
|
|
154
|
+
def _split_full_qualified_name(name: str) -> tuple[str | None, str]:
|
|
155
|
+
"""Split full qualified name to a pair of modname and qualname.
|
|
156
|
+
|
|
157
|
+
A qualname is an abbreviation for "Qualified name" introduced at PEP-3155
|
|
158
|
+
(https://peps.python.org/pep-3155/). It is a dotted path name
|
|
159
|
+
from the module top-level.
|
|
160
|
+
|
|
161
|
+
A "full" qualified name means a string containing both module name and
|
|
162
|
+
qualified name.
|
|
163
|
+
|
|
164
|
+
.. note:: This function actually imports the module to check its existence.
|
|
165
|
+
Therefore you need to mock 3rd party modules if needed before
|
|
166
|
+
calling this function.
|
|
167
|
+
"""
|
|
168
|
+
parts = name.split('.')
|
|
169
|
+
for i, _part in enumerate(parts, 1):
|
|
170
|
+
try:
|
|
171
|
+
modname = ".".join(parts[:i])
|
|
172
|
+
importlib.import_module(modname)
|
|
173
|
+
except ImportError:
|
|
174
|
+
if parts[:i - 1]:
|
|
175
|
+
return ".".join(parts[:i - 1]), ".".join(parts[i - 1:])
|
|
176
|
+
else:
|
|
177
|
+
return None, ".".join(parts)
|
|
178
|
+
except IndexError:
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
return name, ""
|
|
182
|
+
|
|
183
|
+
|
|
149
184
|
# -- Generating output ---------------------------------------------------------
|
|
150
185
|
|
|
151
186
|
|
|
@@ -230,104 +265,6 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|
|
230
265
|
qualname: str | None = None) -> str:
|
|
231
266
|
doc = get_documenter(app, obj, parent)
|
|
232
267
|
|
|
233
|
-
def skip_member(obj: Any, name: str, objtype: str) -> bool:
|
|
234
|
-
try:
|
|
235
|
-
return app.emit_firstresult('autodoc-skip-member', objtype, name,
|
|
236
|
-
obj, False, {})
|
|
237
|
-
except Exception as exc:
|
|
238
|
-
logger.warning(__('autosummary: failed to determine %r to be documented, '
|
|
239
|
-
'the following exception was raised:\n%s'),
|
|
240
|
-
name, exc, type='autosummary')
|
|
241
|
-
return False
|
|
242
|
-
|
|
243
|
-
def get_class_members(obj: Any) -> dict[str, Any]:
|
|
244
|
-
members = sphinx.ext.autodoc.get_class_members(obj, [qualname], safe_getattr)
|
|
245
|
-
return {name: member.object for name, member in members.items()}
|
|
246
|
-
|
|
247
|
-
def get_module_members(obj: Any) -> dict[str, Any]:
|
|
248
|
-
members = {}
|
|
249
|
-
for name in members_of(obj, app.config):
|
|
250
|
-
try:
|
|
251
|
-
members[name] = safe_getattr(obj, name)
|
|
252
|
-
except AttributeError:
|
|
253
|
-
continue
|
|
254
|
-
return members
|
|
255
|
-
|
|
256
|
-
def get_all_members(obj: Any) -> dict[str, Any]:
|
|
257
|
-
if doc.objtype == "module":
|
|
258
|
-
return get_module_members(obj)
|
|
259
|
-
elif doc.objtype == "class":
|
|
260
|
-
return get_class_members(obj)
|
|
261
|
-
return {}
|
|
262
|
-
|
|
263
|
-
def get_members(obj: Any, types: set[str], include_public: list[str] = [],
|
|
264
|
-
imported: bool = True) -> tuple[list[str], list[str]]:
|
|
265
|
-
items: list[str] = []
|
|
266
|
-
public: list[str] = []
|
|
267
|
-
|
|
268
|
-
all_members = get_all_members(obj)
|
|
269
|
-
for name, value in all_members.items():
|
|
270
|
-
documenter = get_documenter(app, value, obj)
|
|
271
|
-
if documenter.objtype in types:
|
|
272
|
-
# skip imported members if expected
|
|
273
|
-
if imported or getattr(value, '__module__', None) == obj.__name__:
|
|
274
|
-
skipped = skip_member(value, name, documenter.objtype)
|
|
275
|
-
if skipped is True:
|
|
276
|
-
pass
|
|
277
|
-
elif skipped is False:
|
|
278
|
-
# show the member forcedly
|
|
279
|
-
items.append(name)
|
|
280
|
-
public.append(name)
|
|
281
|
-
else:
|
|
282
|
-
items.append(name)
|
|
283
|
-
if name in include_public or not name.startswith('_'):
|
|
284
|
-
# considers member as public
|
|
285
|
-
public.append(name)
|
|
286
|
-
return public, items
|
|
287
|
-
|
|
288
|
-
def get_module_attrs(members: Any) -> tuple[list[str], list[str]]:
|
|
289
|
-
"""Find module attributes with docstrings."""
|
|
290
|
-
attrs, public = [], []
|
|
291
|
-
try:
|
|
292
|
-
analyzer = ModuleAnalyzer.for_module(name)
|
|
293
|
-
attr_docs = analyzer.find_attr_docs()
|
|
294
|
-
for namespace, attr_name in attr_docs:
|
|
295
|
-
if namespace == '' and attr_name in members:
|
|
296
|
-
attrs.append(attr_name)
|
|
297
|
-
if not attr_name.startswith('_'):
|
|
298
|
-
public.append(attr_name)
|
|
299
|
-
except PycodeError:
|
|
300
|
-
pass # give up if ModuleAnalyzer fails to parse code
|
|
301
|
-
return public, attrs
|
|
302
|
-
|
|
303
|
-
def get_modules(
|
|
304
|
-
obj: Any,
|
|
305
|
-
skip: Sequence[str],
|
|
306
|
-
public_members: Sequence[str] | None = None) -> tuple[list[str], list[str]]:
|
|
307
|
-
items: list[str] = []
|
|
308
|
-
public: list[str] = []
|
|
309
|
-
for _, modname, _ispkg in pkgutil.iter_modules(obj.__path__):
|
|
310
|
-
|
|
311
|
-
if modname in skip:
|
|
312
|
-
# module was overwritten in __init__.py, so not accessible
|
|
313
|
-
continue
|
|
314
|
-
fullname = name + '.' + modname
|
|
315
|
-
try:
|
|
316
|
-
module = import_module(fullname)
|
|
317
|
-
if module and hasattr(module, '__sphinx_mock__'):
|
|
318
|
-
continue
|
|
319
|
-
except ImportError:
|
|
320
|
-
pass
|
|
321
|
-
|
|
322
|
-
items.append(fullname)
|
|
323
|
-
if public_members is not None:
|
|
324
|
-
if modname in public_members:
|
|
325
|
-
public.append(fullname)
|
|
326
|
-
else:
|
|
327
|
-
if not modname.startswith('_'):
|
|
328
|
-
public.append(fullname)
|
|
329
|
-
return public, items
|
|
330
|
-
|
|
331
268
|
ns: dict[str, Any] = {}
|
|
332
269
|
ns.update(context)
|
|
333
270
|
|
|
@@ -339,13 +276,13 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|
|
339
276
|
imported_members = imported_members or ('__all__' in dir(obj) and respect_module_all)
|
|
340
277
|
|
|
341
278
|
ns['functions'], ns['all_functions'] = \
|
|
342
|
-
|
|
279
|
+
_get_members(doc, app, obj, {'function'}, imported=imported_members)
|
|
343
280
|
ns['classes'], ns['all_classes'] = \
|
|
344
|
-
|
|
281
|
+
_get_members(doc, app, obj, {'class'}, imported=imported_members)
|
|
345
282
|
ns['exceptions'], ns['all_exceptions'] = \
|
|
346
|
-
|
|
283
|
+
_get_members(doc, app, obj, {'exception'}, imported=imported_members)
|
|
347
284
|
ns['attributes'], ns['all_attributes'] = \
|
|
348
|
-
|
|
285
|
+
_get_module_attrs(name, ns['members'])
|
|
349
286
|
ispackage = hasattr(obj, '__path__')
|
|
350
287
|
if ispackage and recursive:
|
|
351
288
|
# Use members that are not modules as skip list, because it would then mean
|
|
@@ -365,7 +302,7 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|
|
365
302
|
# Otherwise, use get_modules method normally
|
|
366
303
|
if respect_module_all and '__all__' in dir(obj):
|
|
367
304
|
imported_modules, all_imported_modules = \
|
|
368
|
-
|
|
305
|
+
_get_members(doc, app, obj, {'module'}, imported=True)
|
|
369
306
|
skip += all_imported_modules
|
|
370
307
|
imported_modules = [name + '.' + modname for modname in imported_modules]
|
|
371
308
|
all_imported_modules = \
|
|
@@ -375,7 +312,8 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|
|
375
312
|
imported_modules, all_imported_modules = [], []
|
|
376
313
|
public_members = None
|
|
377
314
|
|
|
378
|
-
modules, all_modules =
|
|
315
|
+
modules, all_modules = _get_modules(obj, skip=skip, name=name,
|
|
316
|
+
public_members=public_members)
|
|
379
317
|
ns['modules'] = imported_modules + modules
|
|
380
318
|
ns["all_modules"] = all_imported_modules + all_modules
|
|
381
319
|
elif doc.objtype == 'class':
|
|
@@ -383,12 +321,12 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|
|
383
321
|
ns['inherited_members'] = \
|
|
384
322
|
set(dir(obj)) - set(obj.__dict__.keys())
|
|
385
323
|
ns['methods'], ns['all_methods'] = \
|
|
386
|
-
|
|
324
|
+
_get_members(doc, app, obj, {'method'}, include_public={'__init__'})
|
|
387
325
|
ns['attributes'], ns['all_attributes'] = \
|
|
388
|
-
|
|
326
|
+
_get_members(doc, app, obj, {'attribute', 'property'})
|
|
389
327
|
|
|
390
328
|
if modname is None or qualname is None:
|
|
391
|
-
modname, qualname =
|
|
329
|
+
modname, qualname = _split_full_qualified_name(name)
|
|
392
330
|
|
|
393
331
|
if doc.objtype in ('method', 'attribute', 'property'):
|
|
394
332
|
ns['class'] = qualname.rsplit(".", 1)[0]
|
|
@@ -412,8 +350,118 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|
|
412
350
|
return template.render(doc.objtype, ns)
|
|
413
351
|
|
|
414
352
|
|
|
415
|
-
def
|
|
416
|
-
|
|
353
|
+
def _skip_member(app: Sphinx, obj: Any, name: str, objtype: str) -> bool:
|
|
354
|
+
try:
|
|
355
|
+
return app.emit_firstresult('autodoc-skip-member', objtype, name,
|
|
356
|
+
obj, False, {})
|
|
357
|
+
except Exception as exc:
|
|
358
|
+
logger.warning(__('autosummary: failed to determine %r to be documented, '
|
|
359
|
+
'the following exception was raised:\n%s'),
|
|
360
|
+
name, exc, type='autosummary')
|
|
361
|
+
return False
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _get_class_members(obj: Any) -> dict[str, Any]:
|
|
365
|
+
members = sphinx.ext.autodoc.get_class_members(obj, None, safe_getattr)
|
|
366
|
+
return {name: member.object for name, member in members.items()}
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _get_module_members(app: Sphinx, obj: Any) -> dict[str, Any]:
|
|
370
|
+
members = {}
|
|
371
|
+
for name in members_of(obj, app.config):
|
|
372
|
+
try:
|
|
373
|
+
members[name] = safe_getattr(obj, name)
|
|
374
|
+
except AttributeError:
|
|
375
|
+
continue
|
|
376
|
+
return members
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _get_all_members(doc: type[Documenter], app: Sphinx, obj: Any) -> dict[str, Any]:
|
|
380
|
+
if doc.objtype == 'module':
|
|
381
|
+
return _get_module_members(app, obj)
|
|
382
|
+
elif doc.objtype == 'class':
|
|
383
|
+
return _get_class_members(obj)
|
|
384
|
+
return {}
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def _get_members(doc: type[Documenter], app: Sphinx, obj: Any, types: set[str], *,
|
|
388
|
+
include_public: Set[str] = frozenset(),
|
|
389
|
+
imported: bool = True) -> tuple[list[str], list[str]]:
|
|
390
|
+
items: list[str] = []
|
|
391
|
+
public: list[str] = []
|
|
392
|
+
|
|
393
|
+
all_members = _get_all_members(doc, app, obj)
|
|
394
|
+
for name, value in all_members.items():
|
|
395
|
+
documenter = get_documenter(app, value, obj)
|
|
396
|
+
if documenter.objtype in types:
|
|
397
|
+
# skip imported members if expected
|
|
398
|
+
if imported or getattr(value, '__module__', None) == obj.__name__:
|
|
399
|
+
skipped = _skip_member(app, value, name, documenter.objtype)
|
|
400
|
+
if skipped is True:
|
|
401
|
+
pass
|
|
402
|
+
elif skipped is False:
|
|
403
|
+
# show the member forcedly
|
|
404
|
+
items.append(name)
|
|
405
|
+
public.append(name)
|
|
406
|
+
else:
|
|
407
|
+
items.append(name)
|
|
408
|
+
if name in include_public or not name.startswith('_'):
|
|
409
|
+
# considers member as public
|
|
410
|
+
public.append(name)
|
|
411
|
+
return public, items
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _get_module_attrs(name: str, members: Any) -> tuple[list[str], list[str]]:
|
|
415
|
+
"""Find module attributes with docstrings."""
|
|
416
|
+
attrs, public = [], []
|
|
417
|
+
try:
|
|
418
|
+
analyzer = ModuleAnalyzer.for_module(name)
|
|
419
|
+
attr_docs = analyzer.find_attr_docs()
|
|
420
|
+
for namespace, attr_name in attr_docs:
|
|
421
|
+
if namespace == '' and attr_name in members:
|
|
422
|
+
attrs.append(attr_name)
|
|
423
|
+
if not attr_name.startswith('_'):
|
|
424
|
+
public.append(attr_name)
|
|
425
|
+
except PycodeError:
|
|
426
|
+
pass # give up if ModuleAnalyzer fails to parse code
|
|
427
|
+
return public, attrs
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def _get_modules(
|
|
431
|
+
obj: Any,
|
|
432
|
+
*,
|
|
433
|
+
skip: Sequence[str],
|
|
434
|
+
name: str,
|
|
435
|
+
public_members: Sequence[str] | None = None) -> tuple[list[str], list[str]]:
|
|
436
|
+
items: list[str] = []
|
|
437
|
+
public: list[str] = []
|
|
438
|
+
for _, modname, _ispkg in pkgutil.iter_modules(obj.__path__):
|
|
439
|
+
|
|
440
|
+
if modname in skip:
|
|
441
|
+
# module was overwritten in __init__.py, so not accessible
|
|
442
|
+
continue
|
|
443
|
+
fullname = name + '.' + modname
|
|
444
|
+
try:
|
|
445
|
+
module = import_module(fullname)
|
|
446
|
+
if module and hasattr(module, '__sphinx_mock__'):
|
|
447
|
+
continue
|
|
448
|
+
except ImportError:
|
|
449
|
+
pass
|
|
450
|
+
|
|
451
|
+
items.append(fullname)
|
|
452
|
+
if public_members is not None:
|
|
453
|
+
if modname in public_members:
|
|
454
|
+
public.append(fullname)
|
|
455
|
+
else:
|
|
456
|
+
if not modname.startswith('_'):
|
|
457
|
+
public.append(fullname)
|
|
458
|
+
return public, items
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def generate_autosummary_docs(sources: list[str],
|
|
462
|
+
output_dir: str | os.PathLike[str] | None = None,
|
|
463
|
+
suffix: str = '.rst',
|
|
464
|
+
base_path: str | os.PathLike[str] | None = None,
|
|
417
465
|
imported_members: bool = False, app: Any = None,
|
|
418
466
|
overwrite: bool = True, encoding: str = 'utf-8') -> None:
|
|
419
467
|
showed_sources = sorted(sources)
|
|
@@ -532,10 +580,10 @@ def find_autosummary_in_docstring(
|
|
|
532
580
|
pass
|
|
533
581
|
except ImportExceptionGroup as exc:
|
|
534
582
|
errors = '\n'.join({f"* {type(e).__name__}: {e}" for e in exc.exceptions})
|
|
535
|
-
|
|
583
|
+
logger.warning(f'Failed to import {name}.\nPossible hints:\n{errors}') # NoQA: G004
|
|
536
584
|
except SystemExit:
|
|
537
|
-
|
|
538
|
-
|
|
585
|
+
logger.warning("Failed to import '%s'; the module executes module level "
|
|
586
|
+
'statement and it might call sys.exit().', name)
|
|
539
587
|
return []
|
|
540
588
|
|
|
541
589
|
|
|
@@ -566,7 +614,7 @@ def find_autosummary_in_lines(
|
|
|
566
614
|
|
|
567
615
|
recursive = False
|
|
568
616
|
toctree: str | None = None
|
|
569
|
-
template =
|
|
617
|
+
template = ''
|
|
570
618
|
current_module = module
|
|
571
619
|
in_autosummary = False
|
|
572
620
|
base_indent = ""
|
|
@@ -616,7 +664,7 @@ def find_autosummary_in_lines(
|
|
|
616
664
|
base_indent = m.group(1)
|
|
617
665
|
recursive = False
|
|
618
666
|
toctree = None
|
|
619
|
-
template =
|
|
667
|
+
template = ''
|
|
620
668
|
continue
|
|
621
669
|
|
|
622
670
|
m = automodule_re.search(line)
|
|
@@ -681,18 +729,20 @@ The format of the autosummary directive is documented in the
|
|
|
681
729
|
return parser
|
|
682
730
|
|
|
683
731
|
|
|
684
|
-
def main(argv:
|
|
732
|
+
def main(argv: Sequence[str] = (), /) -> None:
|
|
685
733
|
locale.setlocale(locale.LC_ALL, '')
|
|
686
734
|
sphinx.locale.init_console()
|
|
687
735
|
|
|
688
736
|
app = DummyApplication(sphinx.locale.get_translator())
|
|
689
|
-
logging.setup(app, sys.stdout, sys.stderr) # type: ignore
|
|
737
|
+
logging.setup(app, sys.stdout, sys.stderr) # type: ignore[arg-type]
|
|
690
738
|
setup_documenters(app)
|
|
691
|
-
args = get_parser().parse_args(argv)
|
|
739
|
+
args = get_parser().parse_args(argv or sys.argv[1:])
|
|
692
740
|
|
|
693
741
|
if args.templates:
|
|
694
742
|
app.config.templates_path.append(path.abspath(args.templates))
|
|
695
|
-
app.config.autosummary_ignore_module_all =
|
|
743
|
+
app.config.autosummary_ignore_module_all = ( # type: ignore[attr-defined]
|
|
744
|
+
not args.respect_module_all
|
|
745
|
+
)
|
|
696
746
|
|
|
697
747
|
generate_autosummary_docs(args.source_file, args.output_dir,
|
|
698
748
|
'.' + args.suffix,
|
|
@@ -701,4 +751,4 @@ def main(argv: list[str] = sys.argv[1:]) -> None:
|
|
|
701
751
|
|
|
702
752
|
|
|
703
753
|
if __name__ == '__main__':
|
|
704
|
-
main()
|
|
754
|
+
main(sys.argv[1:])
|
sphinx/ext/coverage.py
CHANGED
|
@@ -10,25 +10,30 @@ import glob
|
|
|
10
10
|
import inspect
|
|
11
11
|
import pickle
|
|
12
12
|
import re
|
|
13
|
+
import sys
|
|
13
14
|
from importlib import import_module
|
|
14
15
|
from os import path
|
|
15
|
-
from typing import IO, Any
|
|
16
|
+
from typing import IO, TYPE_CHECKING, Any, TextIO
|
|
16
17
|
|
|
17
18
|
import sphinx
|
|
18
|
-
from sphinx.application import Sphinx
|
|
19
19
|
from sphinx.builders import Builder
|
|
20
20
|
from sphinx.locale import __
|
|
21
21
|
from sphinx.util import logging
|
|
22
|
-
from sphinx.util.console import red # type: ignore
|
|
22
|
+
from sphinx.util.console import red # type: ignore[attr-defined]
|
|
23
23
|
from sphinx.util.inspect import safe_getattr
|
|
24
24
|
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from collections.abc import Iterator
|
|
27
|
+
|
|
28
|
+
from sphinx.application import Sphinx
|
|
29
|
+
|
|
25
30
|
logger = logging.getLogger(__name__)
|
|
26
31
|
|
|
27
32
|
|
|
28
33
|
# utility
|
|
29
34
|
def write_header(f: IO[str], text: str, char: str = '-') -> None:
|
|
30
35
|
f.write(text + '\n')
|
|
31
|
-
f.write(char * len(text) + '\n')
|
|
36
|
+
f.write(char * len(text) + '\n\n')
|
|
32
37
|
|
|
33
38
|
|
|
34
39
|
def compile_regex_list(name: str, exps: str) -> list[re.Pattern[str]]:
|
|
@@ -41,6 +46,25 @@ def compile_regex_list(name: str, exps: str) -> list[re.Pattern[str]]:
|
|
|
41
46
|
return lst
|
|
42
47
|
|
|
43
48
|
|
|
49
|
+
def _write_table(table: list[list[str]]) -> Iterator[str]:
|
|
50
|
+
sizes = [max(len(x[column]) for x in table) + 1 for column in range(len(table[0]))]
|
|
51
|
+
|
|
52
|
+
yield _add_line(sizes, '-')
|
|
53
|
+
yield from _add_row(sizes, table[0], '=')
|
|
54
|
+
|
|
55
|
+
for row in table[1:]:
|
|
56
|
+
yield from _add_row(sizes, row, '-')
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _add_line(sizes: list[int], separator: str) -> str:
|
|
60
|
+
return '+' + ''.join((separator * (size + 1)) + '+' for size in sizes)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _add_row(col_widths: list[int], columns: list[str], separator: str) -> Iterator[str]:
|
|
64
|
+
yield ''.join(f'| {column: <{col_widths[i]}}' for i, column in enumerate(columns)) + '|'
|
|
65
|
+
yield _add_line(col_widths, separator)
|
|
66
|
+
|
|
67
|
+
|
|
44
68
|
class CoverageBuilder(Builder):
|
|
45
69
|
"""
|
|
46
70
|
Evaluates coverage of code in the documentation.
|
|
@@ -80,6 +104,8 @@ class CoverageBuilder(Builder):
|
|
|
80
104
|
|
|
81
105
|
def write(self, *ignored: Any) -> None:
|
|
82
106
|
self.py_undoc: dict[str, dict[str, Any]] = {}
|
|
107
|
+
self.py_undocumented: dict[str, set[str]] = {}
|
|
108
|
+
self.py_documented: dict[str, set[str]] = {}
|
|
83
109
|
self.build_py_coverage()
|
|
84
110
|
self.write_py_coverage()
|
|
85
111
|
|
|
@@ -88,8 +114,9 @@ class CoverageBuilder(Builder):
|
|
|
88
114
|
self.write_c_coverage()
|
|
89
115
|
|
|
90
116
|
def build_c_coverage(self) -> None:
|
|
91
|
-
|
|
92
|
-
|
|
117
|
+
c_objects = {}
|
|
118
|
+
for obj in self.env.domains['c'].get_objects():
|
|
119
|
+
c_objects[obj[2]] = obj[1]
|
|
93
120
|
for filename in self.c_sourcefiles:
|
|
94
121
|
undoc: set[tuple[str, str]] = set()
|
|
95
122
|
with open(filename, encoding="utf-8") as f:
|
|
@@ -98,7 +125,11 @@ class CoverageBuilder(Builder):
|
|
|
98
125
|
match = regex.match(line)
|
|
99
126
|
if match:
|
|
100
127
|
name = match.groups()[0]
|
|
101
|
-
if
|
|
128
|
+
if key not in c_objects:
|
|
129
|
+
undoc.add((key, name))
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
if name not in c_objects[key]:
|
|
102
133
|
for exp in self.c_ignorexps.get(key, []):
|
|
103
134
|
if exp.match(name):
|
|
104
135
|
break
|
|
@@ -157,6 +188,9 @@ class CoverageBuilder(Builder):
|
|
|
157
188
|
self.py_undoc[mod_name] = {'error': err}
|
|
158
189
|
continue
|
|
159
190
|
|
|
191
|
+
documented_objects: set[str] = set()
|
|
192
|
+
undocumented_objects: set[str] = set()
|
|
193
|
+
|
|
160
194
|
funcs = []
|
|
161
195
|
classes: dict[str, list[str]] = {}
|
|
162
196
|
|
|
@@ -185,6 +219,9 @@ class CoverageBuilder(Builder):
|
|
|
185
219
|
if skip_undoc and not obj.__doc__:
|
|
186
220
|
continue
|
|
187
221
|
funcs.append(name)
|
|
222
|
+
undocumented_objects.add(full_name)
|
|
223
|
+
else:
|
|
224
|
+
documented_objects.add(full_name)
|
|
188
225
|
elif inspect.isclass(obj):
|
|
189
226
|
for exp in self.cls_ignorexps:
|
|
190
227
|
if exp.match(name):
|
|
@@ -220,11 +257,47 @@ class CoverageBuilder(Builder):
|
|
|
220
257
|
continue
|
|
221
258
|
if full_attr_name not in objects:
|
|
222
259
|
attrs.append(attr_name)
|
|
260
|
+
undocumented_objects.add(full_attr_name)
|
|
261
|
+
else:
|
|
262
|
+
documented_objects.add(full_attr_name)
|
|
263
|
+
|
|
223
264
|
if attrs:
|
|
224
265
|
# some attributes are undocumented
|
|
225
266
|
classes[name] = attrs
|
|
226
267
|
|
|
227
268
|
self.py_undoc[mod_name] = {'funcs': funcs, 'classes': classes}
|
|
269
|
+
self.py_undocumented[mod_name] = undocumented_objects
|
|
270
|
+
self.py_documented[mod_name] = documented_objects
|
|
271
|
+
|
|
272
|
+
def _write_py_statistics(self, op: TextIO) -> None:
|
|
273
|
+
""" Outputs the table of ``op``."""
|
|
274
|
+
all_modules = set(self.py_documented.keys()).union(
|
|
275
|
+
set(self.py_undocumented.keys()))
|
|
276
|
+
all_objects: set[str] = set()
|
|
277
|
+
all_documented_objects: set[str] = set()
|
|
278
|
+
for module in all_modules:
|
|
279
|
+
all_module_objects = self.py_documented[module].union(self.py_undocumented[module])
|
|
280
|
+
all_objects = all_objects.union(all_module_objects)
|
|
281
|
+
all_documented_objects = all_documented_objects.union(self.py_documented[module])
|
|
282
|
+
|
|
283
|
+
# prepare tabular
|
|
284
|
+
table = [['Module', 'Coverage', 'Undocumented']]
|
|
285
|
+
for module in all_modules:
|
|
286
|
+
module_objects = self.py_documented[module].union(self.py_undocumented[module])
|
|
287
|
+
if len(module_objects):
|
|
288
|
+
value = 100.0 * len(self.py_documented[module]) / len(module_objects)
|
|
289
|
+
else:
|
|
290
|
+
value = 100.0
|
|
291
|
+
|
|
292
|
+
table.append([module, '%.2f%%' % value, '%d' % len(self.py_undocumented[module])])
|
|
293
|
+
table.append([
|
|
294
|
+
'TOTAL',
|
|
295
|
+
f'{100 * len(all_documented_objects) / len(all_objects):.2f}%',
|
|
296
|
+
f'{len(all_objects) - len(all_documented_objects)}',
|
|
297
|
+
])
|
|
298
|
+
|
|
299
|
+
for line in _write_table(table):
|
|
300
|
+
op.write(f'{line}\n')
|
|
228
301
|
|
|
229
302
|
def write_py_coverage(self) -> None:
|
|
230
303
|
output_file = path.join(self.outdir, 'python.txt')
|
|
@@ -232,6 +305,15 @@ class CoverageBuilder(Builder):
|
|
|
232
305
|
with open(output_file, 'w', encoding="utf-8") as op:
|
|
233
306
|
if self.config.coverage_write_headline:
|
|
234
307
|
write_header(op, 'Undocumented Python objects', '=')
|
|
308
|
+
|
|
309
|
+
if self.config.coverage_statistics_to_stdout:
|
|
310
|
+
self._write_py_statistics(sys.stdout)
|
|
311
|
+
|
|
312
|
+
if self.config.coverage_statistics_to_report:
|
|
313
|
+
write_header(op, 'Statistics')
|
|
314
|
+
self._write_py_statistics(op)
|
|
315
|
+
op.write('\n')
|
|
316
|
+
|
|
235
317
|
keys = sorted(self.py_undoc.keys())
|
|
236
318
|
for name in keys:
|
|
237
319
|
undoc = self.py_undoc[name]
|
|
@@ -297,7 +379,8 @@ class CoverageBuilder(Builder):
|
|
|
297
379
|
# dump the coverage data to a pickle file too
|
|
298
380
|
picklepath = path.join(self.outdir, 'undoc.pickle')
|
|
299
381
|
with open(picklepath, 'wb') as dumpfile:
|
|
300
|
-
pickle.dump((self.py_undoc, self.c_undoc
|
|
382
|
+
pickle.dump((self.py_undoc, self.c_undoc,
|
|
383
|
+
self.py_undocumented, self.py_documented), dumpfile)
|
|
301
384
|
|
|
302
385
|
|
|
303
386
|
def setup(app: Sphinx) -> dict[str, Any]:
|
|
@@ -310,6 +393,8 @@ def setup(app: Sphinx) -> dict[str, Any]:
|
|
|
310
393
|
app.add_config_value('coverage_c_regexes', {}, False)
|
|
311
394
|
app.add_config_value('coverage_ignore_c_items', {}, False)
|
|
312
395
|
app.add_config_value('coverage_write_headline', True, False)
|
|
396
|
+
app.add_config_value('coverage_statistics_to_report', True, False, (bool,))
|
|
397
|
+
app.add_config_value('coverage_statistics_to_stdout', True, False, (bool,))
|
|
313
398
|
app.add_config_value('coverage_skip_undoc_in_source', False, False)
|
|
314
399
|
app.add_config_value('coverage_show_missing_items', False, False)
|
|
315
400
|
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|