Merge branch '3.2.x' into 3.x

This commit is contained in:
Takeshi KOMIYA 2020-08-14 14:24:11 +09:00
commit b2d524d2b2
10 changed files with 124 additions and 49 deletions

View File

@ -38,12 +38,21 @@ Deprecated
Features added Features added
-------------- --------------
* #8095: napoleon: Add :confval:`napoleon_preprocess_types` to enable the type
preprocessor for numpy style docstrings
* #8114: C and C++, parse function attributes after parameters and qualifiers.
Bugs fixed Bugs fixed
---------- ----------
* #8074: napoleon: Crashes during processing C-ext module * #8074: napoleon: Crashes during processing C-ext module
* #8088: napoleon: "Inline literal start-string without end-string" warning in
Numpy style Parameters section
* #8084: autodoc: KeyError is raised on documenting an attribute of the broken * #8084: autodoc: KeyError is raised on documenting an attribute of the broken
class class
* #8091: autodoc: AttributeError is raised on documenting an attribute on Python
3.5.2
* C++, fix parsing of template template paramters, broken by the fix of #7944
Testing Testing
-------- --------

View File

@ -32,7 +32,7 @@ from sphinx.transforms import SphinxTransform
from sphinx.transforms.post_transforms import ReferencesResolver from sphinx.transforms.post_transforms import ReferencesResolver
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.cfamily import ( from sphinx.util.cfamily import (
NoOldIdError, ASTBaseBase, ASTBaseParenExprList, NoOldIdError, ASTBaseBase, ASTAttribute, ASTBaseParenExprList,
verify_description_mode, StringifyTransform, verify_description_mode, StringifyTransform,
BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral, BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral,
identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re, identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re,
@ -652,8 +652,9 @@ class ASTFunctionParameter(ASTBase):
class ASTParameters(ASTBase): class ASTParameters(ASTBase):
def __init__(self, args: List[ASTFunctionParameter]) -> None: def __init__(self, args: List[ASTFunctionParameter], attrs: List[ASTAttribute]) -> None:
self.args = args self.args = args
self.attrs = attrs
@property @property
def function_params(self) -> List[ASTFunctionParameter]: def function_params(self) -> List[ASTFunctionParameter]:
@ -669,6 +670,9 @@ class ASTParameters(ASTBase):
first = False first = False
res.append(str(a)) res.append(str(a))
res.append(')') res.append(')')
for attr in self.attrs:
res.append(' ')
res.append(transform(attr))
return ''.join(res) return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
@ -683,6 +687,9 @@ class ASTParameters(ASTBase):
arg.describe_signature(param, 'markType', env, symbol=symbol) arg.describe_signature(param, 'markType', env, symbol=symbol)
paramlist += param paramlist += param
signode += paramlist signode += paramlist
for attr in self.attrs:
signode += nodes.Text(' ')
attr.describe_signature(signode)
class ASTDeclSpecsSimple(ASTBaseBase): class ASTDeclSpecsSimple(ASTBaseBase):
@ -2572,7 +2579,15 @@ class DefinitionParser(BaseParser):
self.fail( self.fail(
'Expecting "," or ")" in parameters, ' 'Expecting "," or ")" in parameters, '
'got "%s".' % self.current_char) 'got "%s".' % self.current_char)
return ASTParameters(args)
attrs = []
while True:
attr = self._parse_attribute()
if attr is None:
break
attrs.append(attr)
return ASTParameters(args, attrs)
def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple: def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple:
"""Just parse the simple ones.""" """Just parse the simple ones."""

View File

@ -1879,7 +1879,8 @@ class ASTNoexceptSpec(ASTBase):
class ASTParametersQualifiers(ASTBase): class ASTParametersQualifiers(ASTBase):
def __init__(self, args: List[ASTFunctionParameter], volatile: bool, const: bool, def __init__(self, args: List[ASTFunctionParameter], volatile: bool, const: bool,
refQual: str, exceptionSpec: ASTNoexceptSpec, trailingReturn: "ASTType", refQual: str, exceptionSpec: ASTNoexceptSpec, trailingReturn: "ASTType",
override: bool, final: bool, initializer: str) -> None: override: bool, final: bool, attrs: List[ASTAttribute],
initializer: str) -> None:
self.args = args self.args = args
self.volatile = volatile self.volatile = volatile
self.const = const self.const = const
@ -1888,6 +1889,7 @@ class ASTParametersQualifiers(ASTBase):
self.trailingReturn = trailingReturn self.trailingReturn = trailingReturn
self.override = override self.override = override
self.final = final self.final = final
self.attrs = attrs
self.initializer = initializer self.initializer = initializer
@property @property
@ -1947,6 +1949,9 @@ class ASTParametersQualifiers(ASTBase):
res.append(' final') res.append(' final')
if self.override: if self.override:
res.append(' override') res.append(' override')
for attr in self.attrs:
res.append(' ')
res.append(transform(attr))
if self.initializer: if self.initializer:
res.append(' = ') res.append(' = ')
res.append(self.initializer) res.append(self.initializer)
@ -1988,6 +1993,9 @@ class ASTParametersQualifiers(ASTBase):
_add_anno(signode, 'final') _add_anno(signode, 'final')
if self.override: if self.override:
_add_anno(signode, 'override') _add_anno(signode, 'override')
for attr in self.attrs:
signode += nodes.Text(' ')
attr.describe_signature(signode)
if self.initializer: if self.initializer:
_add_text(signode, '= ' + str(self.initializer)) _add_text(signode, '= ' + str(self.initializer))
@ -5709,6 +5717,13 @@ class DefinitionParser(BaseParser):
override = self.skip_word_and_ws( override = self.skip_word_and_ws(
'override') # they can be permuted 'override') # they can be permuted
attrs = []
while True:
attr = self._parse_attribute()
if attr is None:
break
attrs.append(attr)
self.skip_ws() self.skip_ws()
initializer = None initializer = None
if self.skip_string('='): if self.skip_string('='):
@ -5725,7 +5740,7 @@ class DefinitionParser(BaseParser):
return ASTParametersQualifiers( return ASTParametersQualifiers(
args, volatile, const, refQual, exceptionSpec, trailingReturn, args, volatile, const, refQual, exceptionSpec, trailingReturn,
override, final, initializer) override, final, attrs, initializer)
def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple: def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple:
"""Just parse the simple ones.""" """Just parse the simple ones."""
@ -6251,6 +6266,7 @@ class DefinitionParser(BaseParser):
# ========================================================================== # ==========================================================================
def _parse_template_paramter(self) -> ASTTemplateParam: def _parse_template_paramter(self) -> ASTTemplateParam:
self.skip_ws()
if self.skip_word('template'): if self.skip_word('template'):
# declare a tenplate template parameter # declare a tenplate template parameter
nestedParams = self._parse_template_parameter_list() nestedParams = self._parse_template_parameter_list()

View File

@ -1613,6 +1613,9 @@ class DataDocumenter(ModuleLevelDocumenter):
except KeyError: except KeyError:
# a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
annotations = {} annotations = {}
except AttributeError:
# AttributeError is raised on 3.5.2 (fixed by 3.5.3)
annotations = {}
if self.objpath[-1] in annotations: if self.objpath[-1] in annotations:
objrepr = stringify_typehint(annotations.get(self.objpath[-1])) objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
@ -1986,6 +1989,9 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
except KeyError: except KeyError:
# a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
annotations = {} annotations = {}
except AttributeError:
# AttributeError is raised on 3.5.2 (fixed by 3.5.3)
annotations = {}
if self.objpath[-1] in annotations: if self.objpath[-1] in annotations:
objrepr = stringify_typehint(annotations.get(self.objpath[-1])) objrepr = stringify_typehint(annotations.get(self.objpath[-1]))

View File

@ -41,6 +41,7 @@ class Config:
napoleon_use_param = True napoleon_use_param = True
napoleon_use_rtype = True napoleon_use_rtype = True
napoleon_use_keyword = True napoleon_use_keyword = True
napoleon_preprocess_types = False
napoleon_type_aliases = None napoleon_type_aliases = None
napoleon_custom_sections = None napoleon_custom_sections = None
@ -237,9 +238,12 @@ class Config:
:returns: *bool* -- True if successful, False otherwise :returns: *bool* -- True if successful, False otherwise
napoleon_preprocess_types : :obj:`bool` (Defaults to False)
Enable the type preprocessor for numpy style docstrings.
napoleon_type_aliases : :obj:`dict` (Defaults to None) napoleon_type_aliases : :obj:`dict` (Defaults to None)
Add a mapping of strings to string, translating types in numpy Add a mapping of strings to string, translating types in numpy
style docstrings. style docstrings. Only works if ``napoleon_preprocess_types = True``.
napoleon_custom_sections : :obj:`list` (Defaults to None) napoleon_custom_sections : :obj:`list` (Defaults to None)
Add a list of custom sections to include, expanding the list of parsed sections. Add a list of custom sections to include, expanding the list of parsed sections.
@ -268,6 +272,7 @@ class Config:
'napoleon_use_param': (True, 'env'), 'napoleon_use_param': (True, 'env'),
'napoleon_use_rtype': (True, 'env'), 'napoleon_use_rtype': (True, 'env'),
'napoleon_use_keyword': (True, 'env'), 'napoleon_use_keyword': (True, 'env'),
'napoleon_preprocess_types': (False, 'env'),
'napoleon_type_aliases': (None, 'env'), 'napoleon_type_aliases': (None, 'env'),
'napoleon_custom_sections': (None, 'env') 'napoleon_custom_sections': (None, 'env')
} }

View File

@ -1104,11 +1104,12 @@ class NumpyDocstring(GoogleDocstring):
_name, _type = line, '' _name, _type = line, ''
_name, _type = _name.strip(), _type.strip() _name, _type = _name.strip(), _type.strip()
_name = self._escape_args_and_kwargs(_name) _name = self._escape_args_and_kwargs(_name)
_type = _convert_numpy_type_spec( if self._config.napoleon_preprocess_types:
_type, _type = _convert_numpy_type_spec(
location=self._get_location(), _type,
translations=self._config.napoleon_type_aliases or {}, location=self._get_location(),
) translations=self._config.napoleon_type_aliases or {},
)
if prefer_type and not _type: if prefer_type and not _type:
_type, _name = _name, _type _type, _name = _name, _type

View File

@ -391,7 +391,7 @@ class BaseParser:
% startPos) % startPos)
return self.definition[startPos:self.pos] return self.definition[startPos:self.pos]
def _parse_attribute(self) -> ASTAttribute: def _parse_attribute(self) -> Optional[ASTAttribute]:
self.skip_ws() self.skip_ws()
# try C++11 style # try C++11 style
startPos = self.pos startPos = self.pos

