refactored code and tests, prepared for numpy feature

This commit is contained in:
Quentin Soubeyran 2020-11-18 11:18:52 +01:00
parent a60e1c10b7
commit ec30f77712
9 changed files with 101 additions and 44 deletions

View File

@ -300,7 +300,7 @@ class ExamplePEP526Class:
If the class has public attributes, they may be documented here
in an ``Attributes`` section and follow the same formatting as a
function's ``Args`` section. If ``napoleon_google_attr_annotations``
function's ``Args`` section. If ``napoleon_attr_annotations``
is True, types can be specified in the class body using ``PEP 526``
annotations.

View File

@ -302,7 +302,7 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
napoleon_use_param = True
napoleon_use_rtype = True
napoleon_type_aliases = None
napoleon_google_attr_annotations = False
napoleon_attr_annotations = True
.. _Google style:
https://google.github.io/styleguide/pyguide.html
@ -540,8 +540,10 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
.. versionadded:: 3.2
.. confval:: napoleon_google_attr_annotations
.. confval:: napoleon_attr_annotations
True to allow using `PEP 526`_ attributes annotations in classes.
If an attribute is documented in the docstring without a type and
has an annotation in the class body, that type is used.
True to allow using `PEP 526`_ attributes annotations in classes.
If an attribute is documented in the docstring without a type and
has an annotation in the class body, that type is used.
.. versionadded:: 3.4

View File

@ -44,7 +44,7 @@ class Config:
napoleon_preprocess_types = False
napoleon_type_aliases = None
napoleon_custom_sections = None
napoleon_google_attr_annotations = False
napoleon_attr_annotations = False
.. _Google style:
https://google.github.io/styleguide/pyguide.html
@ -258,7 +258,7 @@ class Config:
section. If the entry is a tuple/list/indexed container, the first entry
is the name of the section, the second is the section key to emulate.
napoleon_google_attr_annotations : :obj:`bool` (Defaults to False)
napoleon_attr_annotations : :obj:`bool` (Defaults to True)
Use the type annotations of class attributes that are documented in the docstring
but do not have a type in the docstring.
@ -279,7 +279,7 @@ class Config:
'napoleon_preprocess_types': (False, 'env'),
'napoleon_type_aliases': (None, 'env'),
'napoleon_custom_sections': (None, 'env'),
'napoleon_google_attr_annotations': (False, 'env'),
'napoleon_attr_annotations': (True, 'env'),
}
def __init__(self, **settings: Any) -> None:

View File

@ -14,14 +14,15 @@ import collections
import inspect
import re
from functools import partial
from typing import Any, Callable, Dict, List, Tuple, Union, get_type_hints
from typing import Any, Callable, Dict, List, Tuple, Union
from sphinx.application import Sphinx
from sphinx.config import Config as SphinxConfig
from sphinx.ext.napoleon.iterators import modify_iter
from sphinx.locale import _, __
from sphinx.util import logging
from sphinx.util.inspect import safe_getattr, stringify_annotation
from sphinx.util.inspect import stringify_annotation
from sphinx.util.typing import get_type_hints
if False:
# For type annotation
@ -601,25 +602,15 @@ class GoogleDocstring:
def _parse_attributes_section(self, section: str) -> List[str]:
lines = []
for _name, _type, _desc in self._consume_fields():
# code adapted from autodoc.AttributeDocumenter:add_directive_header
if not _type and self._what in ('class', 'exception') and self._obj:
if self._config.napoleon_google_attr_annotations:
if self._config.napoleon_attr_annotations:
# cache the class annotations
if not hasattr(self, "_annotations"):
try:
self._annotations = get_type_hints(self._obj)
except NameError:
# Failed to evaluate ForwardRef (maybe TYPE_CHECKING)
self._annotations = safe_getattr(self._obj, '__annotations__', {})
except TypeError:
self._annotations = {}
except KeyError:
# a broken class found
# (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
self._annotations = {}
except AttributeError:
# AttributeError is raised on 3.5.2 (fixed by 3.5.3)
self._annotations = {}
localns = getattr(self._config, "autodoc_type_aliases", {}) or {}
localns.update(getattr(
self._config, "napoleon_type_aliases", {}
) or {})
self._annotations = get_type_hints(self._obj, None, localns)
if _name in self._annotations:
_type = stringify_annotation(self._annotations[_name])
if self._config.napoleon_use_ivar:

