mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #3870 from tk0miya/refactor_sphinx.util.inspect
Refactor sphinx.util.inspect
This commit is contained in:
commit
6ec959c026
@ -33,7 +33,7 @@ from sphinx.pycode import ModuleAnalyzer, PycodeError
|
|||||||
from sphinx.application import ExtensionError
|
from sphinx.application import ExtensionError
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.nodes import nested_parse_with_titles
|
from sphinx.util.nodes import nested_parse_with_titles
|
||||||
from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \
|
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
|
||||||
safe_getattr, object_description, is_builtin_class_method, \
|
safe_getattr, object_description, is_builtin_class_method, \
|
||||||
isenumclass, isenumattribute
|
isenumclass, isenumattribute
|
||||||
from sphinx.util.docstrings import prepare_docstring
|
from sphinx.util.docstrings import prepare_docstring
|
||||||
@ -1358,7 +1358,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
# cannot introspect arguments of a C function or method
|
# cannot introspect arguments of a C function or method
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
argspec = getargspec(self.object)
|
args = Signature(self.object).format_args()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
if (is_builtin_class_method(self.object, '__new__') and
|
if (is_builtin_class_method(self.object, '__new__') and
|
||||||
is_builtin_class_method(self.object, '__init__')):
|
is_builtin_class_method(self.object, '__init__')):
|
||||||
@ -1368,12 +1368,10 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
# typing) we try to use the constructor signature as function
|
# typing) we try to use the constructor signature as function
|
||||||
# signature without the first argument.
|
# signature without the first argument.
|
||||||
try:
|
try:
|
||||||
argspec = getargspec(self.object.__new__)
|
args = Signature(self.object.__new__, bound_method=True).format_args()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
argspec = getargspec(self.object.__init__)
|
args = Signature(self.object.__init__, bound_method=True).format_args()
|
||||||
if argspec[0]:
|
|
||||||
del argspec[0][0]
|
|
||||||
args = formatargspec(self.object, *argspec)
|
|
||||||
# escape backslashes for reST
|
# escape backslashes for reST
|
||||||
args = args.replace('\\', '\\\\')
|
args = args.replace('\\', '\\\\')
|
||||||
return args
|
return args
|
||||||
@ -1425,14 +1423,11 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)):
|
not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)):
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
argspec = getargspec(initmeth)
|
return Signature(initmeth, bound_method=True).format_args()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# still not possible: happens e.g. for old-style classes
|
# still not possible: happens e.g. for old-style classes
|
||||||
# with __init__ in C
|
# with __init__ in C
|
||||||
return None
|
return None
|
||||||
if argspec[0] and argspec[0][0] in ('cls', 'self'):
|
|
||||||
del argspec[0][0]
|
|
||||||
return formatargspec(initmeth, *argspec)
|
|
||||||
|
|
||||||
def format_signature(self):
|
def format_signature(self):
|
||||||
# type: () -> unicode
|
# type: () -> unicode
|
||||||
@ -1619,10 +1614,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
inspect.ismethoddescriptor(self.object):
|
inspect.ismethoddescriptor(self.object):
|
||||||
# can never get arguments of a C function or method
|
# can never get arguments of a C function or method
|
||||||
return None
|
return None
|
||||||
argspec = getargspec(self.object)
|
args = Signature(self.object, bound_method=True).format_args()
|
||||||
if argspec[0] and argspec[0][0] in ('cls', 'self'):
|
|
||||||
del argspec[0][0]
|
|
||||||
args = formatargspec(self.object, *argspec)
|
|
||||||
# escape backslashes for reST
|
# escape backslashes for reST
|
||||||
args = args.replace('\\', '\\\\')
|
args = args.replace('\\', '\\\\')
|
||||||
return args
|
return args
|
||||||
|
@ -8,21 +8,21 @@
|
|||||||
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import typing
|
||||||
|
import inspect
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
from six import PY3, binary_type
|
from six import PY2, PY3, StringIO, binary_type, string_types, itervalues
|
||||||
from six.moves import builtins
|
from six.moves import builtins
|
||||||
|
|
||||||
from sphinx.util import force_decode
|
from sphinx.util import force_decode
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
from typing import Any, Callable, List, Tuple, Type # NOQA
|
from typing import Any, Callable, Dict, List, Tuple, Type # NOQA
|
||||||
|
|
||||||
# this imports the standard library inspect module without resorting to
|
|
||||||
# relatively import this module
|
|
||||||
inspect = __import__('inspect')
|
|
||||||
|
|
||||||
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
|
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ else: # 2.7
|
|||||||
func = func.func
|
func = func.func
|
||||||
if not inspect.isfunction(func):
|
if not inspect.isfunction(func):
|
||||||
raise TypeError('%r is not a Python function' % func)
|
raise TypeError('%r is not a Python function' % func)
|
||||||
args, varargs, varkw = inspect.getargs(func.__code__)
|
args, varargs, varkw = inspect.getargs(func.__code__) # type: ignore
|
||||||
func_defaults = func.__defaults__
|
func_defaults = func.__defaults__
|
||||||
if func_defaults is None:
|
if func_defaults is None:
|
||||||
func_defaults = []
|
func_defaults = []
|
||||||
@ -231,3 +231,228 @@ def is_builtin_class_method(obj, attr_name):
|
|||||||
if not hasattr(builtins, safe_getattr(cls, '__name__', '')): # type: ignore
|
if not hasattr(builtins, safe_getattr(cls, '__name__', '')): # type: ignore
|
||||||
return False
|
return False
|
||||||
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls # type: ignore
|
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class Parameter(object):
|
||||||
|
"""Fake parameter class for python2."""
|
||||||
|
POSITIONAL_ONLY = 0
|
||||||
|
POSITIONAL_OR_KEYWORD = 1
|
||||||
|
VAR_POSITIONAL = 2
|
||||||
|
KEYWORD_ONLY = 3
|
||||||
|
VAR_KEYWORD = 4
|
||||||
|
empty = object()
|
||||||
|
|
||||||
|
def __init__(self, name, kind=POSITIONAL_OR_KEYWORD, default=empty):
|
||||||
|
# type: (str, int, Any) -> None
|
||||||
|
self.name = name
|
||||||
|
self.kind = kind
|
||||||
|
self.default = default
|
||||||
|
self.annotation = self.empty
|
||||||
|
|
||||||
|
|
||||||
|
class Signature(object):
|
||||||
|
"""The Signature object represents the call signature of a callable object and
|
||||||
|
its return annotation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, subject, bound_method=False):
|
||||||
|
# type: (Callable, bool) -> None
|
||||||
|
# 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))
|
||||||
|
|
||||||
|
self.subject = subject
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
self.signatures = inspect.signature(subject)
|
||||||
|
else:
|
||||||
|
self.argspec = getargspec(subject)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.annotations = typing.get_type_hints(subject)
|
||||||
|
except:
|
||||||
|
self.annotations = None
|
||||||
|
|
||||||
|
if bound_method:
|
||||||
|
# client gives a hint that the subject is a bound method
|
||||||
|
|
||||||
|
if PY3 and inspect.ismethod(subject):
|
||||||
|
# inspect.signature already considers the subject is bound method.
|
||||||
|
# So it is not need to skip first argument.
|
||||||
|
self.skip_first_argument = False
|
||||||
|
else:
|
||||||
|
self.skip_first_argument = True
|
||||||
|
else:
|
||||||
|
if PY3:
|
||||||
|
# inspect.signature recognizes type of method properly without any hints
|
||||||
|
self.skip_first_argument = False
|
||||||
|
else:
|
||||||
|
# check the subject is bound method or not
|
||||||
|
self.skip_first_argument = inspect.ismethod(subject) and subject.__self__ # type: ignore # NOQA
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parameters(self):
|
||||||
|
# type: () -> Dict
|
||||||
|
if PY3:
|
||||||
|
return self.signatures.parameters
|
||||||
|
else:
|
||||||
|
params = OrderedDict() # type: Dict
|
||||||
|
positionals = len(self.argspec.args) - len(self.argspec.defaults)
|
||||||
|
for i, arg in enumerate(self.argspec.args):
|
||||||
|
if i < positionals:
|
||||||
|
params[arg] = Parameter(arg)
|
||||||
|
else:
|
||||||
|
default = self.argspec.defaults[i - positionals]
|
||||||
|
params[arg] = Parameter(arg, default=default)
|
||||||
|
if self.argspec.varargs:
|
||||||
|
params[self.argspec.varargs] = Parameter(self.argspec.varargs,
|
||||||
|
Parameter.VAR_POSITIONAL)
|
||||||
|
if self.argspec.keywords:
|
||||||
|
params[self.argspec.keywords] = Parameter(self.argspec.keywords,
|
||||||
|
Parameter.VAR_KEYWORD)
|
||||||
|
return params
|
||||||
|
|
||||||
|
@property
|
||||||
|
def return_annotation(self):
|
||||||
|
# type: () -> Any
|
||||||
|
if PY3:
|
||||||
|
return self.signatures.return_annotation
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def format_args(self):
|
||||||
|
# type: () -> unicode
|
||||||
|
args = []
|
||||||
|
last_kind = None
|
||||||
|
for i, param in enumerate(itervalues(self.parameters)):
|
||||||
|
# skip first argument if subject is bound method
|
||||||
|
if self.skip_first_argument and i == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
arg = StringIO()
|
||||||
|
|
||||||
|
# insert '*' between POSITIONAL args and KEYWORD_ONLY args::
|
||||||
|
# func(a, b, *, c, d):
|
||||||
|
if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD,
|
||||||
|
param.POSITIONAL_ONLY):
|
||||||
|
args.append('*')
|
||||||
|
|
||||||
|
if param.kind in (param.POSITIONAL_ONLY,
|
||||||
|
param.POSITIONAL_OR_KEYWORD,
|
||||||
|
param.KEYWORD_ONLY):
|
||||||
|
arg.write(param.name)
|
||||||
|
if param.annotation is not param.empty:
|
||||||
|
if isinstance(param.annotation, string_types) and \
|
||||||
|
param.name in self.annotations:
|
||||||
|
arg.write(': ')
|
||||||
|
arg.write(self.format_annotation(self.annotations[param.name]))
|
||||||
|
else:
|
||||||
|
arg.write(': ')
|
||||||
|
arg.write(self.format_annotation(param.annotation))
|
||||||
|
if param.default is not param.empty:
|
||||||
|
if param.annotation is param.empty:
|
||||||
|
arg.write('=')
|
||||||
|
arg.write(object_description(param.default)) # type: ignore
|
||||||
|
else:
|
||||||
|
arg.write(' = ')
|
||||||
|
arg.write(object_description(param.default)) # type: ignore
|
||||||
|
elif param.kind == param.VAR_POSITIONAL:
|
||||||
|
arg.write('*')
|
||||||
|
arg.write(param.name)
|
||||||
|
elif param.kind == param.VAR_KEYWORD:
|
||||||
|
arg.write('**')
|
||||||
|
arg.write(param.name)
|
||||||
|
|
||||||
|
args.append(arg.getvalue())
|
||||||
|
last_kind = param.kind
|
||||||
|
|
||||||
|
if PY2 or self.return_annotation is inspect.Parameter.empty:
|
||||||
|
return '(%s)' % ', '.join(args)
|
||||||
|
else:
|
||||||
|
if isinstance(self.return_annotation, string_types) and \
|
||||||
|
'return' in self.annotations:
|
||||||
|
annotation = self.format_annotation(self.annotations['return'])
|
||||||
|
else:
|
||||||
|
annotation = self.format_annotation(self.return_annotation)
|
||||||
|
|
||||||
|
return '(%s) -> %s' % (', '.join(args), annotation)
|
||||||
|
|
||||||
|
def format_annotation(self, annotation):
|
||||||
|
# type: (Any) -> str
|
||||||
|
"""Return formatted representation of a type annotation.
|
||||||
|
|
||||||
|
Show qualified names for types and additional details for types from
|
||||||
|
the ``typing`` module.
|
||||||
|
|
||||||
|
Displaying complex types from ``typing`` relies on its private API.
|
||||||
|
"""
|
||||||
|
if isinstance(annotation, string_types):
|
||||||
|
return annotation # type: ignore
|
||||||
|
if isinstance(annotation, typing.TypeVar): # type: ignore
|
||||||
|
return annotation.__name__
|
||||||
|
if annotation == Ellipsis:
|
||||||
|
return '...'
|
||||||
|
if not isinstance(annotation, type):
|
||||||
|
return repr(annotation)
|
||||||
|
|
||||||
|
qualified_name = (annotation.__module__ + '.' + annotation.__qualname__ # type: ignore
|
||||||
|
if annotation else repr(annotation))
|
||||||
|
|
||||||
|
if annotation.__module__ == 'builtins':
|
||||||
|
return annotation.__qualname__ # type: ignore
|
||||||
|
elif isinstance(annotation, typing.GenericMeta):
|
||||||
|
# In Python 3.5.2+, all arguments are stored in __args__,
|
||||||
|
# whereas __parameters__ only contains generic parameters.
|
||||||
|
#
|
||||||
|
# Prior to Python 3.5.2, __args__ is not available, and all
|
||||||
|
# arguments are in __parameters__.
|
||||||
|
params = None
|
||||||
|
if hasattr(annotation, '__args__'):
|
||||||
|
if annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA
|
||||||
|
params = annotation.__args__ # type: ignore
|
||||||
|
else: # typing.Callable
|
||||||
|
args = ', '.join(self.format_annotation(arg) for arg
|
||||||
|
in annotation.__args__[:-1]) # type: ignore
|
||||||
|
result = self.format_annotation(annotation.__args__[-1]) # type: ignore
|
||||||
|
return '%s[[%s], %s]' % (qualified_name, args, result)
|
||||||
|
elif hasattr(annotation, '__parameters__'):
|
||||||
|
params = annotation.__parameters__ # type: ignore
|
||||||
|
if params is not None:
|
||||||
|
param_str = ', '.join(self.format_annotation(p) for p in params)
|
||||||
|
return '%s[%s]' % (qualified_name, param_str)
|
||||||
|
elif (hasattr(typing, 'UnionMeta') and # for py35 or below
|
||||||
|
isinstance(annotation, typing.UnionMeta) and # type: ignore
|
||||||
|
hasattr(annotation, '__union_params__')):
|
||||||
|
params = annotation.__union_params__ # type: ignore
|
||||||
|
if params is not None:
|
||||||
|
param_str = ', '.join(self.format_annotation(p) for p in params)
|
||||||
|
return '%s[%s]' % (qualified_name, param_str)
|
||||||
|
elif (isinstance(annotation, typing.CallableMeta) and # type: ignore
|
||||||
|
getattr(annotation, '__args__', None) is not None and
|
||||||
|
hasattr(annotation, '__result__')):
|
||||||
|
# Skipped in the case of plain typing.Callable
|
||||||
|
args = annotation.__args__ # type: ignore
|
||||||
|
if args is None:
|
||||||
|
return qualified_name
|
||||||
|
elif args is Ellipsis:
|
||||||
|
args_str = '...'
|
||||||
|
else:
|
||||||
|
formatted_args = (self.format_annotation(a) for a in args)
|
||||||
|
args_str = '[%s]' % ', '.join(formatted_args)
|
||||||
|
return '%s[%s, %s]' % (qualified_name,
|
||||||
|
args_str,
|
||||||
|
self.format_annotation(annotation.__result__)) # type: ignore # NOQA
|
||||||
|
elif (isinstance(annotation, typing.TupleMeta) and # type: ignore
|
||||||
|
hasattr(annotation, '__tuple_params__') and
|
||||||
|
hasattr(annotation, '__tuple_use_ellipsis__')):
|
||||||
|
params = annotation.__tuple_params__ # type: ignore
|
||||||
|
if params is not None:
|
||||||
|
param_strings = [self.format_annotation(p) for p in params]
|
||||||
|
if annotation.__tuple_use_ellipsis__: # type: ignore
|
||||||
|
param_strings.append('...')
|
||||||
|
return '%s[%s]' % (qualified_name,
|
||||||
|
', '.join(param_strings))
|
||||||
|
|
||||||
|
return qualified_name
|
||||||
|
@ -235,7 +235,7 @@ def test_format_signature():
|
|||||||
pass
|
pass
|
||||||
assert formatsig('method', 'H.foo', H.foo1, None, None) == '(b, *c)'
|
assert formatsig('method', 'H.foo', H.foo1, None, None) == '(b, *c)'
|
||||||
assert formatsig('method', 'H.foo', H.foo1, 'a', None) == '(a)'
|
assert formatsig('method', 'H.foo', H.foo1, 'a', None) == '(a)'
|
||||||
assert formatsig('method', 'H.foo', H.foo2, None, None) == '(b, *c)'
|
assert formatsig('method', 'H.foo', H.foo2, None, None) == '(*c)'
|
||||||
assert formatsig('method', 'H.foo', H.foo3, None, None) == r"(d='\\n')"
|
assert formatsig('method', 'H.foo', H.foo3, None, None) == r"(d='\\n')"
|
||||||
|
|
||||||
# test exception handling (exception is caught and args is '')
|
# test exception handling (exception is caught and args is '')
|
||||||
|
@ -113,6 +113,146 @@ def test_getargspec_bound_methods():
|
|||||||
assert expected_bound == inspect.getargspec(wrapped_bound_method)
|
assert expected_bound == inspect.getargspec(wrapped_bound_method)
|
||||||
|
|
||||||
|
|
||||||
|
def test_Signature():
|
||||||
|
# literals
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
inspect.Signature(1)
|
||||||
|
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
inspect.Signature('')
|
||||||
|
|
||||||
|
# builitin classes
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
inspect.Signature(int)
|
||||||
|
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
inspect.Signature(str)
|
||||||
|
|
||||||
|
# normal function
|
||||||
|
def func(a, b, c=1, d=2, *e, **f):
|
||||||
|
pass
|
||||||
|
|
||||||
|
sig = inspect.Signature(func).format_args()
|
||||||
|
assert sig == '(a, b, c=1, d=2, *e, **f)'
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
if sys.version_info < (3,):
|
||||||
|
assert sig == '(b, d=2)'
|
||||||
|
else:
|
||||||
|
assert sig == '(b, *, c=11, d=2)'
|
||||||
|
|
||||||
|
|
||||||
|
def test_Signature_methods():
|
||||||
|
class Foo:
|
||||||
|
def meth1(self, arg1, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def meth2(cls, arg1, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def meth3(arg1, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@functools.wraps(Foo().meth1)
|
||||||
|
def wrapped_bound_method(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# unbound method
|
||||||
|
sig = inspect.Signature(Foo.meth1).format_args()
|
||||||
|
assert sig == '(self, arg1, **kwargs)'
|
||||||
|
|
||||||
|
sig = inspect.Signature(Foo.meth1, bound_method=True).format_args()
|
||||||
|
assert sig == '(arg1, **kwargs)'
|
||||||
|
|
||||||
|
# bound method
|
||||||
|
sig = inspect.Signature(Foo().meth1).format_args()
|
||||||
|
assert sig == '(arg1, **kwargs)'
|
||||||
|
|
||||||
|
# class method
|
||||||
|
sig = inspect.Signature(Foo.meth2).format_args()
|
||||||
|
assert sig == '(arg1, *args, **kwargs)'
|
||||||
|
|
||||||
|
sig = inspect.Signature(Foo().meth2).format_args()
|
||||||
|
assert sig == '(arg1, *args, **kwargs)'
|
||||||
|
|
||||||
|
# static method
|
||||||
|
sig = inspect.Signature(Foo.meth3).format_args()
|
||||||
|
assert sig == '(arg1, *args, **kwargs)'
|
||||||
|
|
||||||
|
sig = inspect.Signature(Foo().meth3).format_args()
|
||||||
|
assert sig == '(arg1, *args, **kwargs)'
|
||||||
|
|
||||||
|
# wrapped bound method
|
||||||
|
sig = inspect.Signature(wrapped_bound_method).format_args()
|
||||||
|
if sys.version_info < (3,):
|
||||||
|
assert sig == '(*args, **kwargs)'
|
||||||
|
elif sys.version_info < (3, 4, 4):
|
||||||
|
assert sig == '(self, arg1, **kwargs)'
|
||||||
|
else:
|
||||||
|
assert sig == '(arg1, **kwargs)'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info < (3, 5),
|
||||||
|
reason='type annotation test is available on py35 or above')
|
||||||
|
def test_Signature_annotations():
|
||||||
|
from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11
|
||||||
|
|
||||||
|
# Class annotations
|
||||||
|
sig = inspect.Signature(f0).format_args()
|
||||||
|
assert sig == '(x: int, y: numbers.Integral) -> None'
|
||||||
|
|
||||||
|
# Generic types with concrete parameters
|
||||||
|
sig = inspect.Signature(f1).format_args()
|
||||||
|
assert sig == '(x: typing.List[int]) -> typing.List[int]'
|
||||||
|
|
||||||
|
# TypeVars and generic types with TypeVars
|
||||||
|
sig = inspect.Signature(f2).format_args()
|
||||||
|
assert sig == '(x: typing.List[T], y: typing.List[T_co], z: T) -> typing.List[T_contra]'
|
||||||
|
|
||||||
|
# Union types
|
||||||
|
sig = inspect.Signature(f3).format_args()
|
||||||
|
assert sig == '(x: typing.Union[str, numbers.Integral]) -> None'
|
||||||
|
|
||||||
|
# Quoted annotations
|
||||||
|
sig = inspect.Signature(f4).format_args()
|
||||||
|
assert sig == '(x: str, y: str) -> None'
|
||||||
|
|
||||||
|
# Keyword-only arguments
|
||||||
|
sig = inspect.Signature(f5).format_args()
|
||||||
|
assert 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'
|
||||||
|
|
||||||
|
# Space around '=' for defaults
|
||||||
|
sig = inspect.Signature(f7).format_args()
|
||||||
|
assert sig == '(x: int = None, y: dict = {}) -> None'
|
||||||
|
|
||||||
|
# Callable types
|
||||||
|
sig = inspect.Signature(f8).format_args()
|
||||||
|
assert sig == '(x: typing.Callable[[int, str], int]) -> None'
|
||||||
|
|
||||||
|
sig = inspect.Signature(f9).format_args()
|
||||||
|
assert sig == '(x: typing.Callable) -> None'
|
||||||
|
|
||||||
|
# Tuple types
|
||||||
|
sig = inspect.Signature(f10).format_args()
|
||||||
|
assert sig == '(x: typing.Tuple[int, str], y: typing.Tuple[int, ...]) -> None'
|
||||||
|
|
||||||
|
# Instance annotations
|
||||||
|
sig = inspect.Signature(f11).format_args()
|
||||||
|
assert sig == '(x: CustomAnnotation, y: 123) -> None'
|
||||||
|
|
||||||
|
|
||||||
def test_safe_getattr_with_default():
|
def test_safe_getattr_with_default():
|
||||||
class Foo(object):
|
class Foo(object):
|
||||||
def __getattr__(self, item):
|
def __getattr__(self, item):
|
||||||
|
Loading…
Reference in New Issue
Block a user