Skip to content

Commit 8079eb4

Browse files
authored
fix: handle Sphinx 9.x new autodoc options key format and sentinels (#688)
* fix: handle Sphinx 9.x new autodoc options key format and sentinels Sphinx 9.x's new non-legacy autodoc uses underscored keys (e.g., `exclude_members`) and the `EMPTY` sentinel instead of hyphenated keys (`exclude-members`) and the `ALL` sentinel used by older Sphinx. This caused `:exclude-members:` to be silently ignored by numpydoc, producing spurious `py:obj reference target not found` warnings for excluded inherited members in nitpicky mode. - Update `ClassDoc.__init__` to check both `exclude_members` and `exclude-members` keys, and normalize `EMPTY` to `ALL` - Update `mangle_docstrings` to use `vars()` instead of the deprecated Mapping interface for `_AutoDocumenterOptions` Closes #671 * Add test
1 parent 525b6d2 commit 8079eb4

3 files changed

Lines changed: 62 additions & 5 deletions

File tree

numpydoc/docscrape.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,8 +628,17 @@ def __init__(self, cls, doc=None, modulename="", func_doc=FunctionDoc, config=No
628628

629629
if "sphinx" in sys.modules:
630630
from sphinx.ext.autodoc import ALL
631+
632+
try:
633+
from sphinx.ext.autodoc._sentinels import EMPTY
634+
except ImportError:
635+
try:
636+
from sphinx.ext.autodoc import EMPTY
637+
except ImportError:
638+
EMPTY = object()
631639
else:
632640
ALL = object()
641+
EMPTY = object()
633642

634643
if config is None:
635644
config = {}
@@ -649,7 +658,11 @@ def __init__(self, cls, doc=None, modulename="", func_doc=FunctionDoc, config=No
649658
_members = config.get("members", [])
650659
if _members is ALL:
651660
_members = None
652-
_exclude = config.get("exclude-members", [])
661+
_exclude = config.get("exclude_members")
662+
if _exclude is None:
663+
_exclude = config.get("exclude-members", [])
664+
if _exclude is EMPTY:
665+
_exclude = ALL
653666

654667
if config.get("show_class_members", True) and _exclude is not ALL:
655668

numpydoc/numpydoc.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,17 @@ def mangle_docstrings(app: SphinxApp, what, name, obj, options, lines):
187187
}
188188
# TODO: Find a cleaner way to take care of this change away from dict
189189
# https://github.com/sphinx-doc/sphinx/issues/13942
190-
try:
191-
cfg.update(options or {})
192-
except TypeError:
193-
cfg.update(options.__dict__ or {})
190+
if options is not None:
191+
if isinstance(options, dict):
192+
cfg.update(options)
193+
elif hasattr(options, "from_directive_options"):
194+
# Sphinx 9.x _AutoDocumenterOptions
195+
cfg.update(vars(options))
196+
else:
197+
try:
198+
cfg.update(options or {})
199+
except TypeError:
200+
cfg.update(options.__dict__ or {})
194201
u_NL = "\n"
195202
if what == "module":
196203
# Strip top title

numpydoc/tests/test_numpydoc.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@
44
from pathlib import PosixPath
55

66
import pytest
7+
import sphinx
78
from docutils import nodes
89
from sphinx.ext.autodoc import ALL
910
from sphinx.util import logging
1011

12+
try:
13+
from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions
14+
from sphinx.ext.autodoc._sentinels import EMPTY
15+
except ImportError:
16+
_AutoDocumenterOptions = None # Sphinx < 9
17+
EMPTY = None
18+
1119
from numpydoc.numpydoc import (
1220
_clean_text_signature,
1321
clean_backrefs,
@@ -82,6 +90,35 @@ def test_mangle_docstrings_basic():
8290
assert "upper" not in [x.strip() for x in lines]
8391

8492

93+
@pytest.mark.skipif(
94+
sphinx.version_info < (9, 0),
95+
reason="_AutoDocumenterOptions not available",
96+
)
97+
@pytest.mark.parametrize(
98+
("kwargs", "has_rpartition", "has_upper"),
99+
[
100+
({"exclude_members": {"upper"}}, True, False),
101+
({"exclude_members": EMPTY}, False, False),
102+
({}, True, True),
103+
],
104+
)
105+
def test_mangle_docstrings_exclude_members_sphinx9(kwargs, has_rpartition, has_upper):
106+
"""Non-regression test for #671: Sphinx 9.x _AutoDocumenterOptions with
107+
underscored exclude_members key and EMPTY sentinel."""
108+
s = """
109+
A top section before
110+
111+
.. autoclass:: str
112+
"""
113+
114+
lines = s.split("\n")
115+
options = _AutoDocumenterOptions(**kwargs)
116+
mangle_docstrings(MockApp(), "class", "str", str, options, lines)
117+
lines = [x.strip() for x in lines]
118+
assert ("rpartition" in lines) is has_rpartition
119+
assert ("upper" in lines) is has_upper
120+
121+
85122
def test_mangle_docstrings_inherited_class_members():
86123
# if subclass docs are rendered, this PosixPath should have Path.samefile
87124
p = """

0 commit comments

Comments
 (0)