mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
2909 lines
72 KiB
Python
2909 lines
72 KiB
Python
"""Tests for :mod:`sphinx.ext.napoleon.docstring` module."""
|
|
|
|
import re
|
|
import zlib
|
|
from collections import namedtuple
|
|
from inspect import cleandoc
|
|
from itertools import product
|
|
from textwrap import dedent
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
from sphinx.ext.intersphinx import load_mappings, validate_intersphinx_mapping
|
|
from sphinx.ext.napoleon import Config
|
|
from sphinx.ext.napoleon.docstring import (
|
|
GoogleDocstring,
|
|
NumpyDocstring,
|
|
_convert_numpy_type_spec,
|
|
_recombine_set_tokens,
|
|
_token_type,
|
|
_tokenize_type_spec,
|
|
)
|
|
from sphinx.testing.util import etree_parse
|
|
|
|
from tests.test_extensions.ext_napoleon_pep526_data_google import PEP526GoogleClass
|
|
from tests.test_extensions.ext_napoleon_pep526_data_numpy import PEP526NumpyClass
|
|
|
|
|
|
class NamedtupleSubclass(namedtuple('NamedtupleSubclass', ('attr1', 'attr2'))):
|
|
"""Sample namedtuple subclass
|
|
|
|
Attributes
|
|
----------
|
|
attr1 : Arbitrary type
|
|
Quick description of attr1
|
|
attr2 : Another arbitrary type
|
|
Quick description of attr2
|
|
attr3 : Type
|
|
|
|
Adds a newline after the type
|
|
|
|
"""
|
|
|
|
# To avoid creating a dict, as a namedtuple doesn't have it:
|
|
__slots__ = ()
|
|
|
|
def __new__(cls, attr1, attr2=None):
|
|
return super().__new__(cls, attr1, attr2)
|
|
|
|
|
|
class TestNamedtupleSubclass:
|
|
def test_attributes_docstring(self):
|
|
config = Config()
|
|
actual = NumpyDocstring(
|
|
cleandoc(NamedtupleSubclass.__doc__),
|
|
config=config,
|
|
app=None,
|
|
what='class',
|
|
name='NamedtupleSubclass',
|
|
obj=NamedtupleSubclass,
|
|
)
|
|
expected = """\
|
|
Sample namedtuple subclass
|
|
|
|
.. attribute:: attr1
|
|
|
|
Quick description of attr1
|
|
|
|
:type: Arbitrary type
|
|
|
|
.. attribute:: attr2
|
|
|
|
Quick description of attr2
|
|
|
|
:type: Another arbitrary type
|
|
|
|
.. attribute:: attr3
|
|
|
|
Adds a newline after the type
|
|
|
|
:type: Type
|
|
"""
|
|
|
|
assert str(actual) == expected
|
|
|
|
|
|
class TestInlineAttribute:
|
|
inline_google_docstring = (
|
|
'inline description with '
|
|
'``a : in code``, '
|
|
'a :ref:`reference`, '
|
|
'a `link <https://foo.bar>`_, '
|
|
'a :meta public:, '
|
|
'a :meta field: value and '
|
|
'an host:port and HH:MM strings.'
|
|
)
|
|
|
|
@staticmethod
|
|
def _docstring(source):
|
|
rst = GoogleDocstring(
|
|
source, config=Config(), app=None, what='attribute', name='some_data', obj=0
|
|
)
|
|
return str(rst)
|
|
|
|
def test_class_data_member(self):
|
|
source = 'data member description:\n\n- a: b'
|
|
actual = self._docstring(source).splitlines()
|
|
assert actual == ['data member description:', '', '- a: b']
|
|
|
|
def test_class_data_member_inline(self):
|
|
source = f'CustomType: {self.inline_google_docstring}'
|
|
actual = self._docstring(source).splitlines()
|
|
assert actual == [self.inline_google_docstring, '', ':type: CustomType']
|
|
|
|
def test_class_data_member_inline_no_type(self):
|
|
source = self.inline_google_docstring
|
|
actual = self._docstring(source).splitlines()
|
|
assert actual == [source]
|
|
|
|
def test_class_data_member_inline_ref_in_type(self):
|
|
source = f':class:`int`: {self.inline_google_docstring}'
|
|
actual = self._docstring(source).splitlines()
|
|
assert actual == [self.inline_google_docstring, '', ':type: :class:`int`']
|
|
|
|
|
|
class TestGoogleDocstring:
|
|
docstrings = [
|
|
(
|
|
"""Single line summary""",
|
|
"""Single line summary""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Extended description
|
|
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
Extended description
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Args:
|
|
arg1(str):Extended
|
|
description of arg1
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Parameters: **arg1** (*str*) -- Extended
|
|
description of arg1
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Args:
|
|
arg1(str):Extended
|
|
description of arg1
|
|
arg2 ( int ) : Extended
|
|
description of arg2
|
|
|
|
Keyword Args:
|
|
kwarg1(str):Extended
|
|
description of kwarg1
|
|
kwarg2 ( int ) : Extended
|
|
description of kwarg2""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Parameters: * **arg1** (*str*) -- Extended
|
|
description of arg1
|
|
* **arg2** (*int*) -- Extended
|
|
description of arg2
|
|
|
|
:Keyword Arguments: * **kwarg1** (*str*) -- Extended
|
|
description of kwarg1
|
|
* **kwarg2** (*int*) -- Extended
|
|
description of kwarg2
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Arguments:
|
|
arg1(str):Extended
|
|
description of arg1
|
|
arg2 ( int ) : Extended
|
|
description of arg2
|
|
|
|
Keyword Arguments:
|
|
kwarg1(str):Extended
|
|
description of kwarg1
|
|
kwarg2 ( int ) : Extended
|
|
description of kwarg2""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Parameters: * **arg1** (*str*) -- Extended
|
|
description of arg1
|
|
* **arg2** (*int*) -- Extended
|
|
description of arg2
|
|
|
|
:Keyword Arguments: * **kwarg1** (*str*) -- Extended
|
|
description of kwarg1
|
|
* **kwarg2** (*int*) -- Extended
|
|
description of kwarg2
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Return:
|
|
str:Extended
|
|
description of return value
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:returns: *str* -- Extended
|
|
description of return value
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Returns:
|
|
str:Extended
|
|
description of return value
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:returns: *str* -- Extended
|
|
description of return value
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Returns:
|
|
Extended
|
|
description of return value
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:returns: Extended
|
|
description of return value
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Returns:
|
|
Extended
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:returns: Extended
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Args:
|
|
arg1(str):Extended
|
|
description of arg1
|
|
*args: Variable length argument list.
|
|
**kwargs: Arbitrary keyword arguments.
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Parameters: * **arg1** (*str*) -- Extended
|
|
description of arg1
|
|
* **\\*args** -- Variable length argument list.
|
|
* **\\*\\*kwargs** -- Arbitrary keyword arguments.
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Args:
|
|
arg1 (list(int)): Description
|
|
arg2 (list[int]): Description
|
|
arg3 (dict(str, int)): Description
|
|
arg4 (dict[str, int]): Description
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Parameters: * **arg1** (*list(int)*) -- Description
|
|
* **arg2** (*list[int]*) -- Description
|
|
* **arg3** (*dict(str, int)*) -- Description
|
|
* **arg4** (*dict[str, int]*) -- Description
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Receive:
|
|
arg1 (list(int)): Description
|
|
arg2 (list[int]): Description
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Receives: * **arg1** (*list(int)*) -- Description
|
|
* **arg2** (*list[int]*) -- Description
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Receives:
|
|
arg1 (list(int)): Description
|
|
arg2 (list[int]): Description
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Receives: * **arg1** (*list(int)*) -- Description
|
|
* **arg2** (*list[int]*) -- Description
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Yield:
|
|
str:Extended
|
|
description of yielded value
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Yields: *str* -- Extended
|
|
description of yielded value
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Yields:
|
|
Extended
|
|
description of yielded value
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Yields: Extended
|
|
description of yielded value
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Args:
|
|
|
|
arg1 (list of str): Extended
|
|
description of arg1.
|
|
arg2 (tuple of int): Extended
|
|
description of arg2.
|
|
arg3 (tuple of list of float): Extended
|
|
description of arg3.
|
|
arg4 (int, float, or list of bool): Extended
|
|
description of arg4.
|
|
arg5 (list of int, float, or bool): Extended
|
|
description of arg5.
|
|
arg6 (list of int or float): Extended
|
|
description of arg6.
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Parameters: * **arg1** (*list of str*) -- Extended
|
|
description of arg1.
|
|
* **arg2** (*tuple of int*) -- Extended
|
|
description of arg2.
|
|
* **arg3** (*tuple of list of float*) -- Extended
|
|
description of arg3.
|
|
* **arg4** (*int, float, or list of bool*) -- Extended
|
|
description of arg4.
|
|
* **arg5** (*list of int, float, or bool*) -- Extended
|
|
description of arg5.
|
|
* **arg6** (*list of int or float*) -- Extended
|
|
description of arg6.
|
|
""",
|
|
),
|
|
]
|
|
|
|
def test_sphinx_admonitions(self):
|
|
admonition_map = {
|
|
'Attention': 'attention',
|
|
'Caution': 'caution',
|
|
'Danger': 'danger',
|
|
'Error': 'error',
|
|
'Hint': 'hint',
|
|
'Important': 'important',
|
|
'Note': 'note',
|
|
'Tip': 'tip',
|
|
'Todo': 'todo',
|
|
'Warning': 'warning',
|
|
'Warnings': 'warning',
|
|
}
|
|
config = Config()
|
|
for section, admonition in admonition_map.items():
|
|
# Multiline
|
|
actual = GoogleDocstring(
|
|
f'{section}:\n'
|
|
' this is the first line\n'
|
|
'\n'
|
|
' and this is the second line\n',
|
|
config,
|
|
)
|
|
expect = (
|
|
f'.. {admonition}::\n'
|
|
'\n'
|
|
' this is the first line\n'
|
|
' \n'
|
|
' and this is the second line\n'
|
|
)
|
|
assert str(actual) == expect
|
|
|
|
# Single line
|
|
actual = GoogleDocstring(
|
|
f'{section}:\n' ' this is a single line\n', config
|
|
)
|
|
expect = f'.. {admonition}:: this is a single line\n'
|
|
assert str(actual) == expect
|
|
|
|
def test_docstrings(self):
|
|
config = Config(
|
|
napoleon_use_param=False,
|
|
napoleon_use_rtype=False,
|
|
napoleon_use_keyword=False,
|
|
)
|
|
for docstring, expected in self.docstrings:
|
|
actual = GoogleDocstring(dedent(docstring), config)
|
|
expected = dedent(expected)
|
|
assert str(actual) == expected
|
|
|
|
def test_parameters_with_class_reference(self):
|
|
docstring = """\
|
|
Construct a new XBlock.
|
|
|
|
This class should only be used by runtimes.
|
|
|
|
Arguments:
|
|
runtime (:class:`~typing.Dict`\\[:class:`int`,:class:`str`\\]): Use it to
|
|
access the environment. It is available in XBlock code
|
|
as ``self.runtime``.
|
|
|
|
field_data (:class:`FieldData`): Interface used by the XBlock
|
|
fields to access their data from wherever it is persisted.
|
|
|
|
scope_ids (:class:`ScopeIds`): Identifiers needed to resolve scopes.
|
|
|
|
"""
|
|
|
|
actual = GoogleDocstring(docstring)
|
|
expected = """\
|
|
Construct a new XBlock.
|
|
|
|
This class should only be used by runtimes.
|
|
|
|
:param runtime: Use it to
|
|
access the environment. It is available in XBlock code
|
|
as ``self.runtime``.
|
|
:type runtime: :class:`~typing.Dict`\\[:class:`int`,:class:`str`\\]
|
|
:param field_data: Interface used by the XBlock
|
|
fields to access their data from wherever it is persisted.
|
|
:type field_data: :class:`FieldData`
|
|
:param scope_ids: Identifiers needed to resolve scopes.
|
|
:type scope_ids: :class:`ScopeIds`
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
def test_attributes_with_class_reference(self):
|
|
docstring = """\
|
|
Attributes:
|
|
in_attr(:class:`numpy.ndarray`): super-dooper attribute
|
|
"""
|
|
|
|
actual = GoogleDocstring(docstring)
|
|
expected = """\
|
|
.. attribute:: in_attr
|
|
|
|
super-dooper attribute
|
|
|
|
:type: :class:`numpy.ndarray`
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
docstring = """\
|
|
Attributes:
|
|
in_attr(numpy.ndarray): super-dooper attribute
|
|
"""
|
|
|
|
actual = GoogleDocstring(docstring)
|
|
expected = """\
|
|
.. attribute:: in_attr
|
|
|
|
super-dooper attribute
|
|
|
|
:type: numpy.ndarray
|
|
"""
|
|
|
|
def test_attributes_with_use_ivar(self):
|
|
docstring = """\
|
|
Attributes:
|
|
foo (int): blah blah
|
|
bar (str): blah blah
|
|
"""
|
|
|
|
config = Config(napoleon_use_ivar=True)
|
|
actual = GoogleDocstring(docstring, config, obj=self.__class__)
|
|
expected = """\
|
|
:ivar foo: blah blah
|
|
:vartype foo: int
|
|
:ivar bar: blah blah
|
|
:vartype bar: str
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
def test_code_block_in_returns_section(self):
|
|
docstring = """
|
|
Returns:
|
|
foobar: foo::
|
|
|
|
codecode
|
|
codecode
|
|
"""
|
|
expected = """
|
|
:returns:
|
|
|
|
foo::
|
|
|
|
codecode
|
|
codecode
|
|
:rtype: foobar
|
|
"""
|
|
actual = GoogleDocstring(docstring)
|
|
assert str(actual) == expected
|
|
|
|
def test_colon_in_return_type(self):
|
|
docstring = """Example property.
|
|
|
|
Returns:
|
|
:py:class:`~.module.submodule.SomeClass`: an example instance
|
|
if available, None if not available.
|
|
"""
|
|
expected = """Example property.
|
|
|
|
:returns: an example instance
|
|
if available, None if not available.
|
|
:rtype: :py:class:`~.module.submodule.SomeClass`
|
|
"""
|
|
actual = GoogleDocstring(docstring)
|
|
assert str(actual) == expected
|
|
|
|
def test_xrefs_in_return_type(self):
|
|
docstring = """Example Function
|
|
|
|
Returns:
|
|
:class:`numpy.ndarray`: A :math:`n \\times 2` array containing
|
|
a bunch of math items
|
|
"""
|
|
expected = """Example Function
|
|
|
|
:returns: A :math:`n \\times 2` array containing
|
|
a bunch of math items
|
|
:rtype: :class:`numpy.ndarray`
|
|
"""
|
|
actual = GoogleDocstring(docstring)
|
|
assert str(actual) == expected
|
|
|
|
def test_raises_types(self):
|
|
docstrings = [
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
RuntimeError:
|
|
A setting wasn't specified, or was invalid.
|
|
ValueError:
|
|
Something something value error.
|
|
:py:class:`AttributeError`
|
|
errors for missing attributes.
|
|
~InvalidDimensionsError
|
|
If the dimensions couldn't be parsed.
|
|
`InvalidArgumentsError`
|
|
If the arguments are invalid.
|
|
:exc:`~ValueError`
|
|
If the arguments are wrong.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises RuntimeError: A setting wasn't specified, or was invalid.
|
|
:raises ValueError: Something something value error.
|
|
:raises AttributeError: errors for missing attributes.
|
|
:raises ~InvalidDimensionsError: If the dimensions couldn't be parsed.
|
|
:raises InvalidArgumentsError: If the arguments are invalid.
|
|
:raises ~ValueError: If the arguments are wrong.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
InvalidDimensionsError
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises InvalidDimensionsError:
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
Invalid Dimensions Error
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises Invalid Dimensions Error:
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
Invalid Dimensions Error: With description
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises Invalid Dimensions Error: With description
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
InvalidDimensionsError: If the dimensions couldn't be parsed.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises InvalidDimensionsError: If the dimensions couldn't be parsed.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
Invalid Dimensions Error: If the dimensions couldn't be parsed.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises Invalid Dimensions Error: If the dimensions couldn't be parsed.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
If the dimensions couldn't be parsed.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises If the dimensions couldn't be parsed.:
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
:class:`exc.InvalidDimensionsError`
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises exc.InvalidDimensionsError:
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
:class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
:class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed,
|
|
then a :class:`exc.InvalidDimensionsError` will be raised.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed,
|
|
then a :class:`exc.InvalidDimensionsError` will be raised.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
:class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed.
|
|
:class:`exc.InvalidArgumentsError`: If the arguments are invalid.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed.
|
|
:raises exc.InvalidArgumentsError: If the arguments are invalid.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises:
|
|
:class:`exc.InvalidDimensionsError`
|
|
:class:`exc.InvalidArgumentsError`
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises exc.InvalidDimensionsError:
|
|
:raises exc.InvalidArgumentsError:
|
|
""",
|
|
),
|
|
]
|
|
for docstring, expected in docstrings:
|
|
actual = GoogleDocstring(docstring)
|
|
assert str(actual) == expected
|
|
|
|
def test_kwargs_in_arguments(self):
|
|
docstring = """Allows to create attributes binded to this device.
|
|
|
|
Some other paragraph.
|
|
|
|
Code sample for usage::
|
|
|
|
dev.bind(loopback=Loopback)
|
|
dev.loopback.configure()
|
|
|
|
Arguments:
|
|
**kwargs: name/class pairs that will create resource-managers
|
|
bound as instance attributes to this instance. See code
|
|
example above.
|
|
"""
|
|
expected = """Allows to create attributes binded to this device.
|
|
|
|
Some other paragraph.
|
|
|
|
Code sample for usage::
|
|
|
|
dev.bind(loopback=Loopback)
|
|
dev.loopback.configure()
|
|
|
|
:param \\*\\*kwargs: name/class pairs that will create resource-managers
|
|
bound as instance attributes to this instance. See code
|
|
example above.
|
|
"""
|
|
actual = GoogleDocstring(docstring)
|
|
assert str(actual) == expected
|
|
|
|
def test_section_header_formatting(self):
|
|
docstrings = [
|
|
(
|
|
"""
|
|
Summary line
|
|
|
|
Example:
|
|
Multiline reStructuredText
|
|
literal code block
|
|
|
|
""",
|
|
"""
|
|
Summary line
|
|
|
|
.. rubric:: Example
|
|
|
|
Multiline reStructuredText
|
|
literal code block
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Summary line
|
|
|
|
Example::
|
|
|
|
Multiline reStructuredText
|
|
literal code block
|
|
|
|
""",
|
|
"""
|
|
Summary line
|
|
|
|
Example::
|
|
|
|
Multiline reStructuredText
|
|
literal code block
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Summary line
|
|
|
|
:Example:
|
|
|
|
Multiline reStructuredText
|
|
literal code block
|
|
|
|
""",
|
|
"""
|
|
Summary line
|
|
|
|
:Example:
|
|
|
|
Multiline reStructuredText
|
|
literal code block
|
|
""",
|
|
),
|
|
]
|
|
for docstring, expected in docstrings:
|
|
actual = GoogleDocstring(docstring)
|
|
assert str(actual) == expected
|
|
|
|
def test_list_in_parameter_description(self):
|
|
docstring = """One line summary.
|
|
|
|
Parameters:
|
|
no_list (int):
|
|
one_bullet_empty (int):
|
|
*
|
|
one_bullet_single_line (int):
|
|
- first line
|
|
one_bullet_two_lines (int):
|
|
+ first line
|
|
continued
|
|
two_bullets_single_line (int):
|
|
- first line
|
|
- second line
|
|
two_bullets_two_lines (int):
|
|
* first line
|
|
continued
|
|
* second line
|
|
continued
|
|
one_enumeration_single_line (int):
|
|
1. first line
|
|
one_enumeration_two_lines (int):
|
|
1) first line
|
|
continued
|
|
two_enumerations_one_line (int):
|
|
(iii) first line
|
|
(iv) second line
|
|
two_enumerations_two_lines (int):
|
|
a. first line
|
|
continued
|
|
b. second line
|
|
continued
|
|
one_definition_one_line (int):
|
|
item 1
|
|
first line
|
|
one_definition_two_lines (int):
|
|
item 1
|
|
first line
|
|
continued
|
|
two_definitions_one_line (int):
|
|
item 1
|
|
first line
|
|
item 2
|
|
second line
|
|
two_definitions_two_lines (int):
|
|
item 1
|
|
first line
|
|
continued
|
|
item 2
|
|
second line
|
|
continued
|
|
one_definition_blank_line (int):
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
|
|
two_definitions_blank_lines (int):
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
|
|
item 2
|
|
|
|
second line
|
|
|
|
extra second line
|
|
|
|
definition_after_inline_text (int): text line
|
|
|
|
item 1
|
|
first line
|
|
|
|
definition_after_normal_text (int):
|
|
text line
|
|
|
|
item 1
|
|
first line
|
|
"""
|
|
|
|
expected = """One line summary.
|
|
|
|
:param no_list:
|
|
:type no_list: int
|
|
:param one_bullet_empty:
|
|
*
|
|
:type one_bullet_empty: int
|
|
:param one_bullet_single_line:
|
|
- first line
|
|
:type one_bullet_single_line: int
|
|
:param one_bullet_two_lines:
|
|
+ first line
|
|
continued
|
|
:type one_bullet_two_lines: int
|
|
:param two_bullets_single_line:
|
|
- first line
|
|
- second line
|
|
:type two_bullets_single_line: int
|
|
:param two_bullets_two_lines:
|
|
* first line
|
|
continued
|
|
* second line
|
|
continued
|
|
:type two_bullets_two_lines: int
|
|
:param one_enumeration_single_line:
|
|
1. first line
|
|
:type one_enumeration_single_line: int
|
|
:param one_enumeration_two_lines:
|
|
1) first line
|
|
continued
|
|
:type one_enumeration_two_lines: int
|
|
:param two_enumerations_one_line:
|
|
(iii) first line
|
|
(iv) second line
|
|
:type two_enumerations_one_line: int
|
|
:param two_enumerations_two_lines:
|
|
a. first line
|
|
continued
|
|
b. second line
|
|
continued
|
|
:type two_enumerations_two_lines: int
|
|
:param one_definition_one_line:
|
|
item 1
|
|
first line
|
|
:type one_definition_one_line: int
|
|
:param one_definition_two_lines:
|
|
item 1
|
|
first line
|
|
continued
|
|
:type one_definition_two_lines: int
|
|
:param two_definitions_one_line:
|
|
item 1
|
|
first line
|
|
item 2
|
|
second line
|
|
:type two_definitions_one_line: int
|
|
:param two_definitions_two_lines:
|
|
item 1
|
|
first line
|
|
continued
|
|
item 2
|
|
second line
|
|
continued
|
|
:type two_definitions_two_lines: int
|
|
:param one_definition_blank_line:
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
:type one_definition_blank_line: int
|
|
:param two_definitions_blank_lines:
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
|
|
item 2
|
|
|
|
second line
|
|
|
|
extra second line
|
|
:type two_definitions_blank_lines: int
|
|
:param definition_after_inline_text: text line
|
|
|
|
item 1
|
|
first line
|
|
:type definition_after_inline_text: int
|
|
:param definition_after_normal_text: text line
|
|
|
|
item 1
|
|
first line
|
|
:type definition_after_normal_text: int
|
|
"""
|
|
config = Config(napoleon_use_param=True)
|
|
actual = GoogleDocstring(docstring, config)
|
|
assert str(actual) == expected
|
|
|
|
expected = """One line summary.
|
|
|
|
:Parameters: * **no_list** (*int*)
|
|
* **one_bullet_empty** (*int*) --
|
|
|
|
*
|
|
* **one_bullet_single_line** (*int*) --
|
|
|
|
- first line
|
|
* **one_bullet_two_lines** (*int*) --
|
|
|
|
+ first line
|
|
continued
|
|
* **two_bullets_single_line** (*int*) --
|
|
|
|
- first line
|
|
- second line
|
|
* **two_bullets_two_lines** (*int*) --
|
|
|
|
* first line
|
|
continued
|
|
* second line
|
|
continued
|
|
* **one_enumeration_single_line** (*int*) --
|
|
|
|
1. first line
|
|
* **one_enumeration_two_lines** (*int*) --
|
|
|
|
1) first line
|
|
continued
|
|
* **two_enumerations_one_line** (*int*) --
|
|
|
|
(iii) first line
|
|
(iv) second line
|
|
* **two_enumerations_two_lines** (*int*) --
|
|
|
|
a. first line
|
|
continued
|
|
b. second line
|
|
continued
|
|
* **one_definition_one_line** (*int*) --
|
|
|
|
item 1
|
|
first line
|
|
* **one_definition_two_lines** (*int*) --
|
|
|
|
item 1
|
|
first line
|
|
continued
|
|
* **two_definitions_one_line** (*int*) --
|
|
|
|
item 1
|
|
first line
|
|
item 2
|
|
second line
|
|
* **two_definitions_two_lines** (*int*) --
|
|
|
|
item 1
|
|
first line
|
|
continued
|
|
item 2
|
|
second line
|
|
continued
|
|
* **one_definition_blank_line** (*int*) --
|
|
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
* **two_definitions_blank_lines** (*int*) --
|
|
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
|
|
item 2
|
|
|
|
second line
|
|
|
|
extra second line
|
|
* **definition_after_inline_text** (*int*) -- text line
|
|
|
|
item 1
|
|
first line
|
|
* **definition_after_normal_text** (*int*) -- text line
|
|
|
|
item 1
|
|
first line
|
|
"""
|
|
config = Config(napoleon_use_param=False)
|
|
actual = GoogleDocstring(docstring, config)
|
|
assert str(actual) == expected
|
|
|
|
def test_custom_generic_sections(self):
|
|
docstrings = (
|
|
(
|
|
"""\
|
|
Really Important Details:
|
|
You should listen to me!
|
|
""",
|
|
""".. rubric:: Really Important Details
|
|
|
|
You should listen to me!
|
|
""",
|
|
),
|
|
(
|
|
"""\
|
|
Sooper Warning:
|
|
Stop hitting yourself!
|
|
""",
|
|
""":Warns: **Stop hitting yourself!**
|
|
""",
|
|
),
|
|
(
|
|
"""\
|
|
Params Style:
|
|
arg1 (int): Description of arg1
|
|
arg2 (str): Description of arg2
|
|
|
|
""",
|
|
"""\
|
|
:Params Style: * **arg1** (*int*) -- Description of arg1
|
|
* **arg2** (*str*) -- Description of arg2
|
|
""",
|
|
),
|
|
(
|
|
"""\
|
|
Returns Style:
|
|
description of custom section
|
|
|
|
""",
|
|
""":Returns Style: description of custom section
|
|
""",
|
|
),
|
|
)
|
|
|
|
testConfig = Config(
|
|
napoleon_custom_sections=[
|
|
'Really Important Details',
|
|
('Sooper Warning', 'warns'),
|
|
('Params Style', 'params_style'),
|
|
('Returns Style', 'returns_style'),
|
|
]
|
|
)
|
|
|
|
for docstring, expected in docstrings:
|
|
actual = GoogleDocstring(docstring, testConfig)
|
|
assert str(actual) == expected
|
|
|
|
def test_noindex(self):
|
|
docstring = """
|
|
Attributes:
|
|
arg
|
|
description
|
|
|
|
Methods:
|
|
func(i, j)
|
|
description
|
|
"""
|
|
|
|
expected = """
|
|
.. attribute:: arg
|
|
:no-index:
|
|
|
|
description
|
|
|
|
.. method:: func(i, j)
|
|
:no-index:
|
|
|
|
|
|
description
|
|
""" # NoQA: W293
|
|
config = Config()
|
|
actual = GoogleDocstring(
|
|
docstring,
|
|
config=config,
|
|
app=None,
|
|
what='module',
|
|
options={'no-index': True},
|
|
)
|
|
assert str(actual) == expected
|
|
|
|
def test_keywords_with_types(self):
|
|
docstring = """\
|
|
Do as you please
|
|
|
|
Keyword Args:
|
|
gotham_is_yours (None): shall interfere.
|
|
"""
|
|
actual = GoogleDocstring(docstring)
|
|
expected = """\
|
|
Do as you please
|
|
|
|
:keyword gotham_is_yours: shall interfere.
|
|
:kwtype gotham_is_yours: None
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
def test_pep526_annotations(self):
|
|
# Test class attributes annotations
|
|
config = Config(
|
|
napoleon_attr_annotations=True,
|
|
)
|
|
actual = GoogleDocstring(
|
|
cleandoc(PEP526GoogleClass.__doc__),
|
|
config,
|
|
app=None,
|
|
what='class',
|
|
obj=PEP526GoogleClass,
|
|
)
|
|
expected = """\
|
|
Sample class with PEP 526 annotations and google docstring.
|
|
|
|
.. attribute:: attr1
|
|
|
|
Attr1 description.
|
|
|
|
:type: int
|
|
|
|
.. attribute:: attr2
|
|
|
|
Attr2 description.
|
|
|
|
:type: str
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
def test_preprocess_types(self):
|
|
docstring = """\
|
|
Do as you please
|
|
|
|
Yield:
|
|
str:Extended
|
|
"""
|
|
actual = GoogleDocstring(docstring)
|
|
expected = """\
|
|
Do as you please
|
|
|
|
:Yields: *str* -- Extended
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
config = Config(napoleon_preprocess_types=True)
|
|
actual = GoogleDocstring(docstring, config)
|
|
expected = """\
|
|
Do as you please
|
|
|
|
:Yields: :py:class:`str` -- Extended
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
|
|
class TestNumpyDocstring:
|
|
docstrings = [
|
|
(
|
|
"""Single line summary""",
|
|
"""Single line summary""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Extended description
|
|
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
Extended description
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Parameters
|
|
----------
|
|
arg1:str
|
|
Extended
|
|
description of arg1
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Parameters: **arg1** (:class:`str`) -- Extended
|
|
description of arg1
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Parameters
|
|
----------
|
|
arg1:str
|
|
Extended
|
|
description of arg1
|
|
arg2 : int
|
|
Extended
|
|
description of arg2
|
|
|
|
Keyword Arguments
|
|
-----------------
|
|
kwarg1:str
|
|
Extended
|
|
description of kwarg1
|
|
kwarg2 : int
|
|
Extended
|
|
description of kwarg2
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Parameters: * **arg1** (:class:`str`) -- Extended
|
|
description of arg1
|
|
* **arg2** (:class:`int`) -- Extended
|
|
description of arg2
|
|
|
|
:Keyword Arguments: * **kwarg1** (:class:`str`) -- Extended
|
|
description of kwarg1
|
|
* **kwarg2** (:class:`int`) -- Extended
|
|
description of kwarg2
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Return
|
|
------
|
|
str
|
|
Extended
|
|
description of return value
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:returns: :class:`str` -- Extended
|
|
description of return value
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
Extended
|
|
description of return value
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:returns: :class:`str` -- Extended
|
|
description of return value
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Parameters
|
|
----------
|
|
arg1:str
|
|
Extended description of arg1
|
|
*args:
|
|
Variable length argument list.
|
|
**kwargs:
|
|
Arbitrary keyword arguments.
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Parameters: * **arg1** (:class:`str`) -- Extended description of arg1
|
|
* **\\*args** -- Variable length argument list.
|
|
* **\\*\\*kwargs** -- Arbitrary keyword arguments.
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Parameters
|
|
----------
|
|
arg1:str
|
|
Extended description of arg1
|
|
*args, **kwargs:
|
|
Variable length argument list and arbitrary keyword arguments.
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Parameters: * **arg1** (:class:`str`) -- Extended description of arg1
|
|
* **\\*args, \\*\\*kwargs** -- Variable length argument list and arbitrary keyword arguments.
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Receive
|
|
-------
|
|
arg1:str
|
|
Extended
|
|
description of arg1
|
|
arg2 : int
|
|
Extended
|
|
description of arg2
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Receives: * **arg1** (:class:`str`) -- Extended
|
|
description of arg1
|
|
* **arg2** (:class:`int`) -- Extended
|
|
description of arg2
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Receives
|
|
--------
|
|
arg1:str
|
|
Extended
|
|
description of arg1
|
|
arg2 : int
|
|
Extended
|
|
description of arg2
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Receives: * **arg1** (:class:`str`) -- Extended
|
|
description of arg1
|
|
* **arg2** (:class:`int`) -- Extended
|
|
description of arg2
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Yield
|
|
-----
|
|
str
|
|
Extended
|
|
description of yielded value
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Yields: :class:`str` -- Extended
|
|
description of yielded value
|
|
""",
|
|
),
|
|
(
|
|
"""
|
|
Single line summary
|
|
|
|
Yields
|
|
------
|
|
str
|
|
Extended
|
|
description of yielded value
|
|
""",
|
|
"""
|
|
Single line summary
|
|
|
|
:Yields: :class:`str` -- Extended
|
|
description of yielded value
|
|
""",
|
|
),
|
|
]
|
|
|
|
def test_sphinx_admonitions(self):
|
|
admonition_map = {
|
|
'Attention': 'attention',
|
|
'Caution': 'caution',
|
|
'Danger': 'danger',
|
|
'Error': 'error',
|
|
'Hint': 'hint',
|
|
'Important': 'important',
|
|
'Note': 'note',
|
|
'Tip': 'tip',
|
|
'Todo': 'todo',
|
|
'Warning': 'warning',
|
|
'Warnings': 'warning',
|
|
}
|
|
config = Config()
|
|
for section, admonition in admonition_map.items():
|
|
# Multiline
|
|
actual = NumpyDocstring(
|
|
f"{section}\n"
|
|
f"{'-' * len(section)}\n"
|
|
" this is the first line\n"
|
|
"\n"
|
|
" and this is the second line\n",
|
|
config,
|
|
)
|
|
expect = (
|
|
f'.. {admonition}::\n'
|
|
'\n'
|
|
' this is the first line\n'
|
|
' \n'
|
|
' and this is the second line\n'
|
|
)
|
|
assert str(actual) == expect
|
|
|
|
# Single line
|
|
actual = NumpyDocstring(
|
|
f"{section}\n{'-' * len(section)}\n this is a single line\n",
|
|
config,
|
|
)
|
|
expect = f'.. {admonition}:: this is a single line\n'
|
|
assert str(actual) == expect
|
|
|
|
def test_docstrings(self):
|
|
config = Config(
|
|
napoleon_use_param=False,
|
|
napoleon_use_rtype=False,
|
|
napoleon_use_keyword=False,
|
|
napoleon_preprocess_types=True,
|
|
)
|
|
for docstring, expected in self.docstrings:
|
|
actual = NumpyDocstring(dedent(docstring), config)
|
|
expected = dedent(expected)
|
|
assert str(actual) == expected
|
|
|
|
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 = NumpyDocstring(docstring, config)
|
|
expected = dedent("""
|
|
Single line summary
|
|
|
|
:Parameters: **arg1** (*str*) -- Extended
|
|
description of arg1
|
|
""")
|
|
assert str(actual) == expected
|
|
|
|
def test_parameters_with_class_reference(self):
|
|
docstring = """\
|
|
Parameters
|
|
----------
|
|
param1 : :class:`MyClass <name.space.MyClass>` instance
|
|
|
|
Other Parameters
|
|
----------------
|
|
param2 : :class:`MyClass <name.space.MyClass>` instance
|
|
|
|
"""
|
|
|
|
config = Config(napoleon_use_param=False)
|
|
actual = NumpyDocstring(docstring, config)
|
|
expected = """\
|
|
:Parameters: **param1** (:class:`MyClass <name.space.MyClass>` instance)
|
|
|
|
:Other Parameters: **param2** (:class:`MyClass <name.space.MyClass>` instance)
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
config = Config(napoleon_use_param=True)
|
|
actual = NumpyDocstring(docstring, config)
|
|
expected = """\
|
|
:param param1:
|
|
:type param1: :class:`MyClass <name.space.MyClass>` instance
|
|
|
|
:param param2:
|
|
:type param2: :class:`MyClass <name.space.MyClass>` instance
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
def test_multiple_parameters(self):
|
|
docstring = """\
|
|
Parameters
|
|
----------
|
|
x1, x2 : array_like
|
|
Input arrays, description of ``x1``, ``x2``.
|
|
|
|
"""
|
|
|
|
config = Config(napoleon_use_param=False)
|
|
actual = NumpyDocstring(docstring, config)
|
|
expected = """\
|
|
:Parameters: **x1, x2** (*array_like*) -- Input arrays, description of ``x1``, ``x2``.
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
config = Config(napoleon_use_param=True)
|
|
actual = NumpyDocstring(dedent(docstring), config)
|
|
expected = """\
|
|
:param x1: Input arrays, description of ``x1``, ``x2``.
|
|
:type x1: array_like
|
|
:param x2: Input arrays, description of ``x1``, ``x2``.
|
|
:type x2: array_like
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
def test_parameters_without_class_reference(self):
|
|
docstring = """\
|
|
Parameters
|
|
----------
|
|
param1 : MyClass instance
|
|
|
|
"""
|
|
|
|
config = Config(napoleon_use_param=False)
|
|
actual = NumpyDocstring(docstring, config)
|
|
expected = """\
|
|
:Parameters: **param1** (*MyClass instance*)
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
config = Config(napoleon_use_param=True)
|
|
actual = NumpyDocstring(dedent(docstring), config)
|
|
expected = """\
|
|
:param param1:
|
|
:type param1: MyClass instance
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
def test_see_also_refs(self):
|
|
docstring = """\
|
|
numpy.multivariate_normal(mean, cov, shape=None, spam=None)
|
|
|
|
See Also
|
|
--------
|
|
some, other, funcs
|
|
otherfunc : relationship
|
|
|
|
"""
|
|
|
|
actual = NumpyDocstring(docstring)
|
|
|
|
expected = """\
|
|
numpy.multivariate_normal(mean, cov, shape=None, spam=None)
|
|
|
|
.. seealso::
|
|
|
|
:obj:`some`, :obj:`other`, :obj:`funcs`
|
|
\n\
|
|
:obj:`otherfunc`
|
|
relationship
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
docstring = """\
|
|
numpy.multivariate_normal(mean, cov, shape=None, spam=None)
|
|
|
|
See Also
|
|
--------
|
|
some, other, funcs
|
|
otherfunc : relationship
|
|
|
|
"""
|
|
|
|
config = Config()
|
|
app = mock.Mock()
|
|
actual = NumpyDocstring(docstring, config, app, 'method')
|
|
|
|
expected = """\
|
|
numpy.multivariate_normal(mean, cov, shape=None, spam=None)
|
|
|
|
.. seealso::
|
|
|
|
:obj:`some`, :obj:`other`, :obj:`funcs`
|
|
\n\
|
|
:obj:`otherfunc`
|
|
relationship
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
docstring = """\
|
|
numpy.multivariate_normal(mean, cov, shape=None, spam=None)
|
|
|
|
See Also
|
|
--------
|
|
some, other, :func:`funcs`
|
|
otherfunc : relationship
|
|
|
|
"""
|
|
translations = {
|
|
'other': 'MyClass.other',
|
|
'otherfunc': ':func:`~my_package.otherfunc`',
|
|
}
|
|
config = Config(napoleon_type_aliases=translations)
|
|
app = mock.Mock()
|
|
actual = NumpyDocstring(docstring, config, app, 'method')
|
|
|
|
expected = """\
|
|
numpy.multivariate_normal(mean, cov, shape=None, spam=None)
|
|
|
|
.. seealso::
|
|
|
|
:obj:`some`, :obj:`MyClass.other`, :func:`funcs`
|
|
\n\
|
|
:func:`~my_package.otherfunc`
|
|
relationship
|
|
"""
|
|
assert str(actual) == expected
|
|
|
|
def test_colon_in_return_type(self):
|
|
docstring = """
|
|
Summary
|
|
|
|
Returns
|
|
-------
|
|
:py:class:`~my_mod.my_class`
|
|
an instance of :py:class:`~my_mod.my_class`
|
|
"""
|
|
|
|
expected = """
|
|
Summary
|
|
|
|
:returns: an instance of :py:class:`~my_mod.my_class`
|
|
:rtype: :py:class:`~my_mod.my_class`
|
|
"""
|
|
|
|
config = Config()
|
|
app = mock.Mock()
|
|
actual = NumpyDocstring(docstring, config, app, 'method')
|
|
|
|
assert str(actual) == expected
|
|
|
|
def test_underscore_in_attribute(self):
|
|
docstring = """
|
|
Attributes
|
|
----------
|
|
|
|
arg_ : type
|
|
some description
|
|
"""
|
|
|
|
expected = """
|
|
:ivar arg_: some description
|
|
:vartype arg_: type
|
|
"""
|
|
|
|
config = Config(napoleon_use_ivar=True)
|
|
app = mock.Mock()
|
|
actual = NumpyDocstring(docstring, config, app, 'class')
|
|
|
|
assert str(actual) == expected
|
|
|
|
def test_underscore_in_attribute_strip_signature_backslash(self):
|
|
docstring = """
|
|
Attributes
|
|
----------
|
|
|
|
arg_ : type
|
|
some description
|
|
"""
|
|
|
|
expected = """
|
|
:ivar arg\\_: some description
|
|
:vartype arg\\_: type
|
|
"""
|
|
|
|
config = Config(napoleon_use_ivar=True)
|
|
config.strip_signature_backslash = True
|
|
app = mock.Mock()
|
|
actual = NumpyDocstring(docstring, config, app, 'class')
|
|
|
|
assert str(actual) == expected
|
|
|
|
def test_return_types(self):
|
|
docstring = dedent("""
|
|
Returns
|
|
-------
|
|
DataFrame
|
|
a dataframe
|
|
""")
|
|
expected = dedent("""
|
|
:returns: a dataframe
|
|
:rtype: :class:`~pandas.DataFrame`
|
|
""")
|
|
translations = {
|
|
'DataFrame': '~pandas.DataFrame',
|
|
}
|
|
config = Config(
|
|
napoleon_use_param=True,
|
|
napoleon_use_rtype=True,
|
|
napoleon_preprocess_types=True,
|
|
napoleon_type_aliases=translations,
|
|
)
|
|
actual = NumpyDocstring(docstring, config)
|
|
assert str(actual) == expected
|
|
|
|
def test_yield_types(self):
|
|
docstring = dedent("""
|
|
Example Function
|
|
|
|
Yields
|
|
------
|
|
scalar or array-like
|
|
The result of the computation
|
|
""")
|
|
expected = dedent("""
|
|
Example Function
|
|
|
|
:Yields: :term:`scalar` or :class:`array-like <numpy.ndarray>` -- The result of the computation
|
|
""")
|
|
translations = {
|
|
'scalar': ':term:`scalar`',
|
|
'array-like': ':class:`array-like <numpy.ndarray>`',
|
|
}
|
|
config = Config(
|
|
napoleon_type_aliases=translations, napoleon_preprocess_types=True
|
|
)
|
|
app = mock.Mock()
|
|
actual = NumpyDocstring(docstring, config, app, 'method')
|
|
assert str(actual) == expected
|
|
|
|
def test_raises_types(self):
|
|
docstrings = [
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
RuntimeError
|
|
|
|
A setting wasn't specified, or was invalid.
|
|
ValueError
|
|
|
|
Something something value error.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises RuntimeError: A setting wasn't specified, or was invalid.
|
|
:raises ValueError: Something something value error.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
InvalidDimensionsError
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises InvalidDimensionsError:
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
Invalid Dimensions Error
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises Invalid Dimensions Error:
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
Invalid Dimensions Error
|
|
With description
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises Invalid Dimensions Error: With description
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
InvalidDimensionsError
|
|
If the dimensions couldn't be parsed.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises InvalidDimensionsError: If the dimensions couldn't be parsed.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
Invalid Dimensions Error
|
|
If the dimensions couldn't be parsed.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises Invalid Dimensions Error: If the dimensions couldn't be parsed.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
If the dimensions couldn't be parsed.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises If the dimensions couldn't be parsed.:
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
:class:`exc.InvalidDimensionsError`
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises exc.InvalidDimensionsError:
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
:class:`exc.InvalidDimensionsError`
|
|
If the dimensions couldn't be parsed.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
:class:`exc.InvalidDimensionsError`
|
|
If the dimensions couldn't be parsed,
|
|
then a :class:`exc.InvalidDimensionsError` will be raised.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed,
|
|
then a :class:`exc.InvalidDimensionsError` will be raised.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
:class:`exc.InvalidDimensionsError`
|
|
If the dimensions couldn't be parsed.
|
|
:class:`exc.InvalidArgumentsError`
|
|
If the arguments are invalid.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed.
|
|
:raises exc.InvalidArgumentsError: If the arguments are invalid.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
CustomError
|
|
If the dimensions couldn't be parsed.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises package.CustomError: If the dimensions couldn't be parsed.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
AnotherError
|
|
If the dimensions couldn't be parsed.
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises ~package.AnotherError: If the dimensions couldn't be parsed.
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Example Function
|
|
|
|
Raises
|
|
------
|
|
:class:`exc.InvalidDimensionsError`
|
|
:class:`exc.InvalidArgumentsError`
|
|
|
|
""",
|
|
"""
|
|
Example Function
|
|
|
|
:raises exc.InvalidDimensionsError:
|
|
:raises exc.InvalidArgumentsError:
|
|
""",
|
|
),
|
|
]
|
|
for docstring, expected in docstrings:
|
|
translations = {
|
|
'CustomError': 'package.CustomError',
|
|
'AnotherError': ':py:exc:`~package.AnotherError`',
|
|
}
|
|
config = Config(
|
|
napoleon_type_aliases=translations, napoleon_preprocess_types=True
|
|
)
|
|
app = mock.Mock()
|
|
actual = NumpyDocstring(docstring, config, app, 'method')
|
|
assert str(actual) == expected
|
|
|
|
def test_xrefs_in_return_type(self):
|
|
docstring = """
|
|
Example Function
|
|
|
|
Returns
|
|
-------
|
|
:class:`numpy.ndarray`
|
|
A :math:`n \\times 2` array containing
|
|
a bunch of math items
|
|
"""
|
|
expected = """
|
|
Example Function
|
|
|
|
:returns: A :math:`n \\times 2` array containing
|
|
a bunch of math items
|
|
:rtype: :class:`numpy.ndarray`
|
|
"""
|
|
config = Config()
|
|
app = mock.Mock()
|
|
actual = NumpyDocstring(docstring, config, app, 'method')
|
|
assert str(actual) == expected
|
|
|
|
def test_section_header_underline_length(self):
|
|
docstrings = [
|
|
(
|
|
"""
|
|
Summary line
|
|
|
|
Example
|
|
-
|
|
Multiline example
|
|
body
|
|
|
|
""",
|
|
"""
|
|
Summary line
|
|
|
|
Example
|
|
-
|
|
Multiline example
|
|
body
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Summary line
|
|
|
|
Example
|
|
--
|
|
Multiline example
|
|
body
|
|
|
|
""",
|
|
"""
|
|
Summary line
|
|
|
|
.. rubric:: Example
|
|
|
|
Multiline example
|
|
body
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Summary line
|
|
|
|
Example
|
|
-------
|
|
Multiline example
|
|
body
|
|
|
|
""",
|
|
"""
|
|
Summary line
|
|
|
|
.. rubric:: Example
|
|
|
|
Multiline example
|
|
body
|
|
""",
|
|
),
|
|
################################
|
|
(
|
|
"""
|
|
Summary line
|
|
|
|
Example
|
|
------------
|
|
Multiline example
|
|
body
|
|
|
|
""",
|
|
"""
|
|
Summary line
|
|
|
|
.. rubric:: Example
|
|
|
|
Multiline example
|
|
body
|
|
""",
|
|
),
|
|
]
|
|
for docstring, expected in docstrings:
|
|
actual = NumpyDocstring(docstring)
|
|
assert str(actual) == expected
|
|
|
|
def test_list_in_parameter_description(self):
|
|
docstring = """One line summary.
|
|
|
|
Parameters
|
|
----------
|
|
no_list : int
|
|
one_bullet_empty : int
|
|
*
|
|
one_bullet_single_line : int
|
|
- first line
|
|
one_bullet_two_lines : int
|
|
+ first line
|
|
continued
|
|
two_bullets_single_line : int
|
|
- first line
|
|
- second line
|
|
two_bullets_two_lines : int
|
|
* first line
|
|
continued
|
|
* second line
|
|
continued
|
|
one_enumeration_single_line : int
|
|
1. first line
|
|
one_enumeration_two_lines : int
|
|
1) first line
|
|
continued
|
|
two_enumerations_one_line : int
|
|
(iii) first line
|
|
(iv) second line
|
|
two_enumerations_two_lines : int
|
|
a. first line
|
|
continued
|
|
b. second line
|
|
continued
|
|
one_definition_one_line : int
|
|
item 1
|
|
first line
|
|
one_definition_two_lines : int
|
|
item 1
|
|
first line
|
|
continued
|
|
two_definitions_one_line : int
|
|
item 1
|
|
first line
|
|
item 2
|
|
second line
|
|
two_definitions_two_lines : int
|
|
item 1
|
|
first line
|
|
continued
|
|
item 2
|
|
second line
|
|
continued
|
|
one_definition_blank_line : int
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
|
|
two_definitions_blank_lines : int
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
|
|
item 2
|
|
|
|
second line
|
|
|
|
extra second line
|
|
|
|
definition_after_normal_text : int
|
|
text line
|
|
|
|
item 1
|
|
first line
|
|
"""
|
|
|
|
expected = """One line summary.
|
|
|
|
:param no_list:
|
|
:type no_list: int
|
|
:param one_bullet_empty:
|
|
*
|
|
:type one_bullet_empty: int
|
|
:param one_bullet_single_line:
|
|
- first line
|
|
:type one_bullet_single_line: int
|
|
:param one_bullet_two_lines:
|
|
+ first line
|
|
continued
|
|
:type one_bullet_two_lines: int
|
|
:param two_bullets_single_line:
|
|
- first line
|
|
- second line
|
|
:type two_bullets_single_line: int
|
|
:param two_bullets_two_lines:
|
|
* first line
|
|
continued
|
|
* second line
|
|
continued
|
|
:type two_bullets_two_lines: int
|
|
:param one_enumeration_single_line:
|
|
1. first line
|
|
:type one_enumeration_single_line: int
|
|
:param one_enumeration_two_lines:
|
|
1) first line
|
|
continued
|
|
:type one_enumeration_two_lines: int
|
|
:param two_enumerations_one_line:
|
|
(iii) first line
|
|
(iv) second line
|
|
:type two_enumerations_one_line: int
|
|
:param two_enumerations_two_lines:
|
|
a. first line
|
|
continued
|
|
b. second line
|
|
continued
|
|
:type two_enumerations_two_lines: int
|
|
:param one_definition_one_line:
|
|
item 1
|
|
first line
|
|
:type one_definition_one_line: int
|
|
:param one_definition_two_lines:
|
|
item 1
|
|
first line
|
|
continued
|
|
:type one_definition_two_lines: int
|
|
:param two_definitions_one_line:
|
|
item 1
|
|
first line
|
|
item 2
|
|
second line
|
|
:type two_definitions_one_line: int
|
|
:param two_definitions_two_lines:
|
|
item 1
|
|
first line
|
|
continued
|
|
item 2
|
|
second line
|
|
continued
|
|
:type two_definitions_two_lines: int
|
|
:param one_definition_blank_line:
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
:type one_definition_blank_line: int
|
|
:param two_definitions_blank_lines:
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
|
|
item 2
|
|
|
|
second line
|
|
|
|
extra second line
|
|
:type two_definitions_blank_lines: int
|
|
:param definition_after_normal_text: text line
|
|
|
|
item 1
|
|
first line
|
|
:type definition_after_normal_text: int
|
|
"""
|
|
config = Config(napoleon_use_param=True)
|
|
actual = NumpyDocstring(docstring, config)
|
|
assert str(actual) == expected
|
|
|
|
expected = """One line summary.
|
|
|
|
:Parameters: * **no_list** (:class:`int`)
|
|
* **one_bullet_empty** (:class:`int`) --
|
|
|
|
*
|
|
* **one_bullet_single_line** (:class:`int`) --
|
|
|
|
- first line
|
|
* **one_bullet_two_lines** (:class:`int`) --
|
|
|
|
+ first line
|
|
continued
|
|
* **two_bullets_single_line** (:class:`int`) --
|
|
|
|
- first line
|
|
- second line
|
|
* **two_bullets_two_lines** (:class:`int`) --
|
|
|
|
* first line
|
|
continued
|
|
* second line
|
|
continued
|
|
* **one_enumeration_single_line** (:class:`int`) --
|
|
|
|
1. first line
|
|
* **one_enumeration_two_lines** (:class:`int`) --
|
|
|
|
1) first line
|
|
continued
|
|
* **two_enumerations_one_line** (:class:`int`) --
|
|
|
|
(iii) first line
|
|
(iv) second line
|
|
* **two_enumerations_two_lines** (:class:`int`) --
|
|
|
|
a. first line
|
|
continued
|
|
b. second line
|
|
continued
|
|
* **one_definition_one_line** (:class:`int`) --
|
|
|
|
item 1
|
|
first line
|
|
* **one_definition_two_lines** (:class:`int`) --
|
|
|
|
item 1
|
|
first line
|
|
continued
|
|
* **two_definitions_one_line** (:class:`int`) --
|
|
|
|
item 1
|
|
first line
|
|
item 2
|
|
second line
|
|
* **two_definitions_two_lines** (:class:`int`) --
|
|
|
|
item 1
|
|
first line
|
|
continued
|
|
item 2
|
|
second line
|
|
continued
|
|
* **one_definition_blank_line** (:class:`int`) --
|
|
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
* **two_definitions_blank_lines** (:class:`int`) --
|
|
|
|
item 1
|
|
|
|
first line
|
|
|
|
extra first line
|
|
|
|
item 2
|
|
|
|
second line
|
|
|
|
extra second line
|
|
* **definition_after_normal_text** (:class:`int`) -- text line
|
|
|
|
item 1
|
|
first line
|
|
"""
|
|
config = Config(napoleon_use_param=False, napoleon_preprocess_types=True)
|
|
actual = NumpyDocstring(docstring, config)
|
|
assert str(actual) == expected
|
|
|
|
def test_token_type(self):
|
|
tokens = (
|
|
('1', 'literal'),
|
|
('-4.6', 'literal'),
|
|
('2j', 'literal'),
|
|
("'string'", 'literal'),
|
|
('"another_string"', 'literal'),
|
|
('{1, 2}', 'literal'),
|
|
("{'va{ue', 'set'}", 'literal'),
|
|
('optional', 'control'),
|
|
('default', 'control'),
|
|
(', ', 'delimiter'),
|
|
(' of ', 'delimiter'),
|
|
(' or ', 'delimiter'),
|
|
(': ', 'delimiter'),
|
|
('True', 'obj'),
|
|
('None', 'obj'),
|
|
('name', 'obj'),
|
|
(':py:class:`Enum`', 'reference'),
|
|
)
|
|
|
|
for token, expected in tokens:
|
|
actual = _token_type(token)
|
|
assert actual == expected
|
|
|
|
def test_tokenize_type_spec(self):
|
|
specs = (
|
|
'str',
|
|
'defaultdict',
|
|
'int, float, or complex',
|
|
'int or float or None, optional',
|
|
'list of list of int or float, optional',
|
|
'tuple of list of str, float, or int',
|
|
'{"F", "C", "N"}',
|
|
"{'F', 'C', 'N'}, default: 'F'",
|
|
"{'F', 'C', 'N or C'}, default 'F'",
|
|
"str, default: 'F or C'",
|
|
'int, default: None',
|
|
'int, default None',
|
|
'int, default :obj:`None`',
|
|
'"ma{icious"',
|
|
r"'with \'quotes\''",
|
|
)
|
|
|
|
tokens = (
|
|
['str'],
|
|
['defaultdict'],
|
|
['int', ', ', 'float', ', or ', 'complex'],
|
|
['int', ' or ', 'float', ' or ', 'None', ', ', 'optional'],
|
|
['list', ' of ', 'list', ' of ', 'int', ' or ', 'float', ', ', 'optional'],
|
|
['tuple', ' of ', 'list', ' of ', 'str', ', ', 'float', ', or ', 'int'],
|
|
['{', '"F"', ', ', '"C"', ', ', '"N"', '}'],
|
|
['{', "'F'", ', ', "'C'", ', ', "'N'", '}', ', ', 'default', ': ', "'F'"],
|
|
['{', "'F'", ', ', "'C'", ', ', "'N or C'", '}', ', ', 'default', ' ', "'F'"],
|
|
['str', ', ', 'default', ': ', "'F or C'"],
|
|
['int', ', ', 'default', ': ', 'None'],
|
|
['int', ', ', 'default', ' ', 'None'],
|
|
['int', ', ', 'default', ' ', ':obj:`None`'],
|
|
['"ma{icious"'],
|
|
[r"'with \'quotes\''"],
|
|
) # fmt: skip
|
|
|
|
for spec, expected in zip(specs, tokens, strict=True):
|
|
actual = _tokenize_type_spec(spec)
|
|
assert actual == expected
|
|
|
|
def test_recombine_set_tokens(self):
|
|
tokens = (
|
|
['{', '1', ', ', '2', '}'],
|
|
['{', '"F"', ', ', '"C"', ', ', '"N"', '}', ', ', 'optional'],
|
|
['{', "'F'", ', ', "'C'", ', ', "'N'", '}', ', ', 'default', ': ', 'None'],
|
|
['{', "'F'", ', ', "'C'", ', ', "'N'", '}', ', ', 'default', ' ', 'None'],
|
|
)
|
|
|
|
combined_tokens = (
|
|
['{1, 2}'],
|
|
['{"F", "C", "N"}', ', ', 'optional'],
|
|
["{'F', 'C', 'N'}", ', ', 'default', ': ', 'None'],
|
|
["{'F', 'C', 'N'}", ', ', 'default', ' ', 'None'],
|
|
)
|
|
|
|
for tokens_, expected in zip(tokens, combined_tokens, strict=True):
|
|
actual = _recombine_set_tokens(tokens_)
|
|
assert actual == expected
|
|
|
|
def test_recombine_set_tokens_invalid(self):
|
|
tokens = (
|
|
['{', '1', ', ', '2'],
|
|
['"F"', ', ', '"C"', ', ', '"N"', '}', ', ', 'optional'],
|
|
['{', '1', ', ', '2', ', ', 'default', ': ', 'None'],
|
|
)
|
|
combined_tokens = (
|
|
['{1, 2'],
|
|
['"F"', ', ', '"C"', ', ', '"N"', '}', ', ', 'optional'],
|
|
['{1, 2', ', ', 'default', ': ', 'None'],
|
|
)
|
|
|
|
for tokens_, expected in zip(tokens, combined_tokens, strict=True):
|
|
actual = _recombine_set_tokens(tokens_)
|
|
assert actual == expected
|
|
|
|
def test_convert_numpy_type_spec(self):
|
|
translations = {
|
|
'DataFrame': 'pandas.DataFrame',
|
|
}
|
|
|
|
specs = (
|
|
'',
|
|
'optional',
|
|
'str, optional',
|
|
'int or float or None, default: None',
|
|
'list of tuple of str, optional',
|
|
'int, default None',
|
|
'{"F", "C", "N"}',
|
|
"{'F', 'C', 'N'}, default: 'N'",
|
|
"{'F', 'C', 'N'}, default 'N'",
|
|
'DataFrame, optional',
|
|
)
|
|
|
|
converted = (
|
|
'',
|
|
'*optional*',
|
|
':class:`str`, *optional*',
|
|
':class:`int` or :class:`float` or :obj:`None`, *default*: :obj:`None`',
|
|
':class:`list` of :class:`tuple` of :class:`str`, *optional*',
|
|
':class:`int`, *default* :obj:`None`',
|
|
'``{"F", "C", "N"}``',
|
|
"``{'F', 'C', 'N'}``, *default*: ``'N'``",
|
|
"``{'F', 'C', 'N'}``, *default* ``'N'``",
|
|
':class:`pandas.DataFrame`, *optional*',
|
|
)
|
|
|
|
for spec, expected in zip(specs, converted, strict=True):
|
|
actual = _convert_numpy_type_spec(spec, translations=translations)
|
|
assert actual == expected
|
|
|
|
def test_parameter_types(self):
|
|
docstring = dedent("""\
|
|
Parameters
|
|
----------
|
|
param1 : DataFrame
|
|
the data to work on
|
|
param2 : int or float or None, optional
|
|
a parameter with different types
|
|
param3 : dict-like, optional
|
|
a optional mapping
|
|
param4 : int or float or None, optional
|
|
a optional parameter with different types
|
|
param5 : {"F", "C", "N"}, optional
|
|
a optional parameter with fixed values
|
|
param6 : int, default None
|
|
different default format
|
|
param7 : mapping of hashable to str, optional
|
|
a optional mapping
|
|
param8 : ... or Ellipsis
|
|
ellipsis
|
|
param9 : tuple of list of int
|
|
a parameter with tuple of list of int
|
|
""")
|
|
expected = dedent("""\
|
|
:param param1: the data to work on
|
|
:type param1: :class:`DataFrame`
|
|
:param param2: a parameter with different types
|
|
:type param2: :class:`int` or :class:`float` or :obj:`None`, *optional*
|
|
:param param3: a optional mapping
|
|
:type param3: :term:`dict-like <mapping>`, *optional*
|
|
:param param4: a optional parameter with different types
|
|
:type param4: :class:`int` or :class:`float` or :obj:`None`, *optional*
|
|
:param param5: a optional parameter with fixed values
|
|
:type param5: ``{"F", "C", "N"}``, *optional*
|
|
:param param6: different default format
|
|
:type param6: :class:`int`, *default* :obj:`None`
|
|
:param param7: a optional mapping
|
|
:type param7: :term:`mapping` of :term:`hashable` to :class:`str`, *optional*
|
|
:param param8: ellipsis
|
|
:type param8: :obj:`... <Ellipsis>` or :obj:`Ellipsis`
|
|
:param param9: a parameter with tuple of list of int
|
|
:type param9: :class:`tuple` of :class:`list` of :class:`int`
|
|
""")
|
|
translations = {
|
|
'dict-like': ':term:`dict-like <mapping>`',
|
|
'mapping': ':term:`mapping`',
|
|
'hashable': ':term:`hashable`',
|
|
}
|
|
config = Config(
|
|
napoleon_use_param=True,
|
|
napoleon_use_rtype=True,
|
|
napoleon_preprocess_types=True,
|
|
napoleon_type_aliases=translations,
|
|
)
|
|
actual = NumpyDocstring(docstring, config)
|
|
assert str(actual) == expected
|
|
|
|
def test_token_type_invalid(self, app):
|
|
tokens = (
|
|
'{1, 2',
|
|
'}',
|
|
"'abc",
|
|
"def'",
|
|
'"ghi',
|
|
'jkl"',
|
|
)
|
|
errors = (
|
|
r'.+: invalid value set \(missing closing brace\):',
|
|
r'.+: invalid value set \(missing opening brace\):',
|
|
r'.+: malformed string literal \(missing closing quote\):',
|
|
r'.+: malformed string literal \(missing opening quote\):',
|
|
r'.+: malformed string literal \(missing closing quote\):',
|
|
r'.+: malformed string literal \(missing opening quote\):',
|
|
)
|
|
for token, error in zip(tokens, errors, strict=True):
|
|
try:
|
|
_token_type(token)
|
|
finally:
|
|
raw_warnings = app.warning.getvalue()
|
|
warnings = [w for w in raw_warnings.split('\n') if w.strip()]
|
|
|
|
assert len(warnings) == 1
|
|
assert re.compile(error).match(warnings[0])
|
|
app.warning.truncate(0)
|
|
|
|
@pytest.mark.parametrize(
|
|
('name', 'expected'),
|
|
[
|
|
('x, y, z', 'x, y, z'),
|
|
('*args, **kwargs', r'\*args, \*\*kwargs'),
|
|
('*x, **y', r'\*x, \*\*y'),
|
|
],
|
|
)
|
|
def test_escape_args_and_kwargs(self, name, expected):
|
|
numpy_docstring = NumpyDocstring('')
|
|
actual = numpy_docstring._escape_args_and_kwargs(name)
|
|
|
|
assert actual == expected
|
|
|
|
def test_pep526_annotations(self):
|
|
# test class attributes annotations
|
|
config = Config(
|
|
napoleon_attr_annotations=True,
|
|
)
|
|
actual = NumpyDocstring(
|
|
cleandoc(PEP526NumpyClass.__doc__),
|
|
config,
|
|
app=None,
|
|
what='class',
|
|
obj=PEP526NumpyClass,
|
|
)
|
|
expected = """\
|
|
Sample class with PEP 526 annotations and numpy docstring
|
|
|
|
.. attribute:: attr1
|
|
|
|
Attr1 description
|
|
|
|
:type: int
|
|
|
|
.. attribute:: attr2
|
|
|
|
Attr2 description
|
|
|
|
:type: str
|
|
"""
|
|
print(actual)
|
|
assert str(actual) == expected
|
|
|
|
|
|
@pytest.mark.sphinx(
|
|
'text',
|
|
testroot='ext-napoleon',
|
|
confoverrides={
|
|
'autodoc_typehints': 'description',
|
|
'autodoc_typehints_description_target': 'all',
|
|
},
|
|
)
|
|
def test_napoleon_and_autodoc_typehints_description_all(app):
|
|
app.build()
|
|
content = (app.outdir / 'typehints.txt').read_text(encoding='utf-8')
|
|
assert content == (
|
|
'typehints\n'
|
|
'*********\n'
|
|
'\n'
|
|
'mypackage.typehints.hello(x, *args, **kwargs)\n'
|
|
'\n'
|
|
' Parameters:\n'
|
|
' * **x** (*int*) -- X\n'
|
|
'\n'
|
|
' * ***args** (*int*) -- Additional arguments.\n'
|
|
'\n'
|
|
' * ****kwargs** (*int*) -- Extra arguments.\n'
|
|
'\n'
|
|
' Return type:\n'
|
|
' None\n'
|
|
)
|
|
|
|
|
|
@pytest.mark.sphinx(
|
|
'text',
|
|
testroot='ext-napoleon',
|
|
confoverrides={
|
|
'autodoc_typehints': 'description',
|
|
'autodoc_typehints_description_target': 'documented_params',
|
|
},
|
|
)
|
|
def test_napoleon_and_autodoc_typehints_description_documented_params(app):
|
|
app.build()
|
|
content = (app.outdir / 'typehints.txt').read_text(encoding='utf-8')
|
|
assert content == (
|
|
'typehints\n'
|
|
'*********\n'
|
|
'\n'
|
|
'mypackage.typehints.hello(x, *args, **kwargs)\n'
|
|
'\n'
|
|
' Parameters:\n'
|
|
' * **x** (*int*) -- X\n'
|
|
'\n'
|
|
' * ***args** (*int*) -- Additional arguments.\n'
|
|
'\n'
|
|
' * ****kwargs** (*int*) -- Extra arguments.\n'
|
|
)
|
|
|
|
|
|
@pytest.mark.sphinx('html', testroot='ext-napoleon-paramtype', freshenv=True)
|
|
def test_napoleon_keyword_and_paramtype(app, tmp_path):
|
|
inv_file = tmp_path / 'objects.inv'
|
|
inv_file.write_bytes(
|
|
b"""\
|
|
# Sphinx inventory version 2
|
|
# Project: Intersphinx Test
|
|
# Version: 42
|
|
# The remainder of this file is compressed using zlib.
|
|
"""
|
|
+ zlib.compress(b"""\
|
|
None py:data 1 none.html -
|
|
list py:class 1 list.html -
|
|
int py:class 1 int.html -
|
|
""")
|
|
) # NoQA: W291
|
|
app.config.intersphinx_mapping = {'python': ('127.0.0.1:5555', str(inv_file))}
|
|
validate_intersphinx_mapping(app, app.config)
|
|
load_mappings(app)
|
|
|
|
app.build(force_all=True)
|
|
|
|
etree = etree_parse(app.outdir / 'index.html')
|
|
|
|
for name, typename in product(
|
|
('keyword', 'kwarg', 'kwparam'),
|
|
('paramtype', 'kwtype'),
|
|
):
|
|
param = f'{name}_{typename}'
|
|
li_ = list(etree.findall(f'.//li/p/strong[.="{param}"]/../..'))
|
|
assert len(li_) == 1
|
|
li = li_[0]
|
|
|
|
text = li.text or ''.join(li.itertext())
|
|
assert text == f'{param} (list[int]) \u2013 some param'
|
|
|
|
a_ = list(li.findall('.//a[@class="reference external"]'))
|
|
|
|
assert len(a_) == 2
|
|
for a, uri in zip(a_, ('list.html', 'int.html'), strict=True):
|
|
assert a.attrib['href'] == f'127.0.0.1:5555/{uri}'
|
|
assert a.attrib['title'] == '(in Intersphinx Test v42)'
|