Skip to content

Commit 3dec862

Browse files
authored
Delete FakeDirective in autosummary (sphinx-doc#13967)
1 parent dcfaa98 commit 3dec862

File tree

3 files changed

+44
-80
lines changed

3 files changed

+44
-80
lines changed

sphinx/ext/autodoc/_documenters.py

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,8 @@ def _gather_members(
598598
member=member,
599599
member_name=member_name,
600600
is_attr=is_attr,
601-
parent_documenter=self,
601+
parent_obj_type=self.objtype,
602+
parent_props=self.props,
602603
)
603604
if not obj_type:
604605
# don't know how to document this member
@@ -795,7 +796,9 @@ def _best_object_type_for_member(
795796
member: Any,
796797
member_name: str,
797798
is_attr: bool,
798-
parent_documenter: Documenter,
799+
*,
800+
parent_obj_type: str,
801+
parent_props: _ItemProperties | None,
799802
) -> str | None:
800803
"""Return the best object type that supports documenting *member*."""
801804
filtered = []
@@ -818,42 +821,36 @@ def _best_object_type_for_member(
818821
# as NewType can be an attribute and is a class after Python 3.10.
819822
filtered.append((15, 'class'))
820823

821-
if isinstance(parent_documenter, ClassDocumenter):
824+
if parent_obj_type in {'class', 'exception'}:
822825
if inspect.isproperty(member):
823826
# priority must be higher than 'attribute'
824827
filtered.append((11, 'property'))
825828

826-
# Support for class properties. Note: these only work on Python 3.9.
827-
elif hasattr(parent_documenter, 'props'):
828-
# See FakeDirective &c in autosummary, parent might not be a
829-
# 'proper' Documenter.
830-
__dict__ = safe_getattr(parent_documenter.props._obj, '__dict__', {})
829+
# See _get_documenter() in autosummary, parent_props might be None.
830+
elif parent_props is not None:
831+
# Support for class properties. Note: these only work on Python 3.9.
832+
__dict__ = safe_getattr(parent_props._obj, '__dict__', {})
831833
obj = __dict__.get(member_name)
832834
if isinstance(obj, classmethod) and inspect.isproperty(obj.__func__):
833835
# priority must be higher than 'attribute'
834836
filtered.append((11, 'property'))
835837

836-
if not isinstance(parent_documenter, ModuleDocumenter):
838+
if parent_obj_type != 'module':
837839
if inspect.isattributedescriptor(member) or not (
838840
inspect.isroutine(member) or isinstance(member, type)
839841
):
840842
# priority must be higher than 'method', else it will recognise
841843
# some non-data descriptors as methods
842844
filtered.append((10, 'attribute'))
843845

844-
if inspect.isroutine(member) and not isinstance(
845-
parent_documenter, ModuleDocumenter
846-
):
846+
if inspect.isroutine(member) and parent_obj_type != 'module':
847847
# priority must be higher than 'function'
848848
filtered.append((1, 'method'))
849849

850850
if (
851851
inspect.isfunction(member)
852852
or inspect.isbuiltin(member)
853-
or (
854-
inspect.isroutine(member)
855-
and isinstance(parent_documenter, ModuleDocumenter)
856-
)
853+
or (inspect.isroutine(member) and parent_obj_type == 'module')
857854
):
858855
# supports functions, builtins and bound methods exported
859856
# at the module level
@@ -862,7 +859,7 @@ def _best_object_type_for_member(
862859
if isinstance(member, AnyTypeAliasType):
863860
filtered.append((0, 'type'))
864861

865-
if isinstance(parent_documenter, ModuleDocumenter) and is_attr:
862+
if parent_obj_type == 'module' and is_attr:
866863
filtered.append((-10, 'data'))
867864

868865
if filtered:

sphinx/ext/autosummary/__init__.py

Lines changed: 24 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -55,19 +55,16 @@
5555
import re
5656
import sys
5757
from inspect import Parameter
58-
from pathlib import Path
5958
from types import ModuleType
6059
from typing import TYPE_CHECKING, cast
6160

6261
from docutils import nodes
6362
from docutils.parsers.rst import directives
64-
from docutils.parsers.rst.states import RSTStateMachine, Struct, state_classes
63+
from docutils.parsers.rst.states import RSTStateMachine, state_classes
6564
from docutils.statemachine import StringList
6665

6766
import sphinx
6867
from sphinx import addnodes
69-
from sphinx.config import Config
70-
from sphinx.environment import BuildEnvironment
7168
from sphinx.errors import PycodeError
7269
from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions
7370
from sphinx.ext.autodoc._documenters import _best_object_type_for_member
@@ -76,9 +73,7 @@
7673
from sphinx.ext.autodoc.importer import _format_signatures, import_module
7774
from sphinx.ext.autodoc.mock import mock
7875
from sphinx.locale import __
79-
from sphinx.project import Project
8076
from sphinx.pycode import ModuleAnalyzer
81-
from sphinx.registry import SphinxComponentRegistry
8277
from sphinx.util import logging, rst
8378
from sphinx.util.docutils import (
8479
NullReporter,
@@ -98,8 +93,9 @@
9893
from docutils.nodes import Node, system_message
9994

10095
from sphinx.application import Sphinx
96+
from sphinx.environment import BuildEnvironment
10197
from sphinx.ext.autodoc import Documenter
102-
from sphinx.extension import Extension
98+
from sphinx.registry import SphinxComponentRegistry
10399
from sphinx.util.typing import ExtensionMetadata, OptionSpec
104100
from sphinx.writers.html5 import HTML5Translator
105101

@@ -158,31 +154,6 @@ def autosummary_table_visit_html(
158154
# -- autodoc integration -------------------------------------------------------
159155

160156

161-
class FakeApplication:
162-
verbosity = 0
163-
164-
def __init__(self) -> None:
165-
self.doctreedir = Path()
166-
self.events = None
167-
self.extensions: dict[str, Extension] = {}
168-
self.srcdir = Path()
169-
self.config = Config()
170-
self.project = Project('', {})
171-
self.registry = SphinxComponentRegistry()
172-
173-
174-
class FakeDirective(DocumenterBridge):
175-
def __init__(self) -> None:
176-
settings = Struct(tab_width=8)
177-
document = Struct(settings=settings)
178-
app = FakeApplication()
179-
app.config.add('autodoc_class_signature', 'mixed', 'env', ())
180-
env = BuildEnvironment(app) # type: ignore[arg-type]
181-
opts = _AutoDocumenterOptions()
182-
state = Struct(document=document)
183-
super().__init__(env, None, opts, 0, state)
184-
185-
186157
def get_documenter(app: Sphinx, obj: Any, parent: Any) -> type[Documenter]:
187158
"""Get an autodoc.Documenter class suitable for documenting the given
188159
object.
@@ -191,43 +162,36 @@ def get_documenter(app: Sphinx, obj: Any, parent: Any) -> type[Documenter]:
191162
another Python object (e.g. a module or a class) to which *obj*
192163
belongs to.
193164
"""
194-
return _get_documenter(obj, parent, registry=app.registry)
165+
obj_type = _get_documenter(obj, parent)
166+
return app.registry.documenters[obj_type]
195167

196168

197-
def _get_documenter(
198-
obj: Any, parent: Any, *, registry: SphinxComponentRegistry
199-
) -> type[Documenter]:
169+
def _get_documenter(obj: Any, parent: Any) -> str:
200170
"""Get an autodoc.Documenter class suitable for documenting the given
201171
object.
202172
203173
*obj* is the Python object to be documented, and *parent* is an
204174
another Python object (e.g. a module or a class) to which *obj*
205175
belongs to.
206176
"""
207-
from sphinx.ext.autodoc import DataDocumenter, ModuleDocumenter
208-
209177
if inspect.ismodule(obj):
210-
return ModuleDocumenter
211-
212-
# Construct a fake documenter for *parent*
213-
if parent is not None:
214-
parent_doc_cls = _get_documenter(parent, None, registry=registry)
215-
else:
216-
parent_doc_cls = ModuleDocumenter
178+
return 'module'
217179

218-
if hasattr(parent, '__name__'):
219-
parent_doc = parent_doc_cls(FakeDirective(), parent.__name__)
180+
if parent is None:
181+
parent_obj_type = 'module'
220182
else:
221-
parent_doc = parent_doc_cls(FakeDirective(), '')
183+
parent_obj_type = _get_documenter(parent, None)
222184

223185
# Get the correct documenter class for *obj*
224-
obj_type = _best_object_type_for_member(
225-
member=obj, member_name='', is_attr=False, parent_documenter=parent_doc
226-
)
227-
if obj_type:
228-
return registry.documenters[obj_type]
229-
else:
230-
return DataDocumenter
186+
if obj_type := _best_object_type_for_member(
187+
member=obj,
188+
member_name='',
189+
is_attr=False,
190+
parent_obj_type=parent_obj_type,
191+
parent_props=None,
192+
):
193+
return obj_type
194+
return 'data'
231195

232196

233197
# -- .. autosummary:: ----------------------------------------------------------
@@ -343,7 +307,8 @@ def create_documenter(
343307
344308
Wraps _get_documenter and is meant as a hook for extensions.
345309
"""
346-
doccls = _get_documenter(obj, parent, registry=registry)
310+
obj_type = _get_documenter(obj, parent)
311+
doccls = registry.documenters[obj_type]
347312
return doccls(self.bridge, full_name)
348313

349314
def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
@@ -389,16 +354,16 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
389354
continue
390355

391356
self.bridge.result = StringList() # initialize for each documenter
357+
obj_type = _get_documenter(obj, parent)
358+
doccls = self.env._registry.documenters[obj_type]
392359
full_name = real_name
393360
if not isinstance(obj, ModuleType):
394361
# give explicitly separated module name, so that members
395362
# of inner classes can be documented
396363
full_name = modname + '::' + full_name[len(modname) + 1 :]
397364
# NB. using full_name here is important, since Documenters
398365
# handle module prefixes slightly differently
399-
documenter = self.create_documenter(
400-
obj, parent, full_name, registry=self.env._registry
401-
)
366+
documenter = doccls(self.bridge, full_name)
402367
if documenter._load_object_by_name() is None:
403368
logger.warning(
404369
__('failed to import object %s'),

sphinx/ext/autosummary/generate.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ def __init__(
231231
self.object = obj
232232

233233
def get_object_type(self, name: str, value: Any) -> str:
234-
return _get_documenter(value, self.object, registry=self.registry).objtype
234+
return _get_documenter(value, self.object)
235235

236236
def is_skipped(self, name: str, value: Any, objtype: str) -> bool:
237237
try:
@@ -264,7 +264,7 @@ def scan(self, imported_members: bool) -> list[str]:
264264
except AttributeError:
265265
value = None
266266

267-
objtype = self.get_object_type(name, value)
267+
objtype = _get_documenter(value, self.object)
268268
if self.is_skipped(name, value, objtype):
269269
continue
270270

@@ -325,7 +325,8 @@ def generate_autosummary_content(
325325
events: EventManager,
326326
registry: SphinxComponentRegistry,
327327
) -> str:
328-
doc = _get_documenter(obj, parent, registry=registry)
328+
obj_type = _get_documenter(obj, parent)
329+
doc = registry.documenters[obj_type]
329330

330331
ns: dict[str, Any] = {}
331332
ns.update(context)
@@ -578,7 +579,8 @@ def _get_members(
578579

579580
all_members = _get_all_members(doc, obj, config=config)
580581
for name, value in all_members.items():
581-
documenter = _get_documenter(value, obj, registry=registry)
582+
obj_type = _get_documenter(value, obj)
583+
documenter = registry.documenters[obj_type]
582584
if documenter.objtype in types:
583585
# skip imported members if expected
584586
if imported or getattr(value, '__module__', None) == obj.__name__:

0 commit comments

Comments
 (0)