Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions sphinx/ext/autodoc/_directive_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,20 +89,6 @@ def copy(self) -> Self:
def from_directive_options(cls, opts: Mapping[str, Any], /) -> Self:
return cls(**{k.replace('-', '_'): v for k, v in opts.items() if v is not None})

def merge_member_options(self) -> Self:
"""Merge :private-members: and :special-members: into :members:"""
if self.members is ALL:
# merging is not needed when members: ALL
return self

members = self.members or []
for others in self.private_members, self.special_members:
if others is not None and others is not ALL:
members.extend(others)
new = self.copy()
new.members = list(dict.fromkeys(members)) # deduplicate; preserve order
return new


def identity(x: Any) -> Any:
return x
Expand Down
10 changes: 0 additions & 10 deletions sphinx/ext/autodoc/_documenters.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,6 @@ def __init__(
# the module analyzer to get at attribute docs, or None
self.analyzer: ModuleAnalyzer | None = None

if isinstance(self, ModuleDocumenter):
self.options = self.options.merge_member_options()
elif isinstance(self, ClassDocumenter):
if self.config.autodoc_class_signature == 'separated':
# show __init__() method
if self.options.special_members is None:
self.options.special_members = []
self.options.special_members += ['__new__', '__init__']
self.options = self.options.merge_member_options()

def add_line(self, line: str, source: str, *lineno: int, indent: str) -> None:
"""Append one line of generated reST to the output."""
if line.strip(): # not a blank line
Expand Down
40 changes: 32 additions & 8 deletions sphinx/ext/autodoc/_member_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,13 @@ def _gather_members(
found_members = _get_members_to_document(
want_all=want_all,
get_attr=get_attr,
class_signature=config.autodoc_class_signature,
inherit_docstrings=config.autodoc_inherit_docstrings,
props=props,
opt_members=options.members or (),
inherited_members=inherited_members,
opt_private_members=options.private_members,
opt_special_members=options.special_members,
ignore_module_all=bool(options.ignore_module_all),
attr_docs=attr_docs,
)
Expand All @@ -205,6 +208,7 @@ def _gather_members(
want_all=want_all,
events=events,
get_attr=get_attr,
class_signature=config.autodoc_class_signature,
inherit_docstrings=config.autodoc_inherit_docstrings,
options=options,
props=props,
Expand All @@ -229,12 +233,10 @@ def _gather_members(
if not obj_type:
# don't know how to document this member
continue
doccls = registry.documenters[obj_type]
# give explicitly separated module name, so that members
# of inner classes can be documented
dotted_parts = '.'.join((*props.parts, member_name))
full_name = f'{props.module_name}::{dotted_parts}'
documenter = doccls(directive, full_name, indent)

# We now try to import all objects before ordering them. This is to
# avoid possible circular imports if we were to import objects after
Expand All @@ -249,10 +251,13 @@ def _gather_members(
env=env,
events=events,
get_attr=get_attr,
options=documenter.options,
options=directive.genopt,
)
if member_props is None:
continue

doccls = registry.documenters[obj_type]
documenter = doccls(directive, full_name, indent)
documenter.props = member_props

member_documenters.append((documenter, is_attr))
Expand All @@ -277,10 +282,13 @@ def _get_members_to_document(
*,
want_all: bool,
get_attr: _AttrGetter,
class_signature: Literal['mixed', 'separated'],
inherit_docstrings: bool,
props: _ModuleProperties | _ClassDefProperties,
opt_members: ALL_T | Sequence[str],
inherited_members: Set[str],
opt_private_members: ALL_T | Sequence[str] | None,
opt_special_members: ALL_T | Sequence[str] | None,
ignore_module_all: bool,
attr_docs: dict[tuple[str, str], list[str]],
) -> list[ObjectMember]:
Expand Down Expand Up @@ -315,7 +323,16 @@ def _get_members_to_document(
else:
# specific members given
assert opt_members is not ALL
wanted_members = frozenset(opt_members)

# Merge :private-members: and :special-members: into :members:
combined_members = set(opt_members)
if opt_private_members is not None and opt_private_members is not ALL:
combined_members.update(opt_private_members)
if opt_special_members is not None and opt_special_members is not ALL:
combined_members.update(opt_special_members)
if class_signature == 'separated' and props.obj_type in {'class', 'exception'}:
combined_members |= {'__new__', '__init__'} # show __init__() method
wanted_members = frozenset(combined_members)

object_members_map: dict[str, ObjectMember] = {}
if props.obj_type == 'module':
Expand Down Expand Up @@ -489,6 +506,7 @@ def _filter_members(
get_attr: _AttrGetter,
options: _AutoDocumenterOptions,
props: _ModuleProperties | _ClassDefProperties,
class_signature: Literal['mixed', 'separated'],
inherit_docstrings: bool,
inherited_members: Set[str],
exclude_members: EMPTY_T | Set[str] | None,
Expand All @@ -513,6 +531,7 @@ def _filter_members(
member_cls=obj.class_,
get_attr=get_attr,
has_attr_doc=has_attr_doc,
class_signature=class_signature,
inherit_docstrings=inherit_docstrings,
inherited_members=inherited_members,
parent=props._obj,
Expand Down Expand Up @@ -793,6 +812,7 @@ def _should_keep_member(
member_cls: Any,
get_attr: _AttrGetter,
has_attr_doc: bool,
class_signature: Literal['mixed', 'separated'],
inherit_docstrings: bool,
inherited_members: Set[str],
parent: Any,
Expand Down Expand Up @@ -862,12 +882,16 @@ def _should_keep_member(

if special_member_re.match(member_name):
# special __methods__
if member_name == '__doc__' or is_filtered_inherited_member:
return False
if special_members and member_name in special_members:
if member_name == '__doc__': # NoQA: SIM114
return False
elif is_filtered_inherited_member:
return False
return has_doc
if (
class_signature == 'separated'
and member_name in {'__new__', '__init__'}
and inspect.isclass(parent)
):
return has_doc # show __init__() method
return False

if is_private:
Expand Down
18 changes: 10 additions & 8 deletions sphinx/ext/autodoc/directive.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,6 @@ def run(self) -> list[Node]:

# generate the output
get_attr = _AutodocAttrGetter(self.env._registry.autodoc_attrgetters)
params = DocumenterBridge(
self.env, reporter, documenter_options, lineno, self.state, get_attr
)
documenter = doccls(params, self.arguments[0])
props = _load_object_by_name(
name=self.arguments[0],
objtype=objtype, # type: ignore[arg-type]
Expand All @@ -156,11 +152,17 @@ def run(self) -> list[Node]:
env=self.env,
events=self.env.events,
get_attr=get_attr,
options=documenter.options,
options=documenter_options,
)
if props is None:
return []

params = DocumenterBridge(
self.env, reporter, documenter_options, lineno, self.state, get_attr
)
if props is not None:
documenter.props = props
documenter._generate(more_content=self.content)
documenter = doccls(params, self.arguments[0])
documenter.props = props
documenter._generate(more_content=self.content)
if not params.result:
return []

Expand Down
10 changes: 5 additions & 5 deletions sphinx/ext/autosummary/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,6 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:

result = StringList() # initialize for each documenter
obj_type = _get_documenter(obj, parent)
doccls = env._registry.documenters[obj_type]
if isinstance(obj, ModuleType):
full_name = real_name
else:
Expand All @@ -379,7 +378,6 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
# NB. using full_name here is important, since Documenters
# handle module prefixes slightly differently
self.bridge.result = result
documenter = doccls(self.bridge, full_name)
props = _load_object_by_name(
name=full_name,
objtype=obj_type,
Expand All @@ -390,7 +388,7 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
env=env,
events=events,
get_attr=get_attr,
options=documenter.options,
options=self.bridge.genopt,
)
if props is None:
logger.warning(
Expand All @@ -400,7 +398,6 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
)
items.append((display_name, '', '', real_name))
continue
documenter.props = props

# try to also get a source code analyzer for attribute docs
real_module = props._obj___module__ or props.module_name
Expand All @@ -413,7 +410,6 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
logger.debug('[autodoc] module analyzer failed: %s', err)
# no source file -- e.g. for builtin and C modules
analyzer = None
documenter.analyzer = analyzer

# -- Grab the signature

Expand All @@ -429,6 +425,10 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:

# -- Grab the summary

doccls = env._registry.documenters[obj_type]
documenter = doccls(self.bridge, full_name)
documenter.props = props
documenter.analyzer = analyzer
documenter.add_content(None, indent=documenter.indent)
lines = result.data[:]
if props.obj_type != 'module':
Expand Down
22 changes: 12 additions & 10 deletions tests/test_ext_autodoc/autodoc_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from typing import TYPE_CHECKING
from unittest.mock import Mock

from docutils.statemachine import StringList

from sphinx.ext.autodoc._directive_options import (
_AutoDocumenterOptions,
_process_documenter_options,
Expand All @@ -17,8 +19,6 @@
if TYPE_CHECKING:
from typing import Any

from docutils.statemachine import StringList

from sphinx.application import Sphinx
from sphinx.ext.autodoc._property_types import _AutodocObjType

Expand All @@ -38,12 +38,7 @@ def do_autodoc(
default_options=app.config.autodoc_default_options,
options=options,
)
docoptions = _AutoDocumenterOptions.from_directive_options(opts)
state = Mock()
bridge = DocumenterBridge(
app.env, LoggingReporter(''), docoptions, 1, state, safe_getattr
)
documenter = doccls(bridge, name)
doc_options = _AutoDocumenterOptions.from_directive_options(opts)
props = _load_object_by_name(
name=name,
objtype=obj_type,
Expand All @@ -54,9 +49,16 @@ def do_autodoc(
env=app.env,
events=app.events,
get_attr=safe_getattr,
options=documenter.options,
options=doc_options,
)
result = StringList()
if props is not None:
state = Mock()
bridge = DocumenterBridge(
app.env, LoggingReporter(''), doc_options, 1, state, safe_getattr
)
bridge.result = result
documenter = doccls(bridge, name)
documenter.props = props
documenter._generate()
return bridge.result
return result
6 changes: 3 additions & 3 deletions tests/test_ext_autodoc/test_ext_autodoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,8 +598,6 @@ def _special_getattr(obj, attr_name, *defargs):
def _assert_getter_works(app, directive, objtype, name, *attrs):
getattr_spy.clear()

doccls = app.registry.documenters[objtype]
documenter = doccls(directive, name)
props = _load_object_by_name(
name=name,
objtype=objtype,
Expand All @@ -610,9 +608,11 @@ def _assert_getter_works(app, directive, objtype, name, *attrs):
env=app.env,
events=app.events,
get_attr=directive.get_attr,
options=documenter.options,
options=directive.genopt,
)
if props is not None:
doccls = app.registry.documenters[objtype]
documenter = doccls(directive, name)
documenter.props = props
documenter._generate()

Expand Down
Loading