View File

@ -497,17 +497,16 @@ def test_attributes():
parse('member', 'paren_attr({]}) int f') parse('member', 'paren_attr({]}) int f')
# position: decl specs # position: decl specs
check('function', 'static inline __attribute__(()) void f()', check('function', 'static inline __attribute__(()) void f()', {1: 'f'},
{1: 'f'},
output='__attribute__(()) static inline void f()') output='__attribute__(()) static inline void f()')
check('function', '[[attr1]] [[attr2]] void f()', check('function', '[[attr1]] [[attr2]] void f()', {1: 'f'})
{1: 'f'},
output='[[attr1]] [[attr2]] void f()')
# position: declarator # position: declarator
check('member', 'int *[[attr]] i', {1: 'i'}) check('member', 'int *[[attr]] i', {1: 'i'})
check('member', 'int *const [[attr]] volatile i', {1: 'i'}, check('member', 'int *const [[attr]] volatile i', {1: 'i'},
output='int *[[attr]] volatile const i') output='int *[[attr]] volatile const i')
check('member', 'int *[[attr]] *i', {1: 'i'}) check('member', 'int *[[attr]] *i', {1: 'i'})
# position: parameters
check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f'})
# issue michaeljones/breathe#500 # issue michaeljones/breathe#500
check('function', 'LIGHTGBM_C_EXPORT int LGBM_BoosterFree(int handle)', check('function', 'LIGHTGBM_C_EXPORT int LGBM_BoosterFree(int handle)',

View File

@ -764,6 +764,7 @@ def test_templates():
check('class', "template<template<typename> typename> {key}A", {2: "II0E0E1A"}) check('class', "template<template<typename> typename> {key}A", {2: "II0E0E1A"})
check('class', "template<template<typename> typename ...T> {key}A", {2: "II0EDpE1A"}) check('class', "template<template<typename> typename ...T> {key}A", {2: "II0EDpE1A"})
check('class', "template<template<typename> typename...> {key}A", {2: "II0EDpE1A"}) check('class', "template<template<typename> typename...> {key}A", {2: "II0EDpE1A"})
check('class', "template<typename T, template<typename> typename...> {key}A", {2: "I0I0EDpE1A"})
check('class', "template<int> {key}A", {2: "I_iE1A"}) check('class', "template<int> {key}A", {2: "I_iE1A"})
check('class', "template<int T> {key}A", {2: "I_iE1A"}) check('class', "template<int T> {key}A", {2: "I_iE1A"})
@ -937,15 +938,15 @@ def test_attributes():
check('function', 'static inline __attribute__(()) void f()', check('function', 'static inline __attribute__(()) void f()',
{1: 'f', 2: '1fv'}, {1: 'f', 2: '1fv'},
output='__attribute__(()) static inline void f()') output='__attribute__(()) static inline void f()')
check('function', '[[attr1]] [[attr2]] void f()', check('function', '[[attr1]] [[attr2]] void f()', {1: 'f', 2: '1fv'})
{1: 'f', 2: '1fv'},
output='[[attr1]] [[attr2]] void f()')
# position: declarator # position: declarator
check('member', 'int *[[attr]] i', {1: 'i__iP', 2: '1i'}) check('member', 'int *[[attr]] i', {1: 'i__iP', 2: '1i'})
check('member', 'int *const [[attr]] volatile i', {1: 'i__iPVC', 2: '1i'}, check('member', 'int *const [[attr]] volatile i', {1: 'i__iPVC', 2: '1i'},
output='int *[[attr]] volatile const i') output='int *[[attr]] volatile const i')
check('member', 'int &[[attr]] i', {1: 'i__iR', 2: '1i'}) check('member', 'int &[[attr]] i', {1: 'i__iR', 2: '1i'})
check('member', 'int *[[attr]] *i', {1: 'i__iPP', 2: '1i'}) check('member', 'int *[[attr]] *i', {1: 'i__iPP', 2: '1i'})
# position: parameters and qualifiers
check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f', 2: '1fv'})
def test_xref_parsing(): def test_xref_parsing():

View File

@ -66,19 +66,19 @@ Sample namedtuple subclass
Quick description of attr1 Quick description of attr1
:type: :class:`Arbitrary type` :type: Arbitrary type
.. attribute:: attr2 .. attribute:: attr2
Quick description of attr2 Quick description of attr2
:type: :class:`Another arbitrary type` :type: Another arbitrary type
.. attribute:: attr3 .. attribute:: attr3
Adds a newline after the type Adds a newline after the type
:type: :class:`Type` :type: Type
""" """
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
@ -1311,12 +1311,34 @@ class NumpyDocstringTest(BaseDocstringTest):
config = Config( config = Config(
napoleon_use_param=False, napoleon_use_param=False,
napoleon_use_rtype=False, napoleon_use_rtype=False,
napoleon_use_keyword=False) napoleon_use_keyword=False,
napoleon_preprocess_types=True)
for docstring, expected in self.docstrings: for docstring, expected in self.docstrings:
actual = str(NumpyDocstring(dedent(docstring), config)) actual = str(NumpyDocstring(dedent(docstring), config))
expected = dedent(expected) expected = dedent(expected)
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
def test_type_preprocessor(self):
docstring = dedent("""
Single line summary
Parameters
----------
arg1:str
Extended
description of arg1
""")
config = Config(napoleon_preprocess_types=False, napoleon_use_param=False)
actual = str(NumpyDocstring(docstring, config))
expected = dedent("""
Single line summary
:Parameters: **arg1** (*str*) -- Extended
description of arg1
""")
self.assertEqual(expected, actual)
def test_parameters_with_class_reference(self): def test_parameters_with_class_reference(self):
docstring = """\ docstring = """\
Parameters Parameters
@ -1352,7 +1374,7 @@ x1, x2 : array_like
config = Config(napoleon_use_param=False) config = Config(napoleon_use_param=False)
actual = str(NumpyDocstring(docstring, config)) actual = str(NumpyDocstring(docstring, config))
expected = """\ expected = """\
:Parameters: **x1, x2** (:class:`array_like`) -- Input arrays, description of ``x1``, ``x2``. :Parameters: **x1, x2** (*array_like*) -- Input arrays, description of ``x1``, ``x2``.
""" """
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
@ -1360,9 +1382,9 @@ x1, x2 : array_like
actual = str(NumpyDocstring(dedent(docstring), config)) actual = str(NumpyDocstring(dedent(docstring), config))
expected = """\ expected = """\
:param x1: Input arrays, description of ``x1``, ``x2``. :param x1: Input arrays, description of ``x1``, ``x2``.
:type x1: :class:`array_like` :type x1: array_like
:param x2: Input arrays, description of ``x1``, ``x2``. :param x2: Input arrays, description of ``x1``, ``x2``.
:type x2: :class:`array_like` :type x2: array_like
""" """
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
@ -1377,7 +1399,7 @@ param1 : MyClass instance
config = Config(napoleon_use_param=False) config = Config(napoleon_use_param=False)
actual = str(NumpyDocstring(docstring, config)) actual = str(NumpyDocstring(docstring, config))
expected = """\ expected = """\
:Parameters: **param1** (:class:`MyClass instance`) :Parameters: **param1** (*MyClass instance*)
""" """
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
@ -1385,7 +1407,7 @@ param1 : MyClass instance
actual = str(NumpyDocstring(dedent(docstring), config)) actual = str(NumpyDocstring(dedent(docstring), config))
expected = """\ expected = """\
:param param1: :param param1:
:type param1: :class:`MyClass instance` :type param1: MyClass instance
""" """
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
@ -1474,7 +1496,7 @@ arg_ : type
expected = """ expected = """
:ivar arg_: some description :ivar arg_: some description
:vartype arg_: :class:`type` :vartype arg_: type
""" """
config = Config(napoleon_use_ivar=True) config = Config(napoleon_use_ivar=True)
@ -1494,7 +1516,7 @@ arg_ : type
expected = """ expected = """
:ivar arg\\_: some description :ivar arg\\_: some description
:vartype arg\\_: :class:`type` :vartype arg\\_: type
""" """
config = Config(napoleon_use_ivar=True) config = Config(napoleon_use_ivar=True)
@ -1862,59 +1884,59 @@ definition_after_normal_text : int
expected = """One line summary. expected = """One line summary.
:param no_list: :param no_list:
:type no_list: :class:`int` :type no_list: int
:param one_bullet_empty: :param one_bullet_empty:
* *
:type one_bullet_empty: :class:`int` :type one_bullet_empty: int
:param one_bullet_single_line: :param one_bullet_single_line:
- first line - first line
:type one_bullet_single_line: :class:`int` :type one_bullet_single_line: int
:param one_bullet_two_lines: :param one_bullet_two_lines:
+ first line + first line
continued continued
:type one_bullet_two_lines: :class:`int` :type one_bullet_two_lines: int
:param two_bullets_single_line: :param two_bullets_single_line:
- first line - first line
- second line - second line
:type two_bullets_single_line: :class:`int` :type two_bullets_single_line: int
:param two_bullets_two_lines: :param two_bullets_two_lines:
* first line * first line
continued continued
* second line * second line
continued continued
:type two_bullets_two_lines: :class:`int` :type two_bullets_two_lines: int
:param one_enumeration_single_line: :param one_enumeration_single_line:
1. first line 1. first line
:type one_enumeration_single_line: :class:`int` :type one_enumeration_single_line: int
:param one_enumeration_two_lines: :param one_enumeration_two_lines:
1) first line 1) first line
continued continued
:type one_enumeration_two_lines: :class:`int` :type one_enumeration_two_lines: int
:param two_enumerations_one_line: :param two_enumerations_one_line:
(iii) first line (iii) first line
(iv) second line (iv) second line
:type two_enumerations_one_line: :class:`int` :type two_enumerations_one_line: int
:param two_enumerations_two_lines: :param two_enumerations_two_lines:
a. first line a. first line
continued continued
b. second line b. second line
continued continued
:type two_enumerations_two_lines: :class:`int` :type two_enumerations_two_lines: int
:param one_definition_one_line: :param one_definition_one_line:
item 1 item 1
first line first line
:type one_definition_one_line: :class:`int` :type one_definition_one_line: int
:param one_definition_two_lines: :param one_definition_two_lines:
item 1 item 1
first line first line
continued continued
:type one_definition_two_lines: :class:`int` :type one_definition_two_lines: int
:param two_definitions_one_line: :param two_definitions_one_line:
item 1 item 1
first line first line
item 2 item 2
second line second line
:type two_definitions_one_line: :class:`int` :type two_definitions_one_line: int
:param two_definitions_two_lines: :param two_definitions_two_lines:
item 1 item 1
first line first line
@ -1922,14 +1944,14 @@ definition_after_normal_text : int
item 2 item 2
second line second line
continued continued
:type two_definitions_two_lines: :class:`int` :type two_definitions_two_lines: int
:param one_definition_blank_line: :param one_definition_blank_line:
item 1 item 1
first line first line
extra first line extra first line
:type one_definition_blank_line: :class:`int` :type one_definition_blank_line: int
:param two_definitions_blank_lines: :param two_definitions_blank_lines:
item 1 item 1
@ -1942,12 +1964,12 @@ definition_after_normal_text : int
second line second line
extra second line extra second line
:type two_definitions_blank_lines: :class:`int` :type two_definitions_blank_lines: int
:param definition_after_normal_text: text line :param definition_after_normal_text: text line
item 1 item 1
first line first line
:type definition_after_normal_text: :class:`int` :type definition_after_normal_text: int
""" """
config = Config(napoleon_use_param=True) config = Config(napoleon_use_param=True)
actual = str(NumpyDocstring(docstring, config)) actual = str(NumpyDocstring(docstring, config))
@ -2041,7 +2063,7 @@ definition_after_normal_text : int
item 1 item 1
first line first line
""" """
config = Config(napoleon_use_param=False) config = Config(napoleon_use_param=False, napoleon_preprocess_types=True)
actual = str(NumpyDocstring(docstring, config)) actual = str(NumpyDocstring(docstring, config))
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
@ -2222,6 +2244,7 @@ definition_after_normal_text : int
config = Config( config = Config(
napoleon_use_param=True, napoleon_use_param=True,
napoleon_use_rtype=True, napoleon_use_rtype=True,
napoleon_preprocess_types=True,
napoleon_type_aliases=translations, napoleon_type_aliases=translations,
) )
actual = str(NumpyDocstring(docstring, config)) actual = str(NumpyDocstring(docstring, config))