def test_signature_annotations_py38(app): from target.pep570 import foo, bar, baz, qux # case: separator at head sig = inspect.signature(foo) assert stringify_signature(sig) == '(*, a, b)' # case: separator in the middle sig = inspect.signature(bar) assert stringify_signature(sig) == '(a, b, /, c, d)' sig = inspect.signature(baz) assert stringify_signature(sig) == '(a, /, *, b)' # case: separator at tail sig = inspect.signature(qux) assert stringify_signature(sig) == '(a, b, /)'
def test_signature_partial(): def fun(a, b, c=1, d=2): pass p = functools.partial(fun, 10, c=11) sig = inspect.signature(p) assert stringify_signature(sig) == '(b, *, c=11, d=2)'
def process_fnop_signature(app, what, name, obj, options, signature, return_annotation): try: if isinstance(obj, FnOp): fn = obj.fn fn_sig = inspect.signature(fn) signature = str(fn_sig) return_annotation = None except Exception as ex: log.error("Failed `autodoc-process-signature`: %s", ex, exc_info=1) return (signature, return_annotation)
def process_signature(app, what: str, name: str, obj, options, sign, return_annotation): if not callable(obj): return original_obj = obj if inspect.isclass(obj): obj = getattr(obj, '__init__', getattr(obj, '__new__', None)) if isinstance(obj, tomate.filegroup.scanner.ScannerCS): obj = obj.func if not getattr(obj, '__annotations__', None): return obj = inspect.unwrap(obj) sign = signature(obj) parameters = [ param.replace(annotation=inspect.Parameter.empty) for param in sign.parameters.values() ] if '<locals>' in obj.__qualname__: logger.warning( 'Cannot treat a function defined as a local function: "%s" (use @functools.wraps)', name) return if parameters: if inspect.isclass(original_obj): del parameters[0] elif what == 'method': outer = inspect.getmodule(obj) for clsname in obj.__qualname__.split('.')[:-1]: outer = getattr(outer, clsname) method_name = obj.__name__ if method_name.startswith("__") and not method_name.endswith("__"): # If the method starts with double underscore (dunder) # Python applies mangling so we need to prepend the class name. # This doesn't happen if it always ends with double underscore. class_name = obj.__qualname__.split('.')[-2] method_name = "_{c}{m}".format(c=class_name, m=method_name) method_object = outer.__dict__[method_name] if outer else obj if not isinstance(method_object, (classmethod, staticmethod)): del parameters[0] sign = sign.replace(parameters=parameters, return_annotation=inspect.Signature.empty) return stringify_signature(sign).replace('\\', '\\\\'), None
def test_signature_partialmethod(): from functools import partialmethod class Foo: def meth1(self, arg1, arg2, arg3=None, arg4=None): pass def meth2(self, arg1, arg2): pass foo = partialmethod(meth1, 1, 2) bar = partialmethod(meth1, 1, arg3=3) baz = partialmethod(meth2, 1, 2) subject = Foo() sig = inspect.signature(subject.foo) assert stringify_signature(sig) == '(arg3=None, arg4=None)' sig = inspect.signature(subject.bar) assert stringify_signature(sig) == '(arg2, *, arg3=3, arg4=None)' sig = inspect.signature(subject.baz) assert stringify_signature(sig) == '()'
def record_typehints(app: Sphinx, objtype: str, name: str, obj: Any, options: Dict, args: str, retann: str) -> None: """Record type hints to env object.""" try: if callable(obj): annotations = app.env.temp_data.setdefault('annotations', {}) annotation = annotations.setdefault(name, OrderedDict()) sig = inspect.signature(obj) for param in sig.parameters.values(): if param.annotation is not param.empty: annotation[param.name] = typing.stringify(param.annotation) if sig.return_annotation is not sig.empty: annotation['return'] = typing.stringify(sig.return_annotation) except TypeError: pass
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.stringify_signature(inspect.signature(func)) assert sig == '(a, b, c=1, d=2, *e, **f)'
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) assert stringify_signature(sig) == '(self, arg1, **kwargs)' sig = inspect.signature(Foo.meth1, bound_method=True) assert stringify_signature(sig) == '(arg1, **kwargs)' # bound method sig = inspect.signature(Foo().meth1) assert stringify_signature(sig) == '(arg1, **kwargs)' # class method sig = inspect.signature(Foo.meth2) assert stringify_signature(sig) == '(arg1, *args, **kwargs)' sig = inspect.signature(Foo().meth2) assert stringify_signature(sig) == '(arg1, *args, **kwargs)' # static method sig = inspect.signature(Foo.meth3) assert stringify_signature(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) assert stringify_signature(sig) == '(arg1, **kwargs)'
def update_annotations_using_type_comments(app: Sphinx, obj: Any, bound_method: bool) -> None: """Update annotations info of *obj* using type_comments.""" try: function = get_type_comment(obj) if function and hasattr(function, 'argtypes'): if function.argtypes != [ast.Ellipsis]: # type: ignore sig = inspect.signature(obj, bound_method) for i, param in enumerate(sig.parameters.values()): if param.name not in obj.__annotations__: annotation = ast_unparse(function.argtypes[i]) # type: ignore obj.__annotations__[param.name] = annotation if 'return' not in obj.__annotations__: obj.__annotations__['return'] = ast_unparse(function.returns) # type: ignore except NotImplementedError as exc: # failed to ast.unparse() logger.warning("Failed to parse type_comment for %r: %s", obj, exc)
def update_annotations_using_type_comments(app: Sphinx, obj: Any, bound_method: bool) -> None: """Update annotations info of *obj* using type_comments.""" try: type_sig = get_type_comment(obj, bound_method) if type_sig: sig = inspect.signature(obj, bound_method) for param in sig.parameters.values(): if param.name not in obj.__annotations__: annotation = type_sig.parameters[param.name].annotation if annotation is not Parameter.empty: obj.__annotations__[param.name] = ast_unparse( annotation) if 'return' not in obj.__annotations__: obj.__annotations__['return'] = type_sig.return_annotation except NotImplementedError as exc: # failed to ast.unparse() logger.warning("Failed to parse type_comment for %r: %s", obj, exc)
def record_typehints(app: Sphinx, objtype: str, name: str, obj: Any, options: Dict, args: str, retann: str) -> None: """Record type hints to env object.""" if app.config.autodoc_typehints_format == 'short': mode = 'smart' else: mode = 'fully-qualified' try: if callable(obj): annotations = app.env.temp_data.setdefault('annotations', {}) annotation = annotations.setdefault(name, OrderedDict()) sig = inspect.signature(obj, type_aliases=app.config.autodoc_type_aliases) for param in sig.parameters.values(): if param.annotation is not param.empty: annotation[param.name] = typing.stringify(param.annotation, mode) if sig.return_annotation is not sig.empty: annotation['return'] = typing.stringify(sig.return_annotation, mode) except (TypeError, ValueError): pass
def test_signature(): # literals with pytest.raises(TypeError): inspect.signature(1) with pytest.raises(TypeError): inspect.signature('') # builtins are supported on a case-by-case basis, depending on whether # they define __text_signature__ if getattr(list, '__text_signature__', None): sig = inspect.stringify_signature(inspect.signature(list)) assert sig == '(iterable=(), /)' else: with pytest.raises(ValueError): inspect.signature(list) # normal function def func(a, b, c=1, d=2, *e, **f): pass sig = inspect.stringify_signature(inspect.signature(func)) assert sig == '(a, b, c=1, d=2, *e, **f)'
def test_signature_annotations(): from .typing_test_data import (Node, f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21) # Class annotations sig = inspect.signature(f0) assert stringify_signature(sig) == '(x: int, y: numbers.Integral) -> None' # Generic types with concrete parameters sig = inspect.signature(f1) assert stringify_signature( sig) == '(x: typing.List[int]) -> typing.List[int]' # TypeVars and generic types with TypeVars sig = inspect.signature(f2) if sys.version_info < (3, 7): assert stringify_signature(sig) == ( '(x: typing.List[typing.T],' ' y: typing.List[typing.T_co],' ' z: typing.T' ') -> typing.List[typing.T_contra]') else: assert stringify_signature(sig) == ( '(x: typing.List[tests.typing_test_data.T],' ' y: typing.List[tests.typing_test_data.T_co],' ' z: tests.typing_test_data.T' ') -> typing.List[tests.typing_test_data.T_contra]') # Union types sig = inspect.signature(f3) assert stringify_signature( sig) == '(x: typing.Union[str, numbers.Integral]) -> None' # Quoted annotations sig = inspect.signature(f4) assert stringify_signature(sig) == '(x: str, y: str) -> None' # Keyword-only arguments sig = inspect.signature(f5) assert stringify_signature(sig) == '(x: int, *, y: str, z: str) -> None' # Keyword-only arguments with varargs sig = inspect.signature(f6) assert stringify_signature( sig) == '(x: int, *args, y: str, z: str) -> None' # Space around '=' for defaults sig = inspect.signature(f7) if sys.version_info < (3, 11): assert stringify_signature( sig) == '(x: typing.Optional[int] = None, y: dict = {}) -> None' else: assert stringify_signature( sig) == '(x: int = None, y: dict = {}) -> None' # Callable types sig = inspect.signature(f8) assert stringify_signature( sig) == '(x: typing.Callable[[int, str], int]) -> None' sig = inspect.signature(f9) assert stringify_signature(sig) == '(x: typing.Callable) -> None' # Tuple types sig = inspect.signature(f10) assert stringify_signature( sig ) == '(x: typing.Tuple[int, str], y: typing.Tuple[int, ...]) -> None' # Instance annotations sig = inspect.signature(f11) assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None' # tuple with more than two items sig = inspect.signature(f12) assert stringify_signature(sig) == '() -> typing.Tuple[int, str, int]' # optional sig = inspect.signature(f13) assert stringify_signature(sig) == '() -> typing.Optional[str]' # optional union sig = inspect.signature(f20) assert stringify_signature(sig) in ( '() -> typing.Optional[typing.Union[int, str]]', '() -> typing.Optional[typing.Union[str, int]]') # Any sig = inspect.signature(f14) assert stringify_signature(sig) == '() -> typing.Any' # ForwardRef sig = inspect.signature(f15) assert stringify_signature(sig) == '(x: Unknown, y: int) -> typing.Any' # keyword only arguments (1) sig = inspect.signature(f16) assert stringify_signature(sig) == '(arg1, arg2, *, arg3=None, arg4=None)' # keyword only arguments (2) sig = inspect.signature(f17) assert stringify_signature(sig) == '(*, arg3, arg4)' sig = inspect.signature(f18) assert stringify_signature(sig) == ( '(self, arg1: typing.Union[int, typing.Tuple] = 10) -> ' 'typing.List[typing.Dict]') # annotations for variadic and keyword parameters sig = inspect.signature(f19) assert stringify_signature(sig) == '(*args: int, **kwargs: str)' # default value is inspect.Signature.empty sig = inspect.signature(f21) assert stringify_signature(sig) == "(arg1='whatever', arg2)" # type hints by string sig = inspect.signature(Node.children) assert stringify_signature( sig) == '(self) -> typing.List[tests.typing_test_data.Node]' sig = inspect.signature(Node.__init__) assert stringify_signature( sig ) == '(self, parent: typing.Optional[tests.typing_test_data.Node]) -> None' # show_annotation is False sig = inspect.signature(f7) assert stringify_signature(sig, show_annotation=False) == '(x=None, y={})' # show_return_annotation is False sig = inspect.signature(f7) if sys.version_info < (3, 11): assert stringify_signature( sig, show_return_annotation=False ) == '(x: typing.Optional[int] = None, y: dict = {})' else: assert stringify_signature( sig, show_return_annotation=False) == '(x: int = None, y: dict = {})' # unqualified_typehints is True sig = inspect.signature(f7) if sys.version_info < (3, 11): assert stringify_signature( sig, unqualified_typehints=True ) == '(x: ~typing.Optional[int] = None, y: dict = {}) -> None' else: assert stringify_signature(sig, unqualified_typehints=True ) == '(x: int = None, y: dict = {}) -> None'
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) assert stringify_signature(sig) == '(x: int, y: numbers.Integral) -> None' # Generic types with concrete parameters sig = inspect.signature(f1) assert stringify_signature(sig) == '(x: List[int]) -> List[int]' # TypeVars and generic types with TypeVars 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) assert stringify_signature(sig) == '(x: Union[str, numbers.Integral]) -> None' # Quoted annotations sig = inspect.signature(f4) assert stringify_signature(sig) == '(x: str, y: str) -> None' # Keyword-only arguments sig = inspect.signature(f5) assert stringify_signature(sig) == '(x: int, *, y: str, z: str) -> None' # Keyword-only arguments with varargs sig = inspect.signature(f6) assert stringify_signature(sig) == '(x: int, *args, y: str, z: str) -> None' # Space around '=' for defaults sig = inspect.signature(f7) assert stringify_signature(sig) == '(x: int = None, y: dict = {}) -> None' # Callable types sig = inspect.signature(f8) assert stringify_signature(sig) == '(x: Callable[[int, str], int]) -> None' sig = inspect.signature(f9) assert stringify_signature(sig) == '(x: Callable) -> None' # Tuple types sig = inspect.signature(f10) assert stringify_signature(sig) == '(x: Tuple[int, str], y: Tuple[int, ...]) -> None' # Instance annotations sig = inspect.signature(f11) assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None' # tuple with more than two items sig = inspect.signature(f12) assert stringify_signature(sig) == '() -> Tuple[int, str, int]' # optional sig = inspect.signature(f13) assert stringify_signature(sig) == '() -> Optional[str]' # Any sig = inspect.signature(f14) assert stringify_signature(sig) == '() -> Any' # ForwardRef sig = inspect.signature(f15) assert stringify_signature(sig) == '(x: Unknown, y: int) -> Any' # keyword only arguments (1) sig = inspect.signature(f16) assert stringify_signature(sig) == '(arg1, arg2, *, arg3=None, arg4=None)' # keyword only arguments (2) sig = inspect.signature(f17) assert stringify_signature(sig) == '(*, arg3, arg4)' 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) assert stringify_signature(sig) == '(*args: int, **kwargs: str)' # type hints by string sig = inspect.signature(Node.children) if (3, 5, 0) <= sys.version_info < (3, 5, 3): assert stringify_signature(sig) == '(self) -> List[Node]' else: assert stringify_signature(sig) == '(self) -> List[typing_test_data.Node]' sig = inspect.signature(Node.__init__) assert stringify_signature(sig) == '(self, parent: Optional[Node]) -> None' # show_annotation is False 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 = {})'
def process_signature(obj): sig = signature(obj) return stringify_signature(sig)