summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES6
-rw-r--r--doc/extdev/deprecated.rst15
-rw-r--r--sphinx/domains/std.py2
-rw-r--r--sphinx/ext/autodoc/__init__.py25
-rw-r--r--sphinx/ext/napoleon/docstring.py4
-rw-r--r--sphinx/util/inspect.py111
-rw-r--r--tests/roots/test-ext-autodoc/target/pep570.py5
-rw-r--r--tests/test_autodoc.py9
-rw-r--r--tests/test_ext_napoleon_docstring.py3
-rw-r--r--tests/test_util_inspect.py185
10 files changed, 238 insertions, 127 deletions
diff --git a/CHANGES b/CHANGES
index 968e4a24c..9a481b588 100644
--- a/CHANGES
+++ b/CHANGES
@@ -21,9 +21,7 @@ Deprecated
* ``sphinx.roles.Index``
* ``sphinx.util.detect_encoding()``
* ``sphinx.util.get_module_source()``
-* ``sphinx.util.inspect.Signature.format_annotation()``
-* ``sphinx.util.inspect.Signature.format_annotation_new()``
-* ``sphinx.util.inspect.Signature.format_annotation_old()``
+* ``sphinx.util.inspect.Signature``
Features added
--------------
@@ -36,6 +34,7 @@ Features added
* #6696: html: ``:scale:`` option of image/figure directive not working for SVG
images (imagesize-1.2.0 or above is required)
* #6994: imgconverter: Support illustrator file (.ai) to .png conversion
+* autodoc: Support Positional-Only Argument separator (PEP-570 compliant)
* SphinxTranslator now calls visitor/departure method for super node class if
visitor/departure method for original node class not found
@@ -47,6 +46,7 @@ Bugs fixed
* #6961: latex: warning for babel shown twice
* #6559: Wrong node-ids are generated in glossary directive
* #6986: apidoc: misdetects module name for .so file inside module
+* #6999: napoleon: fails to parse tilde in :exc: role
Testing
--------
diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst
index ec6db2c16..6c2b05816 100644
--- a/doc/extdev/deprecated.rst
+++ b/doc/extdev/deprecated.rst
@@ -81,20 +81,11 @@ The following is a list of deprecated interfaces.
- 4.0
- N/A
- * - ``sphinx.util.inspect.Signature.format_annotation()``
+ * - ``sphinx.util.inspect.Signature``
- 2.4
- 4.0
- - ``sphinx.util.typing.stringify()``
-
- * - ``sphinx.util.inspect.Signature.format_annotation_new()``
- - 2.4
- - 4.0
- - ``sphinx.util.typing.stringify()``
-
- * - ``sphinx.util.inspect.Signature.format_annotation_old()``
- - 2.4
- - 4.0
- - ``sphinx.util.typing.stringify()``
+ - ``sphinx.util.inspect.signature`` and
+ ``sphinx.util.inspect.stringify_signature()``
* - ``sphinx.builders.gettext.POHEADER``
- 2.3
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 90c03c9a4..c109b763b 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -254,7 +254,7 @@ def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index
if node_id:
# node_id is given from outside (mainly i18n module), use it forcedly
- pass
+ term['ids'].append(node_id)
elif document:
node_id = make_id(env, document, 'term', termtext)
term['ids'].append(node_id)
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 8c5ace92a..2124b2d25 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -33,7 +33,7 @@ from sphinx.util import logging
from sphinx.util import rpartition
from sphinx.util.docstrings import prepare_docstring
from sphinx.util.inspect import (
- Signature, getdoc, object_description, safe_getattr, safe_getmembers
+ getdoc, object_description, safe_getattr, safe_getmembers, stringify_signature
)
if False:
@@ -983,9 +983,10 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
not inspect.isbuiltin(self.object) and
not inspect.isclass(self.object) and
hasattr(self.object, '__call__')):
- args = Signature(self.object.__call__).format_args(**kwargs)
+ sig = inspect.signature(self.object.__call__)
else:
- args = Signature(self.object).format_args(**kwargs)
+ sig = inspect.signature(self.object)
+ args = stringify_signature(sig, **kwargs)
except TypeError:
if (inspect.is_builtin_class_method(self.object, '__new__') and
inspect.is_builtin_class_method(self.object, '__init__')):
@@ -995,11 +996,11 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
# typing) we try to use the constructor signature as function
# signature without the first argument.
try:
- sig = Signature(self.object.__new__, bound_method=True, has_retval=False)
- args = sig.format_args(**kwargs)
+ sig = inspect.signature(self.object.__new__, bound_method=True)
+ args = stringify_signature(sig, show_return_annotation=False, **kwargs)
except TypeError:
- sig = Signature(self.object.__init__, bound_method=True, has_retval=False)
- args = sig.format_args(**kwargs)
+ sig = inspect.signature(self.object.__init__, bound_method=True)
+ args = stringify_signature(sig, show_return_annotation=False, **kwargs)
# escape backslashes for reST
args = args.replace('\\', '\\\\')
@@ -1080,8 +1081,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)):
return None
try:
- sig = Signature(initmeth, bound_method=True, has_retval=False)
- return sig.format_args(**kwargs)
+ sig = inspect.signature(initmeth, bound_method=True)
+ return stringify_signature(sig, show_return_annotation=False, **kwargs)
except TypeError:
# still not possible: happens e.g. for old-style classes
# with __init__ in C
@@ -1283,9 +1284,11 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
# can never get arguments of a C function or method
return None
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
- args = Signature(self.object, bound_method=False).format_args(**kwargs)
+ sig = inspect.signature(self.object, bound_method=False)
else:
- args = Signature(self.object, bound_method=True).format_args(**kwargs)
+ sig = inspect.signature(self.object, bound_method=True)
+ args = stringify_signature(sig, **kwargs)
+
# escape backslashes for reST
args = args.replace('\\', '\\\\')
return args
diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py
index a06a79cea..81f2496cb 100644
--- a/sphinx/ext/napoleon/docstring.py
+++ b/sphinx/ext/napoleon/docstring.py
@@ -101,8 +101,8 @@ class GoogleDocstring:
"""
- _name_rgx = re.compile(r"^\s*((?::(?P<role>\S+):)?`(?P<name>[a-zA-Z0-9_.-]+)`|"
- r" (?P<name2>[a-zA-Z0-9_.-]+))\s*", re.X)
+ _name_rgx = re.compile(r"^\s*((?::(?P<role>\S+):)?`(?P<name>~?[a-zA-Z0-9_.-]+)`|"
+ r" (?P<name2>~?[a-zA-Z0-9_.-]+))\s*", re.X)
def __init__(self, docstring: Union[str, List[str]], config: SphinxConfig = None,
app: Sphinx = None, what: str = '', name: str = '',
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 967a10d51..8d3392500 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -315,6 +315,108 @@ def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls
+def signature(subject: Callable, bound_method: bool = False) -> inspect.Signature:
+ """Return a Signature object for the given *subject*.
+
+ :param bound_method: Specify *subject* is a bound method or not
+ """
+ # check subject is not a built-in class (ex. int, str)
+ if (isinstance(subject, type) and
+ is_builtin_class_method(subject, "__new__") and
+ is_builtin_class_method(subject, "__init__")):
+ raise TypeError("can't compute signature for built-in type {}".format(subject))
+
+ try:
+ signature = inspect.signature(subject)
+ parameters = list(signature.parameters.values())
+ return_annotation = signature.return_annotation
+ except IndexError:
+ # Until python 3.6.4, cpython has been crashed on inspection for
+ # partialmethods not having any arguments.
+ # https://bugs.python.org/issue33009
+ if hasattr(subject, '_partialmethod'):
+ parameters = []
+ return_annotation = inspect.Parameter.empty
+ else:
+ raise
+
+ try:
+ # Update unresolved annotations using ``get_type_hints()``.
+ annotations = typing.get_type_hints(subject)
+ for i, param in enumerate(parameters):
+ if isinstance(param.annotation, str) and param.name in annotations:
+ parameters[i] = param.replace(annotation=annotations[param.name])
+ if 'return' in annotations:
+ return_annotation = annotations['return']
+ except Exception:
+ # ``get_type_hints()`` does not support some kind of objects like partial,
+ # ForwardRef and so on.
+ pass
+
+ if bound_method:
+ if inspect.ismethod(subject):
+ # ``inspect.signature()`` considers the subject is a bound method and removes
+ # first argument from signature. Therefore no skips are needed here.
+ pass
+ else:
+ if len(parameters) > 0:
+ parameters.pop(0)
+
+ return inspect.Signature(parameters, return_annotation=return_annotation)
+
+
+def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
+ show_return_annotation: bool = True) -> str:
+ """Stringify a Signature object.
+
+ :param show_annotation: Show annotation in result
+ """
+ args = []
+ last_kind = None
+ for param in sig.parameters.values():
+ if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY:
+ # PEP-570: Separator for Positional Only Parameter: /
+ args.append('/')
+ elif param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD,
+ param.POSITIONAL_ONLY,
+ None):
+ # PEP-3102: Separator for Keyword Only Parameter: *
+ args.append('*')
+
+ arg = StringIO()
+ if param.kind == param.VAR_POSITIONAL:
+ arg.write('*' + param.name)
+ elif param.kind == param.VAR_KEYWORD:
+ arg.write('**' + param.name)
+ else:
+ arg.write(param.name)
+
+ if show_annotation and param.annotation is not param.empty:
+ arg.write(': ')
+ arg.write(stringify_annotation(param.annotation))
+ if param.default is not param.empty:
+ if show_annotation and param.annotation is not param.empty:
+ arg.write(' = ')
+ else:
+ arg.write('=')
+ arg.write(object_description(param.default))
+
+ args.append(arg.getvalue())
+ last_kind = param.kind
+
+ if last_kind == inspect.Parameter.POSITIONAL_ONLY:
+ # PEP-570: Separator for Positional Only Parameter: /
+ args.append('/')
+
+ if (sig.return_annotation is inspect.Parameter.empty or
+ show_annotation is False or
+ show_return_annotation is False):
+ return '(%s)' % ', '.join(args)
+ else:
+ annotation = stringify_annotation(sig.return_annotation)
+ return '(%s) -> %s' % (', '.join(args), annotation)
+
+
class Parameter:
"""Fake parameter class for python2."""
POSITIONAL_ONLY = 0
@@ -342,6 +444,9 @@ class Signature:
def __init__(self, subject: Callable, bound_method: bool = False,
has_retval: bool = True) -> None:
+ warnings.warn('sphinx.util.inspect.Signature() is deprecated',
+ RemovedInSphinx40Warning)
+
# check subject is not a built-in class (ex. int, str)
if (isinstance(subject, type) and
is_builtin_class_method(subject, "__new__") and
@@ -467,20 +572,14 @@ class Signature:
def format_annotation(self, annotation: Any) -> str:
"""Return formatted representation of a type annotation."""
- warnings.warn('format_annotation() is deprecated',
- RemovedInSphinx40Warning)
return stringify_annotation(annotation)
def format_annotation_new(self, annotation: Any) -> str:
"""format_annotation() for py37+"""
- warnings.warn('format_annotation_new() is deprecated',
- RemovedInSphinx40Warning)
return stringify_annotation(annotation)
def format_annotation_old(self, annotation: Any) -> str:
"""format_annotation() for py36 or below"""
- warnings.warn('format_annotation_old() is deprecated',
- RemovedInSphinx40Warning)
return stringify_annotation(annotation)
diff --git a/tests/roots/test-ext-autodoc/target/pep570.py b/tests/roots/test-ext-autodoc/target/pep570.py
new file mode 100644
index 000000000..904692eeb
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/pep570.py
@@ -0,0 +1,5 @@
+def foo(a, b, /, c, d):
+ pass
+
+def bar(a, b, /):
+ pass
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index 35e0e5367..bd13cf6c2 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -1318,13 +1318,13 @@ def test_partialmethod(app):
' refs: https://docs.python.jp/3/library/functools.html#functools.partialmethod',
' ',
' ',
- ' .. py:method:: Cell.set_alive() -> None',
+ ' .. py:method:: Cell.set_alive()',
' :module: target.partialmethod',
' ',
' Make a cell alive.',
' ',
' ',
- ' .. py:method:: Cell.set_dead() -> None',
+ ' .. py:method:: Cell.set_dead()',
' :module: target.partialmethod',
' ',
' Make a cell dead.',
@@ -1336,11 +1336,6 @@ def test_partialmethod(app):
' Update state of cell to *state*.',
' ',
]
- if (sys.version_info < (3, 5, 4) or
- (3, 6, 5) <= sys.version_info < (3, 7) or
- (3, 7, 0, 'beta', 3) <= sys.version_info):
- # TODO: this condition should be updated after 3.7-final release.
- expected = '\n'.join(expected).replace(' -> None', '').split('\n')
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.partialmethod.Cell', options)
diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py
index 9547a9d2b..2ce754eff 100644
--- a/tests/test_ext_napoleon_docstring.py
+++ b/tests/test_ext_napoleon_docstring.py
@@ -479,6 +479,8 @@ Raises:
If the dimensions couldn't be parsed.
`InvalidArgumentsError`
If the arguments are invalid.
+ :exc:`~ValueError`
+ If the arguments are wrong.
""", """
Example Function
@@ -488,6 +490,7 @@ Example Function
: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.
"""),
################################
("""
diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py
index 2f4631965..5e035c6a9 100644
--- a/tests/test_util_inspect.py
+++ b/tests/test_util_inspect.py
@@ -17,6 +17,7 @@ import types
import pytest
from sphinx.util import inspect
+from sphinx.util.inspect import stringify_signature
def test_getargspec():
@@ -89,39 +90,39 @@ def test_getargspec_bound_methods():
assert expected_bound == inspect.getargspec(wrapped_bound_method)
-def test_Signature():
+def test_signature():
# literals
with pytest.raises(TypeError):
- inspect.Signature(1)
+ inspect.signature(1)
with pytest.raises(TypeError):
- inspect.Signature('')
+ inspect.signature('')
# builitin classes
with pytest.raises(TypeError):
- inspect.Signature(int)
+ inspect.signature(int)
with pytest.raises(TypeError):
- inspect.Signature(str)
+ inspect.signature(str)
# normal function
def func(a, b, c=1, d=2, *e, **f):
pass
- sig = inspect.Signature(func).format_args()
+ sig = inspect.stringify_signature(inspect.signature(func))
assert sig == '(a, b, c=1, d=2, *e, **f)'
-def test_Signature_partial():
+def test_signature_partial():
def fun(a, b, c=1, d=2):
pass
p = functools.partial(fun, 10, c=11)
- sig = inspect.Signature(p).format_args()
- assert sig == '(b, *, c=11, d=2)'
+ sig = inspect.signature(p)
+ assert stringify_signature(sig) == '(b, *, c=11, d=2)'
-def test_Signature_methods():
+def test_signature_methods():
class Foo:
def meth1(self, arg1, **kwargs):
pass
@@ -139,36 +140,36 @@ def test_Signature_methods():
pass
# unbound method
- sig = inspect.Signature(Foo.meth1).format_args()
- assert sig == '(self, arg1, **kwargs)'
+ sig = inspect.signature(Foo.meth1)
+ assert stringify_signature(sig) == '(self, arg1, **kwargs)'
- sig = inspect.Signature(Foo.meth1, bound_method=True).format_args()
- assert sig == '(arg1, **kwargs)'
+ sig = inspect.signature(Foo.meth1, bound_method=True)
+ assert stringify_signature(sig) == '(arg1, **kwargs)'
# bound method
- sig = inspect.Signature(Foo().meth1).format_args()
- assert sig == '(arg1, **kwargs)'
+ sig = inspect.signature(Foo().meth1)
+ assert stringify_signature(sig) == '(arg1, **kwargs)'
# class method
- sig = inspect.Signature(Foo.meth2).format_args()
- assert sig == '(arg1, *args, **kwargs)'
+ sig = inspect.signature(Foo.meth2)
+ assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
- sig = inspect.Signature(Foo().meth2).format_args()
- assert sig == '(arg1, *args, **kwargs)'
+ sig = inspect.signature(Foo().meth2)
+ assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
# static method
- sig = inspect.Signature(Foo.meth3).format_args()
- assert sig == '(arg1, *args, **kwargs)'
+ sig = inspect.signature(Foo.meth3)
+ assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
- sig = inspect.Signature(Foo().meth3).format_args()
- assert sig == '(arg1, *args, **kwargs)'
+ sig = inspect.signature(Foo().meth3)
+ assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
# wrapped bound method
- sig = inspect.Signature(wrapped_bound_method).format_args()
- assert sig == '(arg1, **kwargs)'
+ sig = inspect.signature(wrapped_bound_method)
+ assert stringify_signature(sig) == '(arg1, **kwargs)'
-def test_Signature_partialmethod():
+def test_signature_partialmethod():
from functools import partialmethod
class Foo:
@@ -183,115 +184,129 @@ def test_Signature_partialmethod():
baz = partialmethod(meth2, 1, 2)
subject = Foo()
- sig = inspect.Signature(subject.foo).format_args()
- assert sig == '(arg3=None, arg4=None)'
+ sig = inspect.signature(subject.foo)
+ assert stringify_signature(sig) == '(arg3=None, arg4=None)'
- sig = inspect.Signature(subject.bar).format_args()
- assert sig == '(arg2, *, arg3=3, arg4=None)'
+ sig = inspect.signature(subject.bar)
+ assert stringify_signature(sig) == '(arg2, *, arg3=3, arg4=None)'
- sig = inspect.Signature(subject.baz).format_args()
- assert sig == '()'
+ sig = inspect.signature(subject.baz)
+ assert stringify_signature(sig) == '()'
-def test_Signature_annotations():
+def test_signature_annotations():
from typing_test_data import (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10,
f11, f12, f13, f14, f15, f16, f17, f18, f19, Node)
# Class annotations
- sig = inspect.Signature(f0).format_args()
- assert sig == '(x: int, y: numbers.Integral) -> None'
+ sig = inspect.signature(f0)
+ assert stringify_signature(sig) == '(x: int, y: numbers.Integral) -> None'
# Generic types with concrete parameters
- sig = inspect.Signature(f1).format_args()
- assert sig == '(x: List[int]) -> List[int]'
+ sig = inspect.signature(f1)
+ assert stringify_signature(sig) == '(x: List[int]) -> List[int]'
# TypeVars and generic types with TypeVars
- sig = inspect.Signature(f2).format_args()
- assert sig == '(x: List[T], y: List[T_co], z: T) -> List[T_contra]'
+ sig = inspect.signature(f2)
+ assert stringify_signature(sig) == '(x: List[T], y: List[T_co], z: T) -> List[T_contra]'
# Union types
- sig = inspect.Signature(f3).format_args()
- assert sig == '(x: Union[str, numbers.Integral]) -> None'
+ sig = inspect.signature(f3)
+ assert stringify_signature(sig) == '(x: Union[str, numbers.Integral]) -> None'
# Quoted annotations
- sig = inspect.Signature(f4).format_args()
- assert sig == '(x: str, y: str) -> None'
+ sig = inspect.signature(f4)
+ assert stringify_signature(sig) == '(x: str, y: str) -> None'
# Keyword-only arguments
- sig = inspect.Signature(f5).format_args()
- assert sig == '(x: int, *, y: str, z: str) -> None'
+ sig = inspect.signature(f5)
+ assert stringify_signature(sig) == '(x: int, *, y: str, z: str) -> None'
# Keyword-only arguments with varargs
- sig = inspect.Signature(f6).format_args()
- assert sig == '(x: int, *args, y: str, z: str) -> None'
+ sig = inspect.signature(f6)
+ assert stringify_signature(sig) == '(x: int, *args, y: str, z: str) -> None'
# Space around '=' for defaults
- sig = inspect.Signature(f7).format_args()
- assert sig == '(x: int = None, y: dict = {}) -> None'
+ sig = inspect.signature(f7)
+ assert stringify_signature(sig) == '(x: int = None, y: dict = {}) -> None'
# Callable types
- sig = inspect.Signature(f8).format_args()
- assert sig == '(x: Callable[[int, str], int]) -> None'
+ sig = inspect.signature(f8)
+ assert stringify_signature(sig) == '(x: Callable[[int, str], int]) -> None'
- sig = inspect.Signature(f9).format_args()
- assert sig == '(x: Callable) -> None'
+ sig = inspect.signature(f9)
+ assert stringify_signature(sig) == '(x: Callable) -> None'
# Tuple types
- sig = inspect.Signature(f10).format_args()
- assert sig == '(x: Tuple[int, str], y: Tuple[int, ...]) -> None'
+ sig = inspect.signature(f10)
+ assert stringify_signature(sig) == '(x: Tuple[int, str], y: Tuple[int, ...]) -> None'
# Instance annotations
- sig = inspect.Signature(f11).format_args()
- assert sig == '(x: CustomAnnotation, y: 123) -> None'
-
- # has_retval=False
- sig = inspect.Signature(f11, has_retval=False).format_args()
- assert sig == '(x: CustomAnnotation, y: 123)'
+ sig = inspect.signature(f11)
+ assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None'
# tuple with more than two items
- sig = inspect.Signature(f12).format_args()
- assert sig == '() -> Tuple[int, str, int]'
+ sig = inspect.signature(f12)
+ assert stringify_signature(sig) == '() -> Tuple[int, str, int]'
# optional
- sig = inspect.Signature(f13).format_args()
- assert sig == '() -> Optional[str]'
+ sig = inspect.signature(f13)
+ assert stringify_signature(sig) == '() -> Optional[str]'
# Any
- sig = inspect.Signature(f14).format_args()
- assert sig == '() -> Any'
+ sig = inspect.signature(f14)
+ assert stringify_signature(sig) == '() -> Any'
# ForwardRef
- sig = inspect.Signature(f15).format_args()
- assert sig == '(x: Unknown, y: int) -> Any'
+ sig = inspect.signature(f15)
+ assert stringify_signature(sig) == '(x: Unknown, y: int) -> Any'
# keyword only arguments (1)
- sig = inspect.Signature(f16).format_args()
- assert sig == '(arg1, arg2, *, arg3=None, arg4=None)'
+ sig = inspect.signature(f16)
+ assert stringify_signature(sig) == '(arg1, arg2, *, arg3=None, arg4=None)'
# keyword only arguments (2)
- sig = inspect.Signature(f17).format_args()
- assert sig == '(*, arg3, arg4)'
+ sig = inspect.signature(f17)
+ assert stringify_signature(sig) == '(*, arg3, arg4)'
- sig = inspect.Signature(f18).format_args()
- assert sig == '(self, arg1: Union[int, Tuple] = 10) -> List[Dict]'
+ sig = inspect.signature(f18)
+ assert stringify_signature(sig) == '(self, arg1: Union[int, Tuple] = 10) -> List[Dict]'
# annotations for variadic and keyword parameters
- sig = inspect.Signature(f19).format_args()
- assert sig == '(*args: int, **kwargs: str)'
+ sig = inspect.signature(f19)
+ assert stringify_signature(sig) == '(*args: int, **kwargs: str)'
# type hints by string
- sig = inspect.Signature(Node.children).format_args()
+ sig = inspect.signature(Node.children)
if (3, 5, 0) <= sys.version_info < (3, 5, 3):
- assert sig == '(self) -> List[Node]'
+ assert stringify_signature(sig) == '(self) -> List[Node]'
else:
- assert sig == '(self) -> List[typing_test_data.Node]'
+ assert stringify_signature(sig) == '(self) -> List[typing_test_data.Node]'
- sig = inspect.Signature(Node.__init__).format_args()
- assert sig == '(self, parent: Optional[Node]) -> None'
+ sig = inspect.signature(Node.__init__)
+ assert stringify_signature(sig) == '(self, parent: Optional[Node]) -> None'
# show_annotation is False
- sig = inspect.Signature(f7).format_args(show_annotation=False)
- assert sig == '(x=None, y={})'
+ sig = inspect.signature(f7)
+ assert stringify_signature(sig, show_annotation=False) == '(x=None, y={})'
+
+ # show_return_annotation is False
+ sig = inspect.signature(f7)
+ assert stringify_signature(sig, show_return_annotation=False) == '(x: int = None, y: dict = {})'
+
+
+@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
+@pytest.mark.sphinx(testroot='ext-autodoc')
+def test_signature_annotations_py38(app):
+ from target.pep570 import foo, bar
+
+ # case: separator in the middle
+ sig = inspect.signature(foo)
+ assert stringify_signature(sig) == '(a, b, /, c, d)'
+
+ # case: separator at tail
+ sig = inspect.signature(bar)
+ assert stringify_signature(sig) == '(a, b, /)'
def test_safe_getattr_with_default():