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_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 format_args(self, **kwargs) -> str: """Formats the arguments from the function object""" if self.config.autodoc_typehints in ("none", "description"): kwargs.setdefault("show_annotation", False) try: self.env.app.emit("autodoc-before-process-signature", self.object, False) sig = self.object.get_signature( type_aliases=self.config.autodoc_type_aliases) args = stringify_signature(sig, **kwargs) except TypeError as exc: logger.warning(__("Failed to get a function signature for %s: %s"), self.fullname, exc) return None except ValueError: args = "" if self.config.strip_signature_backslash: args = args.replace("\\", "\\\\") return args
def test_signature_annotations_py38(app): from target.pep570 import bar, baz, foo, 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 process_signature(app, what: str, name: str, obj, options, signature, return_annotation): if not callable(obj): return original_obj = obj if inspect.isclass(obj): obj = getattr(obj, '__init__', getattr(obj, '__new__', None)) if not getattr(obj, '__annotations__', None): return obj = inspect.unwrap(obj) signature = Signature(obj) parameters = [ param.replace(annotation=inspect.Parameter.empty) for param in signature.parameters.values() ] # The generated dataclass __init__() is weird and needs the second condition if '<locals>' in obj.__qualname__ and not (what == 'method' and name.endswith('.__init__')): logger.warning( 'Cannot treat a function defined as a local function: "%s" (use @functools.wraps)', name) return if parameters: if inspect.isclass(original_obj) or (what == 'method' and name.endswith('.__init__')): 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] signature = signature.replace(parameters=parameters, return_annotation=inspect.Signature.empty) return stringify_signature(signature).replace('\\', '\\\\'), None
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_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 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 strip_annotations(app, what: str, name: str, obj, options, signature, return_annotation): if what not in {'function', 'method', 'class'}: return original_signature = inspect.signature(obj) new_signature = original_signature.replace( return_annotation=inspect.Signature.empty, parameters=[ param.replace(annotation=inspect.Parameter.empty) for param in original_signature.parameters.values() if param.name != 'self' ], ) return stringify_signature(new_signature), None
def format_signature(self, **kwargs: Any) -> str: """ Format the method's signature, including those for any overloaded implementations. :param kwargs: :return: The signature(s), as a multi-line string. """ sigs = [] if self.analyzer and '.'.join(self.objpath) in self.analyzer.overloads: overloaded = True else: overloaded = False sig = super(autodoc.MethodDocumenter, self).format_signature(**kwargs) sigs.append(sig) meth = self.parent.__dict__.get(self.objpath[-1]) if inspect.is_singledispatch_method(meth): # append signature of singledispatch'ed functions for typ, func in meth.dispatcher.registry.items(): if typ is object: pass # default implementation. skipped. else: self.annotate_to_first_argument(func, typ) documenter = MethodDocumenter(self.directive, '') documenter.parent = self.parent documenter.object = func documenter.objpath = [None] # type: ignore sigs.append(documenter.format_signature()) if overloaded: if self.env.config.overloads_location == "signature": for overload in self.analyzer.overloads.get('.'.join( self.objpath)): # type: ignore sig = stringify_signature( self.process_overload_signature(overload), **kwargs) sigs.append(sig) sig = super(autodoc.MethodDocumenter, self).format_signature(**kwargs) sigs.append(sig) return '\n'.join(sigs)
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_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)
def process_signature( app: Sphinx, what: str, name: str, obj, options, signature, return_annotation: Any, ) -> Optional[Tuple[str, None]]: """ Process the signature for a function/method. :param app: The Sphinx application. :param what: :param name: The name of the object being documented. :param obj: The object being documented. :param options: Mapping of autodoc options to values. :param signature: :param return_annotation: :rtype: .. versionchanged:: 0.8.0 Added support for factory function default values in attrs classes. """ if not callable(obj): return None original_obj = obj if inspect.isclass(obj): obj, signature, parameters = preprocess_class_defaults(obj) else: signature, parameters = preprocess_function_defaults(obj) obj = inspect.unwrap(obj) if not getattr(obj, "__annotations__", None): return None # The generated dataclass __init__() and class are weird and need extra checks # This helper function operates on the generated class and methods # of a dataclass, not an instantiated dataclass object. As such, # it cannot be replaced by a call to `dataclasses.is_dataclass()`. def _is_dataclass(name: str, what: str, qualname: str) -> bool: if what == "method" and name.endswith(".__init__"): # generated __init__() return True if what == "class" and qualname.endswith(".__init__"): # generated class return True return False # The generated dataclass __init__() is weird and needs the second condition if (hasattr(obj, "__qualname__") and "<locals>" in obj.__qualname__ and not _is_dataclass(name, what, obj.__qualname__)): sat_logger.warning( "Cannot treat a function defined as a local function: '%s' (use @functools.wraps)", name) return None if parameters: if inspect.isclass(original_obj) or (what == "method" and name.endswith(".__init__")): del parameters[0] elif what == "method": try: outer = inspect.getmodule(obj) if outer is not None: for clsname in obj.__qualname__.split('.')[:-1]: outer = getattr(outer, clsname) except AttributeError: outer = None 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 = f"_{class_name}{method_name}" if outer is not None: method_object = outer.__dict__[method_name] if outer else obj if not isinstance(method_object, (classmethod, staticmethod)): del parameters[0] else: if not inspect.ismethod(obj) and parameters[0].name in { "self", "cls", "_cls" }: del parameters[0] signature = signature.replace(parameters=parameters, return_annotation=inspect.Signature.empty) return stringify_signature(signature), None # .replace('\\', '\\\\')