diff options
author | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2020-12-17 00:46:49 +0900 |
---|---|---|
committer | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2020-12-17 01:07:54 +0900 |
commit | 55c110f60965ea6f0ea32d129b62413f1f84b3f4 (patch) | |
tree | e3065e10b0db4b3749096b6e59433ad8e1565559 | |
parent | 36e684bf83d14e587f0bf48eb0980f41ab66733e (diff) | |
download | sphinx-git-55c110f60965ea6f0ea32d129b62413f1f84b3f4.tar.gz |
Fix #8541: autodoc_type_aliases doesn't work for the instance attrs
So far, autodoc obtains type annotations of instance attributes by
ModuleAnalyzer directly. As a result, autodoc_type_aliases are ignored
for these variables.
This goes to merge type annotations from the class itself and
ModuleAnalyzer's, and get type annotations using `typing.get_type_hints()`
to apply autodoc_type_aliases.
-rw-r--r-- | CHANGES | 2 | ||||
-rw-r--r-- | sphinx/ext/autodoc/__init__.py | 58 | ||||
-rw-r--r-- | tests/roots/test-ext-autodoc/target/annotations.py | 8 | ||||
-rw-r--r-- | tests/test_ext_autodoc.py | 16 | ||||
-rw-r--r-- | tests/test_ext_autodoc_configs.py | 34 |
5 files changed, 100 insertions, 18 deletions
@@ -71,6 +71,8 @@ Bugs fixed attribute in alias class * #8452: autodoc: autodoc_type_aliases doesn't work when autodoc_typehints is set to "description" +* #8541: autodoc: autodoc_type_aliases doesn't work for the type annotation to + instance attributes * #8460: autodoc: autodata and autoattribute directives do not display type information of TypeVars * #8493: autodoc: references to builtins not working in class aliases diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 267adb0bb..8f6136bf3 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1843,6 +1843,26 @@ class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin, ) -> bool: return isinstance(parent, ModuleDocumenter) and isattr + def update_annotations(self, parent: Any) -> None: + """Update __annotations__ to support type_comment and so on.""" + try: + annotations = inspect.getannotations(parent) + + analyzer = ModuleAnalyzer.for_module(self.modname) + analyzer.analyze() + for (classname, attrname) in analyzer.annotations: + if classname == '' and attrname not in annotations: + annotations[attrname] = analyzer.annotations[classname, attrname] # type: ignore # NOQA + except AttributeError: + pass + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) + if self.parent: + self.update_annotations(self.parent) + + return ret + def add_directive_header(self, sig: str) -> None: super().add_directive_header(sig) sourcename = self.get_sourcename() @@ -1857,11 +1877,6 @@ class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin, if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) self.add_line(' :type: ' + objrepr, sourcename) - else: - key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) - if self.analyzer and key in self.analyzer.annotations: - self.add_line(' :type: ' + self.analyzer.annotations[key], - sourcename) try: if self.options.no_value or self.should_suppress_value_header(): @@ -2259,6 +2274,26 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: return False + def update_annotations(self, parent: Any) -> None: + """Update __annotations__ to support type_comment and so on.""" + try: + annotations = inspect.getannotations(parent) + + for cls in inspect.getmro(parent): + try: + module = safe_getattr(cls, '__module__') + qualname = safe_getattr(cls, '__qualname__') + + analyzer = ModuleAnalyzer.for_module(module) + analyzer.analyze() + for (classname, attrname) in analyzer.annotations: + if classname == qualname and attrname not in annotations: + annotations[attrname] = analyzer.annotations[classname, attrname] # type: ignore # NOQA + except (AttributeError, PycodeError): + pass + except AttributeError: + pass + def import_object(self, raiseerror: bool = False) -> bool: try: ret = super().import_object(raiseerror=True) @@ -2275,6 +2310,9 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: self.env.note_reread() ret = False + if self.parent: + self.update_annotations(self.parent) + return ret def get_real_modname(self) -> str: @@ -2294,16 +2332,6 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) self.add_line(' :type: ' + objrepr, sourcename) - else: - try: - qualname = safe_getattr(self.parent, '__qualname__', - '.'.join(self.objpath[:-1])) - key = (qualname, self.objpath[-1]) - if self.analyzer and key in self.analyzer.annotations: - self.add_line(' :type: ' + self.analyzer.annotations[key], - sourcename) - except AttributeError: - pass try: if (self.object is INSTANCEATTR or self.options.no_value or diff --git a/tests/roots/test-ext-autodoc/target/annotations.py b/tests/roots/test-ext-autodoc/target/annotations.py index e9ff2f604..ef600e2af 100644 --- a/tests/roots/test-ext-autodoc/target/annotations.py +++ b/tests/roots/test-ext-autodoc/target/annotations.py @@ -7,6 +7,9 @@ myint = int #: docstring variable: myint +#: docstring +variable2 = None # type: myint + def sum(x: myint, y: myint) -> myint: """docstring""" @@ -32,4 +35,7 @@ class Foo: """docstring""" #: docstring - attr: myint + attr1: myint + + def __init__(self): + self.attr2: myint = None #: docstring diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 52f69dd2e..a81a73b61 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -1675,9 +1675,25 @@ def test_autodoc_typed_inherited_instance_variables(app): '', ' .. py:attribute:: Derived.attr3', ' :module: target.typed_vars', + ' :type: int', ' :value: 0', '', '', + ' .. py:attribute:: Derived.attr4', + ' :module: target.typed_vars', + ' :type: int', + '', + '', + ' .. py:attribute:: Derived.attr5', + ' :module: target.typed_vars', + ' :type: int', + '', + '', + ' .. py:attribute:: Derived.attr6', + ' :module: target.typed_vars', + ' :type: int', + '', + '', ' .. py:attribute:: Derived.attr7', ' :module: target.typed_vars', ' :type: int', diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index d76ed25e3..ac0a2c11c 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -707,7 +707,14 @@ def test_autodoc_type_aliases(app): ' docstring', '', '', - ' .. py:attribute:: Foo.attr', + ' .. py:attribute:: Foo.attr1', + ' :module: target.annotations', + ' :type: int', + '', + ' docstring', + '', + '', + ' .. py:attribute:: Foo.attr2', ' :module: target.annotations', ' :type: int', '', @@ -733,6 +740,14 @@ def test_autodoc_type_aliases(app): '', ' docstring', '', + '', + '.. py:data:: variable2', + ' :module: target.annotations', + ' :type: int', + ' :value: None', + '', + ' docstring', + '', ] # define aliases @@ -749,7 +764,14 @@ def test_autodoc_type_aliases(app): ' docstring', '', '', - ' .. py:attribute:: Foo.attr', + ' .. py:attribute:: Foo.attr1', + ' :module: target.annotations', + ' :type: myint', + '', + ' docstring', + '', + '', + ' .. py:attribute:: Foo.attr2', ' :module: target.annotations', ' :type: myint', '', @@ -775,6 +797,14 @@ def test_autodoc_type_aliases(app): '', ' docstring', '', + '', + '.. py:data:: variable2', + ' :module: target.annotations', + ' :type: myint', + ' :value: None', + '', + ' docstring', + '', ] |