mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #8481 from tk0miya/8480_slots_attributes
Fix #8480: autoattribute could not create document for __slots__ attributes
This commit is contained in:
commit
c941b9cb14
3
CHANGES
3
CHANGES
@ -16,6 +16,7 @@ Deprecated
|
|||||||
* The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``
|
* The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``
|
||||||
* ``sphinx.ext.autodoc.Documenter.get_object_members()``
|
* ``sphinx.ext.autodoc.Documenter.get_object_members()``
|
||||||
* ``sphinx.ext.autodoc.DataDeclarationDocumenter``
|
* ``sphinx.ext.autodoc.DataDeclarationDocumenter``
|
||||||
|
* ``sphinx.ext.autodoc.SlotsAttributeDocumenter``
|
||||||
* ``sphinx.ext.autodoc.TypeVarDocumenter``
|
* ``sphinx.ext.autodoc.TypeVarDocumenter``
|
||||||
* ``sphinx.ext.autodoc.importer._getannotations()``
|
* ``sphinx.ext.autodoc.importer._getannotations()``
|
||||||
* ``sphinx.pycode.ModuleAnalyzer.parse()``
|
* ``sphinx.pycode.ModuleAnalyzer.parse()``
|
||||||
@ -52,6 +53,8 @@ Bugs fixed
|
|||||||
type annotated variables
|
type annotated variables
|
||||||
* #8443: autodoc: autoattribute directive can't create document for PEP-526
|
* #8443: autodoc: autoattribute directive can't create document for PEP-526
|
||||||
based uninitalized variables
|
based uninitalized variables
|
||||||
|
* #8480: autodoc: autoattribute could not create document for __slots__
|
||||||
|
attributes
|
||||||
* #8452: autodoc: autodoc_type_aliases doesn't work when autodoc_typehints is
|
* #8452: autodoc: autodoc_type_aliases doesn't work when autodoc_typehints is
|
||||||
set to "description"
|
set to "description"
|
||||||
* #8460: autodoc: autodata and autoattribute directives do not display type
|
* #8460: autodoc: autodata and autoattribute directives do not display type
|
||||||
|
@ -41,6 +41,11 @@ The following is a list of deprecated interfaces.
|
|||||||
- 5.0
|
- 5.0
|
||||||
- ``sphinx.ext.autodoc.DataDocumenter``
|
- ``sphinx.ext.autodoc.DataDocumenter``
|
||||||
|
|
||||||
|
* - ``sphinx.ext.autodoc.SlotsAttributeDocumenter``
|
||||||
|
- 3.4
|
||||||
|
- 5.0
|
||||||
|
- ``sphinx.ext.autodoc.AttributeDocumenter``
|
||||||
|
|
||||||
* - ``sphinx.ext.autodoc.TypeVarDocumenter``
|
* - ``sphinx.ext.autodoc.TypeVarDocumenter``
|
||||||
- 3.4
|
- 3.4
|
||||||
- 5.0
|
- 5.0
|
||||||
|
@ -1702,7 +1702,9 @@ class ExceptionDocumenter(ClassDocumenter):
|
|||||||
|
|
||||||
class DataDocumenterMixinBase:
|
class DataDocumenterMixinBase:
|
||||||
# define types of instance variables
|
# define types of instance variables
|
||||||
|
parent = None # type: Any
|
||||||
object = None # type: Any
|
object = None # type: Any
|
||||||
|
objpath = None # type: List[str]
|
||||||
|
|
||||||
def should_suppress_directive_header(self) -> bool:
|
def should_suppress_directive_header(self) -> bool:
|
||||||
"""Check directive header should be suppressed."""
|
"""Check directive header should be suppressed."""
|
||||||
@ -2097,7 +2099,54 @@ class SingledispatchMethodDocumenter(MethodDocumenter):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AttributeDocumenter(NewTypeMixin, TypeVarMixin, # type: ignore
|
class SlotsMixin(DataDocumenterMixinBase):
|
||||||
|
"""
|
||||||
|
Mixin for AttributeDocumenter to provide the feature for supporting __slots__.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def isslotsattribute(self) -> bool:
|
||||||
|
"""Check the subject is an attribute in __slots__."""
|
||||||
|
try:
|
||||||
|
__slots__ = inspect.getslots(self.parent)
|
||||||
|
if __slots__ and self.objpath[-1] in __slots__:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def import_object(self, raiseerror: bool = False) -> bool:
|
||||||
|
ret = super().import_object(raiseerror) # type: ignore
|
||||||
|
if self.isslotsattribute():
|
||||||
|
self.object = SLOTSATTR
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def should_suppress_directive_header(self) -> bool:
|
||||||
|
if self.object is SLOTSATTR:
|
||||||
|
self._datadescriptor = True
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return super().should_suppress_directive_header()
|
||||||
|
|
||||||
|
def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
|
||||||
|
if self.object is SLOTSATTR:
|
||||||
|
try:
|
||||||
|
__slots__ = inspect.getslots(self.parent)
|
||||||
|
if __slots__ and __slots__.get(self.objpath[-1]):
|
||||||
|
docstring = prepare_docstring(__slots__[self.objpath[-1]])
|
||||||
|
return [docstring]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
except (AttributeError, ValueError) as exc:
|
||||||
|
logger.warning(__('Invalid __slots__ found on %s. Ignored.'),
|
||||||
|
(self.parent.__qualname__, exc), type='autodoc')
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return super().get_doc(encoding, ignore) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class AttributeDocumenter(NewTypeMixin, SlotsMixin, TypeVarMixin, # type: ignore
|
||||||
DocstringStripSignatureMixin, ClassLevelDocumenter):
|
DocstringStripSignatureMixin, ClassLevelDocumenter):
|
||||||
"""
|
"""
|
||||||
Specialized Documenter subclass for attributes.
|
Specialized Documenter subclass for attributes.
|
||||||
@ -2333,52 +2382,10 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
|
|||||||
# must be higher than AttributeDocumenter
|
# must be higher than AttributeDocumenter
|
||||||
priority = 11
|
priority = 11
|
||||||
|
|
||||||
@classmethod
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
|
warnings.warn("%s is deprecated." % self.__class__.__name__,
|
||||||
) -> bool:
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
"""This documents only SLOTSATTR members."""
|
super().__init__(*args, **kwargs)
|
||||||
return member is SLOTSATTR
|
|
||||||
|
|
||||||
def import_object(self, raiseerror: bool = False) -> bool:
|
|
||||||
"""Never import anything."""
|
|
||||||
# disguise as an attribute
|
|
||||||
self.objtype = 'attribute'
|
|
||||||
self._datadescriptor = True
|
|
||||||
|
|
||||||
with mock(self.config.autodoc_mock_imports):
|
|
||||||
try:
|
|
||||||
ret = import_object(self.modname, self.objpath[:-1], 'class',
|
|
||||||
attrgetter=self.get_attr,
|
|
||||||
warningiserror=self.config.autodoc_warningiserror)
|
|
||||||
self.module, _, _, self.parent = ret
|
|
||||||
return True
|
|
||||||
except ImportError as exc:
|
|
||||||
if raiseerror:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
logger.warning(exc.args[0], type='autodoc', subtype='import_object')
|
|
||||||
self.env.note_reread()
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
|
|
||||||
"""Decode and return lines of the docstring(s) for the object."""
|
|
||||||
if ignore is not None:
|
|
||||||
warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated."
|
|
||||||
% self.__class__.__name__,
|
|
||||||
RemovedInSphinx50Warning, stacklevel=2)
|
|
||||||
name = self.objpath[-1]
|
|
||||||
|
|
||||||
try:
|
|
||||||
__slots__ = inspect.getslots(self.parent)
|
|
||||||
if __slots__ and isinstance(__slots__.get(name, None), str):
|
|
||||||
docstring = prepare_docstring(__slots__[name])
|
|
||||||
return [docstring]
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
except (AttributeError, ValueError) as exc:
|
|
||||||
logger.warning(__('Invalid __slots__ found on %s. Ignored.'),
|
|
||||||
(self.parent.__qualname__, exc), type='autodoc')
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
class NewTypeAttributeDocumenter(AttributeDocumenter):
|
class NewTypeAttributeDocumenter(AttributeDocumenter):
|
||||||
@ -2435,7 +2442,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_autodocumenter(AttributeDocumenter)
|
app.add_autodocumenter(AttributeDocumenter)
|
||||||
app.add_autodocumenter(PropertyDocumenter)
|
app.add_autodocumenter(PropertyDocumenter)
|
||||||
app.add_autodocumenter(InstanceAttributeDocumenter)
|
app.add_autodocumenter(InstanceAttributeDocumenter)
|
||||||
app.add_autodocumenter(SlotsAttributeDocumenter)
|
|
||||||
app.add_autodocumenter(NewTypeAttributeDocumenter)
|
app.add_autodocumenter(NewTypeAttributeDocumenter)
|
||||||
|
|
||||||
app.add_config_value('autoclass_content', 'class', True, ENUM('both', 'class', 'init'))
|
app.add_config_value('autoclass_content', 'class', True, ENUM('both', 'class', 'init'))
|
||||||
|
@ -91,13 +91,13 @@ def setup_documenters(app: Any) -> None:
|
|||||||
InstanceAttributeDocumenter, MethodDocumenter,
|
InstanceAttributeDocumenter, MethodDocumenter,
|
||||||
ModuleDocumenter, NewTypeAttributeDocumenter,
|
ModuleDocumenter, NewTypeAttributeDocumenter,
|
||||||
NewTypeDataDocumenter, PropertyDocumenter,
|
NewTypeDataDocumenter, PropertyDocumenter,
|
||||||
SingledispatchFunctionDocumenter, SlotsAttributeDocumenter)
|
SingledispatchFunctionDocumenter)
|
||||||
documenters = [
|
documenters = [
|
||||||
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
|
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
|
||||||
FunctionDocumenter, MethodDocumenter, NewTypeAttributeDocumenter,
|
FunctionDocumenter, MethodDocumenter, NewTypeAttributeDocumenter,
|
||||||
NewTypeDataDocumenter, AttributeDocumenter, InstanceAttributeDocumenter,
|
NewTypeDataDocumenter, AttributeDocumenter, InstanceAttributeDocumenter,
|
||||||
DecoratorDocumenter, PropertyDocumenter, SlotsAttributeDocumenter,
|
DecoratorDocumenter, PropertyDocumenter, GenericAliasDocumenter,
|
||||||
GenericAliasDocumenter, SingledispatchFunctionDocumenter,
|
SingledispatchFunctionDocumenter,
|
||||||
] # type: List[Type[Documenter]]
|
] # type: List[Type[Documenter]]
|
||||||
for documenter in documenters:
|
for documenter in documenters:
|
||||||
app.registry.add_documenter(documenter.objtype, documenter)
|
app.registry.add_documenter(documenter.objtype, documenter)
|
||||||
|
@ -72,6 +72,41 @@ def test_autoattribute_instance_variable(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_autoattribute_slots_variable_list(app):
|
||||||
|
actual = do_autodoc(app, 'attribute', 'target.slots.Foo.attr')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:attribute:: Foo.attr',
|
||||||
|
' :module: target.slots',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_autoattribute_slots_variable_dict(app):
|
||||||
|
actual = do_autodoc(app, 'attribute', 'target.slots.Bar.attr1')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:attribute:: Bar.attr1',
|
||||||
|
' :module: target.slots',
|
||||||
|
'',
|
||||||
|
' docstring of attr1',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_autoattribute_slots_variable_str(app):
|
||||||
|
actual = do_autodoc(app, 'attribute', 'target.slots.Baz.attr')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:attribute:: Baz.attr',
|
||||||
|
' :module: target.slots',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
def test_autoattribute_NewType(app):
|
def test_autoattribute_NewType(app):
|
||||||
actual = do_autodoc(app, 'attribute', 'target.typevar.Class.T6')
|
actual = do_autodoc(app, 'attribute', 'target.typevar.Class.T6')
|
||||||
|
Loading…
Reference in New Issue
Block a user