Skip to content

Commit 09bcdd9

Browse files
author
Release Manager
committed
gh-38957: rebase sage_autodoc to sphinx 8.1.3 `sage_autodoc.py` is not currently working with sphinx 8.1+. In particular `warningiserror` has mostly been removed. From logs generated by sphinx: ``` # Platform: linux; (Linux-6.6.58-gentoo-dist-x86_64-Intel-R- _Core-TM-_i7-9700_CPU_@_3.00GHz-with-glibc2.40) # Sphinx version: 8.1.3 # Python version: 3.12.7 (CPython) # Docutils version: 0.21.2 # Jinja2 version: 3.1.4 # Pygments version: 2.18.0 # Last messages: # index # # # reading sources... [ 8%] # options # # # reading sources... [ 11%] # sage/misc/trace # # Loaded extensions: # sphinx.ext.mathjax (8.1.3) # alabaster (1.0.0) # sphinxcontrib.applehelp (2.0.0) # sphinxcontrib.devhelp (2.0.0) # sphinxcontrib.htmlhelp (2.1.0) # sphinxcontrib.serializinghtml (2.0.0) # sphinxcontrib.qthelp (2.0.0) # sage_docbuild.ext.inventory_builder (unknown version) # sage_docbuild.ext.multidocs (unknown version) # sphinx.ext.autodoc.preserve_defaults (8.1.3) # sphinx.ext.autodoc.type_comment (8.1.3) # sphinx.ext.autodoc.typehints (8.1.3) # sage_docbuild.ext.sage_autodoc (8.1.3) # sphinx.ext.todo (8.1.3) # sphinx.ext.extlinks (8.1.3) # sphinx.ext.linkcode (8.1.3) # sphinx_copybutton (0.5.2) # sphinx_inline_tabs (2023.04.21) # IPython.sphinxext.ipython_directive (unknown version) # matplotlib.sphinxext.plot_directive (3.9.2) # jupyter_sphinx (0.5.3) # Traceback: Traceback (most recent call last): File "/usr/lib/python3.12/site-packages/sphinx/cmd/build.py", line 514, in build_main app.build(args.force_all, args.filenames) File "/usr/lib/python3.12/site-packages/sphinx/application.py", line 381, in build self.builder.build_update() File "/usr/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 358, in build_update self.build( File "/usr/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 385, in build updated_docnames = set(self.read()) ^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 502, in read self._read_serial(docnames) File "/usr/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 567, in _read_serial self.read_doc(docname) File "/usr/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 630, in read_doc publisher.publish() File "/usr/lib/python3.12/site-packages/docutils/core.py", line 234, in publish self.document = self.reader.read(self.source, self.parser, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/sphinx/io.py", line 106, in read self.parse() File "/usr/lib/python3.12/site-packages/docutils/readers/__init__.py", line 76, in parse self.parser.parse(self.input, document) File "/usr/lib/python3.12/site-packages/sphinx/parsers.py", line 85, in parse self.statemachine.run(inputlines, document, inliner=self.inliner) File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 169, in run results = StateMachineWS.run(self, input_lines, input_offset, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/docutils/statemachine.py", line 233, in run context, next_state, result = self.check_line( ^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/docutils/statemachine.py", line 445, in check_line return method(match, context, next_state) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 2790, in underline self.section(title, source, style, lineno - 1, messages) File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 325, in section self.new_subsection(title, lineno, messages) File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 391, in new_subsection newabsoffset = self.nested_parse( ^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 279, in nested_parse state_machine.run(block, input_offset, memo=self.memo, File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 195, in run results = StateMachineWS.run(self, input_lines, input_offset) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/docutils/statemachine.py", line 233, in run context, next_state, result = self.check_line( ^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/docutils/statemachine.py", line 445, in check_line return method(match, context, next_state) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 2359, in explicit_markup self.explicit_list(blank_finish) File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 2384, in explicit_list newline_offset, blank_finish = self.nested_list_parse( ^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 316, in nested_list_parse state_machine.run(block, input_offset, memo=self.memo, File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 195, in run results = StateMachineWS.run(self, input_lines, input_offset) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/docutils/statemachine.py", line 233, in run context, next_state, result = self.check_line( ^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/docutils/statemachine.py", line 445, in check_line return method(match, context, next_state) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 2662, in explicit_markup nodelist, blank_finish = self.explicit_construct(match) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 2369, in explicit_construct return method(self, expmatch) ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 2106, in directive return self.run_directive( ^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/docutils/parsers/rst/states.py", line 2156, in run_directive result = directive_instance.run() ^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/sphinx/ext/autodoc/directive.py", line 139, in run documenter.generate(more_content=self.content) File "/usr/lib/python3.12/site- packages/sage_docbuild/ext/sage_autodoc.py", line 941, in generate if not self.import_object(): ^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/sage_docbuild/ext/sage_autodoc.py", line 1073, in import_object ret = super().import_object(raiseerror) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site- packages/sage_docbuild/ext/sage_autodoc.py", line 479, in import_object ret = import_object(self.modname, self.objpath, self.objtype, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: import_object() got an unexpected keyword argument 'warningiserror' ``` This PR rebase sage_autodoc to the level of sphinx 8.1.3 while preserving the previous compatibility for python3.9. Some more compatibility fix may have to be added. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. URL: #38957 Reported by: François Bissey Reviewer(s):
2 parents ba94a56 + 9783ba7 commit 09bcdd9

