Skip to content

Commit 90192fc

Browse files
authored
Use _load_object_by_name() directly (#13978)
1 parent fd32eb5 commit 90192fc

File tree

7 files changed

+98
-61
lines changed

7 files changed

+98
-61
lines changed

sphinx/ext/autodoc/_documenters.py

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from sphinx.ext.autodoc._docstrings import _prepare_docstrings, _process_docstrings
2020
from sphinx.ext.autodoc._member_finder import _document_members
2121
from sphinx.ext.autodoc._renderer import _add_content, _directive_header_lines
22-
from sphinx.ext.autodoc.importer import _load_object_by_name
2322
from sphinx.ext.autodoc.mock import ismock
2423
from sphinx.locale import _, __
2524
from sphinx.pycode import ModuleAnalyzer
@@ -98,13 +97,9 @@ def __init__(
9897
self.get_attr = directive.get_attr
9998
self.orig_name = orig_name
10099
self.indent: Final = indent
101-
# the parent/owner of the object to document
102-
self.parent: Any = None
103100
# the module analyzer to get at attribute docs, or None
104101
self.analyzer: ModuleAnalyzer | None = None
105102

106-
self._load_object_has_been_called = False
107-
108103
if isinstance(self, ModuleDocumenter):
109104
self.options = self.options.merge_member_options()
110105
elif isinstance(self, ClassDocumenter):
@@ -122,35 +117,6 @@ def add_line(self, line: str, source: str, *lineno: int, indent: str) -> None:
122117
else:
123118
self.directive.result.append('', source, *lineno)
124119

125-
def _load_object_by_name(self) -> Literal[True] | None:
126-
"""Import the object given by *self.orig_name*.
127-
128-
Returns True if parsing and resolving was successful, otherwise None.
129-
"""
130-
if self._load_object_has_been_called:
131-
return True
132-
133-
ret = _load_object_by_name(
134-
name=self.orig_name,
135-
objtype=self.objtype, # type: ignore[arg-type]
136-
mock_imports=self.config.autodoc_mock_imports,
137-
type_aliases=self.config.autodoc_type_aliases,
138-
current_document=self._current_document,
139-
config=self.config,
140-
env=self.env,
141-
events=self._events,
142-
get_attr=self.get_attr,
143-
options=self.options,
144-
)
145-
if ret is None:
146-
return None
147-
props, parent = ret
148-
149-
self.props = props
150-
self.parent = parent
151-
self._load_object_has_been_called = True
152-
return True
153-
154120
def add_directive_header(self, *, indent: str) -> None:
155121
"""Add the directive header and options to the generated content."""
156122
domain_name = getattr(self, 'domain', 'py')
@@ -286,41 +252,29 @@ def _assemble_more_content(
286252

287253
return more_content
288254

289-
def generate(
255+
def _generate(
290256
self,
291257
more_content: StringList | None = None,
292258
real_modname: str | None = None,
293259
check_module: bool = False,
294260
all_members: bool = False,
295261
) -> None:
296-
"""Generate reST for the object given by *self.orig_name*, and possibly for
262+
"""Generate reST for the object given by *self.props*, and possibly for
297263
its members.
298264
299265
If *more_content* is given, include that content. If *real_modname* is
300266
given, use that module name to find attribute docs. If *check_module* is
301267
True, only generate if the object is defined in the module name it is
302268
imported from. If *all_members* is True, document all members.
303269
"""
304-
if isinstance(self, ClassDocumenter):
270+
if self.props.obj_type in {'class', 'exception'}:
305271
# Do not pass real_modname and use the name from the __module__
306272
# attribute of the class.
307273
# If a class gets imported into the module real_modname
308274
# the analyzer won't find the source of the class, if
309275
# it looks in real_modname.
310276
real_modname = None
311277

312-
if self._load_object_by_name() is None:
313-
return
314-
315-
self._generate(more_content, real_modname, check_module, all_members)
316-
317-
def _generate(
318-
self,
319-
more_content: StringList | None = None,
320-
real_modname: str | None = None,
321-
check_module: bool = False,
322-
all_members: bool = False,
323-
) -> None:
324278
# If there is no real module defined, figure out which to use.
325279
# The real module is used in the module analyzer to look up the module
326280
# where the attribute documentation would actually be found in.

sphinx/ext/autodoc/_member_finder.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions
1111
from sphinx.ext.autodoc._property_types import _ClassDefProperties, _ModuleProperties
1212
from sphinx.ext.autodoc._sentinels import ALL, INSTANCE_ATTR, SLOTS_ATTR
13+
from sphinx.ext.autodoc.importer import _load_object_by_name
1314
from sphinx.ext.autodoc.mock import ismock, undecorate
1415
from sphinx.locale import __
1516
from sphinx.pycode import ModuleAnalyzer
@@ -174,6 +175,8 @@ def _gather_members(
174175
If *want_all* is True, document all members, else those given by
175176
*self.options.members*.
176177
"""
178+
env = directive.env
179+
177180
if props.obj_type not in {'module', 'class', 'exception'}:
178181
msg = 'must be implemented in subclasses'
179182
raise NotImplementedError(msg)
@@ -236,8 +239,21 @@ def _gather_members(
236239
# We now try to import all objects before ordering them. This is to
237240
# avoid possible circular imports if we were to import objects after
238241
# their associated documenters have been sorted.
239-
if documenter._load_object_by_name() is None:
242+
member_props = _load_object_by_name(
243+
name=full_name,
244+
objtype=obj_type,
245+
mock_imports=config.autodoc_mock_imports,
246+
type_aliases=config.autodoc_type_aliases,
247+
current_document=current_document,
248+
config=config,
249+
env=env,
250+
events=events,
251+
get_attr=get_attr,
252+
options=documenter.options,
253+
)
254+
if member_props is None:
240255
continue
256+
documenter.props = member_props
241257

242258
member_documenters.append((documenter, is_attr))
243259

sphinx/ext/autodoc/directive.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
_process_documenter_options,
1212
)
1313
from sphinx.ext.autodoc._documenters import _AutodocAttrGetter
14+
from sphinx.ext.autodoc.importer import _load_object_by_name
1415
from sphinx.util import logging
1516
from sphinx.util.docutils import SphinxDirective, switch_source_input
1617
from sphinx.util.parsing import nested_parse_to_nodes
@@ -145,7 +146,21 @@ def run(self) -> list[Node]:
145146
self.env, reporter, documenter_options, lineno, self.state, get_attr
146147
)
147148
documenter = doccls(params, self.arguments[0])
148-
documenter.generate(more_content=self.content)
149+
props = _load_object_by_name(
150+
name=self.arguments[0],
151+
objtype=objtype, # type: ignore[arg-type]
152+
mock_imports=self.config.autodoc_mock_imports,
153+
type_aliases=self.config.autodoc_type_aliases,
154+
current_document=self.env.current_document,
155+
config=self.config,
156+
env=self.env,
157+
events=self.env.events,
158+
get_attr=get_attr,
159+
options=documenter.options,
160+
)
161+
if props is not None:
162+
documenter.props = props
163+
documenter._generate(more_content=self.content)
149164
if not params.result:
150165
return []
151166

sphinx/ext/autodoc/importer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ def _load_object_by_name(
495495
events: EventManager,
496496
get_attr: _AttrGetter,
497497
options: _AutoDocumenterOptions,
498-
) -> tuple[_ItemProperties, Any] | None:
498+
) -> _ItemProperties | None:
499499
"""Import and load the object given by *name*."""
500500
parsed = _parse_name(
501501
name=name,
@@ -916,7 +916,7 @@ def _load_object_by_name(
916916
f'{args} -> {retann}' if retann else str(args) for args, retann in signatures
917917
)
918918

919-
return props, parent
919+
return props
920920

921921

922922
def _parse_name(

sphinx/ext/autosummary/__init__.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
from sphinx.ext.autodoc._member_finder import _best_object_type_for_member
7272
from sphinx.ext.autodoc._sentinels import INSTANCE_ATTR
7373
from sphinx.ext.autodoc.directive import DocumenterBridge
74-
from sphinx.ext.autodoc.importer import import_module
74+
from sphinx.ext.autodoc.importer import _load_object_by_name, import_module
7575
from sphinx.ext.autodoc.mock import mock
7676
from sphinx.locale import __
7777
from sphinx.pycode import ModuleAnalyzer
@@ -339,6 +339,12 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
339339
)
340340
raise ValueError(msg)
341341

342+
env = self.env
343+
config = env.config
344+
current_document = env.current_document
345+
events = env.events
346+
get_attr = self.bridge.get_attr
347+
342348
max_item_chars = 50
343349

344350
for name in names:
@@ -363,7 +369,7 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
363369

364370
result = StringList() # initialize for each documenter
365371
obj_type = _get_documenter(obj, parent)
366-
doccls = self.env._registry.documenters[obj_type]
372+
doccls = env._registry.documenters[obj_type]
367373
if isinstance(obj, ModuleType):
368374
full_name = real_name
369375
else:
@@ -374,15 +380,27 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
374380
# handle module prefixes slightly differently
375381
self.bridge.result = result
376382
documenter = doccls(self.bridge, full_name)
377-
if documenter._load_object_by_name() is None:
383+
props = _load_object_by_name(
384+
name=full_name,
385+
objtype=obj_type,
386+
mock_imports=config.autodoc_mock_imports,
387+
type_aliases=config.autodoc_type_aliases,
388+
current_document=current_document,
389+
config=config,
390+
env=env,
391+
events=events,
392+
get_attr=get_attr,
393+
options=documenter.options,
394+
)
395+
if props is None:
378396
logger.warning(
379397
__('failed to import object %s'),
380398
real_name,
381399
location=self.get_location(),
382400
)
383401
items.append((display_name, '', '', real_name))
384402
continue
385-
props = documenter.props
403+
documenter.props = props
386404

387405
# try to also get a source code analyzer for attribute docs
388406
real_module = props._obj___module__ or props.module_name

tests/test_ext_autodoc/autodoc_util.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
# NEVER import those objects from sphinx.ext.autodoc directly
1212
from sphinx.ext.autodoc.directive import DocumenterBridge
13+
from sphinx.ext.autodoc.importer import _load_object_by_name
1314
from sphinx.util.docutils import LoggingReporter
1415
from sphinx.util.inspect import safe_getattr
1516

@@ -43,5 +44,19 @@ def do_autodoc(
4344
app.env, LoggingReporter(''), docoptions, 1, state, safe_getattr
4445
)
4546
documenter = doccls(bridge, name)
46-
documenter.generate()
47+
props = _load_object_by_name(
48+
name=name,
49+
objtype=obj_type,
50+
mock_imports=app.config.autodoc_mock_imports,
51+
type_aliases=app.config.autodoc_type_aliases,
52+
current_document=app.env.current_document,
53+
config=app.config,
54+
env=app.env,
55+
events=app.events,
56+
get_attr=safe_getattr,
57+
options=documenter.options,
58+
)
59+
if props is not None:
60+
documenter.props = props
61+
documenter._generate()
4762
return bridge.result

tests/test_ext_autodoc/test_ext_autodoc.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@
2828
)
2929
from sphinx.ext.autodoc._sentinels import ALL
3030
from sphinx.ext.autodoc.directive import DocumenterBridge
31-
from sphinx.ext.autodoc.importer import _format_signatures, _parse_name
31+
from sphinx.ext.autodoc.importer import (
32+
_format_signatures,
33+
_load_object_by_name,
34+
_parse_name,
35+
)
3236
from sphinx.util.inspect import safe_getattr
3337

3438
from tests.test_ext_autodoc.autodoc_util import do_autodoc
@@ -594,8 +598,23 @@ def _special_getattr(obj, attr_name, *defargs):
594598
def _assert_getter_works(app, directive, objtype, name, *attrs):
595599
getattr_spy.clear()
596600

597-
doccls = app.registry.documenters[objtype](directive, name)
598-
doccls.generate()
601+
doccls = app.registry.documenters[objtype]
602+
documenter = doccls(directive, name)
603+
props = _load_object_by_name(
604+
name=name,
605+
objtype=objtype,
606+
mock_imports=app.config.autodoc_mock_imports,
607+
type_aliases=app.config.autodoc_type_aliases,
608+
current_document=app.env.current_document,
609+
config=app.config,
610+
env=app.env,
611+
events=app.events,
612+
get_attr=directive.get_attr,
613+
options=documenter.options,
614+
)
615+
if props is not None:
616+
documenter.props = props
617+
documenter._generate()
599618

600619
hooked_members = {s[1] for s in getattr_spy}
601620
documented_members = {s[1] for s in processed_signatures}

0 commit comments

Comments
 (0)