View File

@ -66,7 +66,7 @@ def get_type_hints(obj: Any, globalns: Dict = None, localns: Dict = None) -> Dic
from sphinx.util.inspect import safe_getattr # lazy loading
try:
return typing.get_type_hints(obj, None, localns)
return typing.get_type_hints(obj, globalns, localns)
except NameError:
# Failed to evaluate ForwardRef (maybe TYPE_CHECKING)
return safe_getattr(obj, '__annotations__', {})

View File

@ -1,10 +0,0 @@
class PEP526Class:
"""Sample class with PEP 526 annotations
Attributes:
attr1: Attr1 description.
attr2: Attr2 description.
"""
attr1: int
attr2: str

View File

@ -0,0 +1,18 @@
"""
Test module for napoleon PEP 526 compatiblity with google style
"""
module_level_var: int = 99
"""This is an example module level variable"""
class PEP526GoogleClass:
"""Sample class with PEP 526 annotations and google docstring
Attributes:
attr1: Attr1 description.
attr2: Attr2 description.
"""
attr1: int
attr2: str

View File

@ -0,0 +1,22 @@
"""
Test module for napoleon PEP 526 compatiblity with numpy style
"""
module_level_var: int = 99
"""This is an example module level variable"""
class PEP526NumpyClass:
"""
Sample class with PEP 526 annotations and numpy docstring
Attributes
----------
attr 1:
Attr1 description
attr 2:
Attr2 description
"""
attr1: int
attr2: str

View File

@ -25,7 +25,11 @@ from sphinx.ext.napoleon.docstring import (GoogleDocstring, NumpyDocstring,
_token_type, _tokenize_type_spec)
if sys.version_info >= (3, 6):
from ext_napoleon_docstring_data import PEP526Class
# TODO: enable imports when used
# import ext_napoleon_pep526_data_google
# import ext_napoleon_pep526_data_numpy
from ext_napoleon_pep526_data_google import PEP526GoogleClass
from ext_napoleon_pep526_data_numpy import PEP526NumpyClass
class NamedtupleSubclass(namedtuple('NamedtupleSubclass', ('attr1', 'attr2'))):
@ -1095,16 +1099,17 @@ Do as you please
:kwtype gotham_is_yours: None
"""
self.assertEqual(expected, actual)
def test_pep_526_annotations(self):
if sys.version_info >= (3, 6):
# Test class attributes annotations
config = Config(
napoleon_google_attr_annotations=True
napoleon_attr_annotations=True
)
actual = str(GoogleDocstring(cleandoc(PEP526Class.__doc__), config, app=None, what="class",
obj=PEP526Class))
actual = str(GoogleDocstring(cleandoc(PEP526GoogleClass.__doc__), config, app=None, what="class",
obj=PEP526GoogleClass))
expected = """\
Sample class with PEP 526 annotations
Sample class with PEP 526 annotations and google docstring
.. attribute:: attr1
@ -1120,6 +1125,9 @@ Sample class with PEP 526 annotations
"""
self.assertEqual(expected, actual)
# test module-level variables documentation
# TODO: use the ext_napoleon_pep526_data_google.module_level_var for that
class NumpyDocstringTest(BaseDocstringTest):
docstrings = [(
@ -2430,3 +2438,29 @@ class TestNumpyDocstring:
actual = numpy_docstring._escape_args_and_kwargs(name)
assert actual == expected
def test_pep_526_annotations(self):
if sys.version_info >= (3, 6):
# test class attributes annotations
# TODO: change this test after implementation in Numpy doc style
config = Config(
napoleon_attr_annotations=True
)
actual = str(NumpyDocstring(cleandoc(PEP526NumpyClass.__doc__), config, app=None, what="class",
obj=PEP526NumpyClass))
expected = """\
Sample class with PEP 526 annotations and numpy docstring
.. attribute:: attr 1
Attr1 description
.. attribute:: attr 2
Attr2 description
"""
print(actual)
assert expected == actual
# test module-level variables documentation
# TODO: use the ext_napoleon_pep526_data_google.module_level_var for that