File tree

1 file changed

+67
-29
lines changed

1 file changed

+67
-29
lines changed

src/sage_docbuild/ext/sage_autodoc.py

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
- François Bissey (2024-08-24): rebased on Sphinx 8.0.2
3636
3737
- François Bissey (2024-09-10): Tweaks to support python 3.9 (and older sphinx) as well
38+
39+
- François Bissey (2024-11-12): rebased on Sphinx 8.1.3 (while trying to keep python 3.9 compatibility)
3840
"""
3941

4042
from __future__ import annotations
@@ -44,7 +46,7 @@
4446
import sys
4547
import re
4648
from inspect import Parameter, Signature
47-
from typing import TYPE_CHECKING, Any, ClassVar, NewType, TypeVar
49+
from typing import TYPE_CHECKING, Any, NewType, TypeVar
4850

4951
from docutils.statemachine import StringList
5052

@@ -86,11 +88,19 @@ def getdoc(obj, *args, **kwargs):
8688
if TYPE_CHECKING:
8789
from collections.abc import Callable, Iterator, Sequence
8890
from types import ModuleType
91+
from typing import ClassVar, Literal, TypeAlias
8992

9093
from sphinx.application import Sphinx
9194
from sphinx.environment import BuildEnvironment
9295
from sphinx.ext.autodoc.directive import DocumenterBridge
9396

97+
_AutodocObjType = Literal[
98+
'module', 'class', 'exception', 'function', 'method', 'attribute'
99+
]
100+
_AutodocProcessDocstringListener: TypeAlias = Callable[
101+
[Sphinx, _AutodocObjType, str, Any, dict[str, bool], list[str]], None
102+
]
103+
94104
logger = logging.getLogger(__name__)
95105

96106

@@ -225,15 +235,17 @@ def merge_members_option(options: dict) -> None:
225235

226236
# Some useful event listener factories for autodoc-process-docstring.
227237

228-
def cut_lines(pre: int, post: int = 0, what: str | None = None) -> Callable:
238+
def cut_lines(
239+
pre: int, post: int = 0, what: Sequence[str] | None = None
240+
) -> _AutodocProcessDocstringListener:
229241
"""Return a listener that removes the first *pre* and last *post*
230242
lines of every docstring. If *what* is a sequence of strings,
231243
only docstrings of a type in *what* will be processed.
232244
233245
Use like this (e.g. in the ``setup()`` function of :file:`conf.py`)::
234246
235247
from sphinx.ext.autodoc import cut_lines
236-
app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
248+
app.connect('autodoc-process-docstring', cut_lines(4, what={'module'}))
237249
238250
This can (and should) be used in place of ``automodule_skip_lines``.
239251
"""
@@ -250,9 +262,22 @@ def cut_lines(pre: int, post: int = 0, what: str | None = None) -> Callable:
250262
#
251263
# ... in place of ``automodule_skip_lines``.
252264
# -------------------------------------------------------------------------
253-
def process(app: Sphinx, what_: str, name: str, obj: Any, options: Any, lines: list[str],
254-
) -> None:
255-
if what and what_ not in what:
265+
if not what:
266+
what_unique: frozenset[str] = frozenset()
267+
elif isinstance(what, str): # strongly discouraged
268+
what_unique = frozenset({what})
269+
else:
270+
what_unique = frozenset(what)
271+
272+
def process(
273+
app: Sphinx,
274+
what_: _AutodocObjType,
275+
name: str,
276+
obj: Any,
277+
options: dict[str, bool],
278+
lines: list[str],
279+
) -> None:
280+
if what_unique and what_ not in what_unique:
256281
return
257282
del lines[:pre]
258283
if post:
@@ -271,7 +296,7 @@ def between(
271296
what: Sequence[str] | None = None,
272297
keepempty: bool = False,
273298
exclude: bool = False,
274-
) -> Callable:
299+
) -> _AutodocProcessDocstringListener:
275300
"""Return a listener that either keeps, or if *exclude* is True excludes,
276301
lines between lines that match the *marker* regular expression. If no line
277302
matches, the resulting docstring would be empty, so no change will be made
@@ -282,8 +307,14 @@ def between(
282307
"""
283308
marker_re = re.compile(marker)
284309

285-
def process(app: Sphinx, what_: str, name: str, obj: Any, options: Any, lines: list[str],
286-
) -> None:
310+
def process(
311+
app: Sphinx,
312+
what_: _AutodocObjType,
313+
name: str,
314+
obj: Any,
315+
options: dict[str, bool],
316+
lines: list[str],
317+
) -> None:
287318
if what and what_ not in what:
288319
return
289320
deleted = 0
@@ -308,7 +339,7 @@ def process(app: Sphinx, what_: str, name: str, obj: Any, options: Any, lines: l
308339

309340
# This class is used only in ``sphinx.ext.autodoc.directive``,
310341
# But we define this class here to keep compatibility (see #4538)
311-
class Options(dict):
342+
class Options(dict[str, Any]):
312343
"""A dict/attribute hybrid that returns None on nonexisting keys."""
313344

314345
def copy(self) -> Options:
@@ -476,9 +507,10 @@ def import_object(self, raiseerror: bool = False) -> bool:
476507
"""
477508
with mock(self.config.autodoc_mock_imports):
478509
try:
479-
ret = import_object(self.modname, self.objpath, self.objtype,
480-
attrgetter=self.get_attr,
481-
warningiserror=self.config.autodoc_warningiserror)
510+
ret = import_object(
511+
self.modname, self.objpath, self.objtype,
512+
attrgetter=self.get_attr,
513+
)
482514
self.module, self.parent, self.object_name, self.object = ret
483515
if ismock(self.object):
484516
self.object = undecorate(self.object)
@@ -1145,7 +1177,8 @@ def get_object_members(self, want_all: bool) -> tuple[bool, list[ObjectMember]]:
11451177
else:
11461178
logger.warning(__('missing attribute mentioned in :members: option: '
11471179
'module %s, attribute %s'),
1148-
safe_getattr(self.object, '__name__', '???', name),
1180+
safe_getattr(self.object, '__name__', '???'),
1181+
name,
11491182
type='autodoc')
11501183
return False, ret
11511184

@@ -2179,7 +2212,7 @@ def import_object(self, raiseerror: bool = False) -> bool:
21792212
# annotation only instance variable (PEP-526)
21802213
try:
21812214
with mock(self.config.autodoc_mock_imports):
2182-
parent = import_module(self.modname, self.config.autodoc_warningiserror)
2215+
parent = import_module(self.modname)
21832216
annotations = get_type_hints(parent, None,
21842217
self.config.autodoc_type_aliases,
21852218
include_extras=True)
@@ -2629,9 +2662,10 @@ def import_object(self, raiseerror: bool = False) -> bool:
26292662
except ImportError as exc:
26302663
try:
26312664
with mock(self.config.autodoc_mock_imports):
2632-
ret = import_object(self.modname, self.objpath[:-1], 'class',
2633-
attrgetter=self.get_attr, # type: ignore[attr-defined]
2634-
warningiserror=self.config.autodoc_warningiserror)
2665+
ret = import_object(
2666+
self.modname, self.objpath[:-1], 'class',
2667+
attrgetter=self.get_attr, # type: ignore[attr-defined]
2668+
)
26352669
parent = ret[3]
26362670
if self.is_runtime_instance_attribute(parent):
26372671
self.object = self.RUNTIME_INSTANCE_ATTRIBUTE
@@ -2676,16 +2710,17 @@ def is_uninitialized_instance_attribute(self, parent: Any) -> bool:
26762710
return self.objpath[-1] in annotations
26772711

26782712
def import_object(self, raiseerror: bool = False) -> bool:
2679-
"""Check the exisitence of uninitialized instance attribute when failed to import
2713+
"""Check the existence of uninitialized instance attribute when failed to import
26802714
the attribute.
26812715
"""
26822716
try:
26832717
return super().import_object(raiseerror=True) # type: ignore[misc]
26842718
except ImportError as exc:
26852719
try:
2686-
ret = import_object(self.modname, self.objpath[:-1], 'class',
2687-
attrgetter=self.get_attr, # type: ignore[attr-defined]
2688-
warningiserror=self.config.autodoc_warningiserror)
2720+
ret = import_object(
2721+
self.modname, self.objpath[:-1], 'class',
2722+
attrgetter=self.get_attr, # type: ignore[attr-defined]
2723+
)
26892724
parent = ret[3]
26902725
if self.is_uninitialized_instance_attribute(parent):
26912726
self.object = UNINITIALIZED_ATTR
@@ -2760,9 +2795,7 @@ def can_document_member(
27602795
if isinstance(type(member), ClasscallMetaclass):
27612796
return True
27622797
# ---------------------------------------------------------------------
2763-
if not inspect.isroutine(member) and not isinstance(member, type):
2764-
return True
2765-
return False
2798+
return not inspect.isroutine(member) and not isinstance(member, type)
27662799

27672800
def document_members(self, all_members: bool = False) -> None:
27682801
pass
@@ -2918,7 +2951,7 @@ def can_document_member(
29182951
return False
29192952

29202953
def import_object(self, raiseerror: bool = False) -> bool:
2921-
"""Check the exisitence of uninitialized instance attribute when failed to import
2954+
"""Check the existence of uninitialized instance attribute when failed to import
29222955
the attribute.
29232956
"""
29242957
ret = super().import_object(raiseerror)
@@ -2991,9 +3024,14 @@ def _get_property_getter(self) -> Callable | None:
29913024

29923025
def autodoc_attrgetter(app: Sphinx, obj: Any, name: str, *defargs: Any) -> Any:
29933026
"""Alternative getattr() for types"""
2994-
for typ, func in app.registry.autodoc_attrgettrs.items():
2995-
if isinstance(obj, typ):
2996-
return func(obj, name, *defargs)
3027+
try:
3028+
for typ, func in app.registry.autodoc_attrgetters.items():
3029+
if isinstance(obj, typ):
3030+
return func(obj, name, *defargs)
3031+
except AttributeError:
3032+
for typ, func in app.registry.autodoc_attrgettrs.items():
3033+
if isinstance(obj, typ):
3034+
return func(obj, name, *defargs)
29973035

29983036
return safe_getattr(obj, name, *defargs)
29993037

0 commit comments

Comments
 (0)