added napoleon_google_attr_annotations option to use PEP 526 on google style

This commit is contained in:
Quentin Soubeyran 2020-10-05 14:15:01 +02:00
parent c941b9cb14
commit f268665292
5 changed files with 117 additions and 3 deletions

View File

@ -294,3 +294,21 @@ class ExampleClass:
def _private_without_docstring(self):
pass
class ExamplePEP526Class:
"""The summary line for a class docstring should fit on one line.
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``
is True, types can be specified in the class body using ``PEP 526``
annotations.
Attributes:
attr1: Description of `attr1`.
attr2: Description of `attr2`.
"""
attr1: str
attr2: int

View File

@ -203,7 +203,8 @@ Type Annotations
This is an alternative to expressing types directly in docstrings.
One benefit of expressing types according to `PEP 484`_ is that
type checkers and IDEs can take advantage of them for static code
analysis.
analysis. `PEP 484`_ was then extended by `PEP 526`_ which introduced
a similar way to annotate variables (and attributes).
Google style with Python 3 type annotations::
@ -221,6 +222,19 @@ Google style with Python 3 type annotations::
"""
return True
class Class:
"""Summary line.
Extended description of class
Attributes:
attr1: Description of attr1
attr2: Description of attr2
"""
attr1: int
attr2: str
Google style with types in docstrings::
@ -238,6 +252,16 @@ Google style with types in docstrings::
"""
return True
class Class:
"""Summary line.
Extended description of class
Attributes:
attr1 (int): Description of attr1
attr2 (str): Description of attr2
"""
.. Note::
`Python 2/3 compatible annotations`_ aren't currently
@ -246,6 +270,9 @@ Google style with types in docstrings::
.. _PEP 484:
https://www.python.org/dev/peps/pep-0484/
.. _PEP 526:
https://www.python.org/dev/peps/pep-0526/
.. _Python 2/3 compatible annotations:
https://www.python.org/dev/peps/pep-0484/#suggested-syntax-for-python-2-7-and-straddling-code
@ -275,6 +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
.. _Google style:
https://google.github.io/styleguide/pyguide.html
@ -511,3 +539,9 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
:type arg2: :term:`dict-like <mapping>`
.. versionadded:: 3.2
.. confval:: napoleon_google_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.

View File

@ -44,6 +44,7 @@ class Config:
napoleon_preprocess_types = False
napoleon_type_aliases = None
napoleon_custom_sections = None
napoleon_google_attr_annotations = False
.. _Google style:
https://google.github.io/styleguide/pyguide.html
@ -257,6 +258,9 @@ 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)
Use the type annotations of class attributes that are documented in the docstring
but do not have a type in the docstring.
"""
_config_values = {
@ -274,7 +278,8 @@ class Config:
'napoleon_use_keyword': (True, 'env'),
'napoleon_preprocess_types': (False, 'env'),
'napoleon_type_aliases': (None, 'env'),
'napoleon_custom_sections': (None, 'env')
'napoleon_custom_sections': (None, 'env'),
'napoleon_google_attr_annotations': (False, 'env'),
}
def __init__(self, **settings: Any) -> None:

View File

@ -14,13 +14,14 @@ import collections
import inspect
import re
from functools import partial
from typing import Any, Callable, Dict, List, Tuple, Union
from typing import Any, Callable, Dict, List, Tuple, Union, get_type_hints
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
if False:
# For type annotation
@ -600,6 +601,26 @@ 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 == 'class' and self._obj:
if self._config.napoleon_google_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 = {}
if _name in self._annotations:
_type = stringify_annotation(self._annotations[_name])
if self._config.napoleon_use_ivar:
_name = self._qualify_name(_name, self._obj)
field = ':ivar %s: ' % _name

View File

@ -45,6 +45,18 @@ class NamedtupleSubclass(namedtuple('NamedtupleSubclass', ('attr1', 'attr2'))):
return super().__new__(cls, attr1, attr2)
class PEP526Class:
"""Sample class with PEP 526 annotations
Attributes:
attr1: Attr1 description.
attr2: Attr2 description.
"""
attr1: int
attr2: str
class BaseDocstringTest(TestCase):
pass
@ -1091,6 +1103,30 @@ Do as you please
:kwtype gotham_is_yours: None
"""
self.assertEqual(expected, actual)
def test_pep_526_annotations(self):
config = Config(
napoleon_google_attr_annotations=True
)
actual = str(GoogleDocstring(cleandoc(PEP526Class.__doc__), config, app=None, what="class",
obj=PEP526Class))
print(actual)
expected = """\
Sample class with PEP 526 annotations
.. attribute:: attr1
Attr1 description.
:type: int
.. attribute:: attr2
Attr2 description.
:type: str
"""
self.assertEqual(expected, actual)
class NumpyDocstringTest(BaseDocstringTest):