API Reference

sigtools.modifiers: Modify the effective signature of the decorated callable

The functions in this module can be used as decorators to mark and enforce some parameters to be keyword-only (kwoargs) or annotate (annotate) them, just like you can using Python 3 syntax. You can also mark and enforce parameters to be positional-only (posoargs). autokwoargs helps you quickly make your parameters with default values become keyword-only.

class sigtools.modifiers.annotate(_annotate__return_annotation=<unset>, **annotations)[source]

Annotates a function, avoiding the use of python3 syntax

These two functions are equivalent:

def py3_func(spam: 'ham', eggs: 'chicken'=False) -> 'return':
    return spam, eggs

@annotate('return', spam='ham', eggs='chicken')
def py23_func(spam, eggs=False):
    return spam, eggs
Parameters:
  • _annotate__return_annotation – The annotation to attach for return value
  • annotations – The annotations to attach for each parameter
Raises:

ValueError if a parameter to be annotated does not exist on the function

sigtools.modifiers.kwoargs(*kwoarg_names, start=None)[source]

Marks the given parameters as keyword-only, avoiding the use of python3 syntax.

These two functions are equivalent:

def py3_func(spam, *, ham, eggs='chicken'):
    return spam, ham, eggs

@kwoargs('ham', 'eggs')
def py23_func(spam, ham, eggs='chichen'):
    return spam, ham, eggs
Parameters:
  • start (str) – If given and is the name of a parameter, it and all parameters after it are made keyword-only
  • kwoarg_names (str) – Names of the parameters to convert
Raises:

ValueError if end or one of posoarg_names isn’t in the decorated function’s signature.

sigtools.modifiers.autokwoargs(func=None, *, exceptions=())[source]

Marks all arguments with default values as keyword-only.

Parameters:exceptions (sequence) – names of parameters not to convert
>>> from sigtools.modifiers import autokwoargs
>>> @autokwoargs(exceptions=['c'])
... def func(a, b, c=3, d=4, e=5):
...     pass
...
>>> from inspect import signature
>>> print(signature(func))
(a, b, c=3, *, d=4, e=5)
sigtools.modifiers.posoargs(*posoarg_names, end=None)[source]

Marks the given parameters as positional-only.

If the resulting function is passed any named arguments that references a positional parameter, TypeError will be raised.

>>> from sigtools.modifiers import posoargs
>>> @posoargs('ham')
... def func(ham, spam):
...     pass
...
>>> func('ham', 'spam')
>>> func('ham', spam='spam')
>>> func(ham='ham', spam='spam')
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "./sigtools/modifiers.py", line 94, in __call__
    .format(' '.join(repr(name) for name in intersect))
TypeError: Named arguments refer to positional-only parameters: 'ham'
Parameters:
  • end (str) – If given and is the name of a parameter, it and all parameters leading to it are made positional-only.
  • posoarg_names (str) – Names of the parameters to convert
Raises:

ValueError if end or one of posoarg_names isn’t in the decorated function’s signature.

sigtools.specifiers: Decorators to enhance a callable’s signature

The forwards_to_* decorators from this module will leave a “note” on the decorated object for sigtools.specifiers.signature to pick up. These “notes” tell signature in which way the signature of the examinated object should be crafted. The forwards_to_* decorators here will help you tell introspection or documentation tools what the *args and **kwargs parameters stand for in your function if it forwards them to another callable. This should cover most use cases, but you can use forger_function or set_signature_forger to create your own.

sigtools.specifiers.signature(obj, auto=True, args=(), kwargs={})

Retrieves the full signature of obj, either by taking note of decorators from this module, or by performing automatic signature discovery.

If auto is true, the signature will be automatically refined based on how *args and **kwargs are used throughout the function.

If args and/or kwargs are specified, they are used by automatic signature discovery as arguments passed into the function. This is useful if the function calls something passed in as a parameter.

You can use emulate=True as an argument to the decorators from this module if you wish them to work with inspect.signature or its funcsigs backport directly.

>>> from sigtools import specifiers
>>> import inspect
>>> def inner(a, b):
...     return a + b
...
>>> # Relying on automatic discovery
>>> def outer(c, *args, **kwargs):
...     return c * inner(*args, **kwargs)
>>> print(inspect.signature(outer))
(c, *args, **kwargs)
>>> print(specifiers.signature(outer, auto=False))
(c, *args, **kwargs)
>>> print(specifiers.signature(outer))
(c, a, b)
>>>
>>> # Using a decorator from this module
>>> @specifiers.forwards_to_function(inner)
... def outer(c, *args, **kwargs):
...     return c * inner(*args, **kwargs)
...
>>> print(inspect.signature(outer))
(c, *args, **kwargs)
>>> print(specifiers.signature(outer), auto=False)
(c, a, b)
>>> print(specifiers.signature(outer))
(c, a, b)
>>>
>>> # Using the emulate argument for compatibility with inspect
>>> @specifiers.forwards_to_function(inner, emulate=True)
... def outer(c, *args, **kwargs):
...     return c * inner(*args, **kwargs)
>>> print(inspect.signature(outer))
(c, a, b)
>>> print(specifiers.signature(outer), auto=False)
(c, a, b)
>>> print(specifiers.signature(outer))
(c, a, b)
Parameters:
  • auto (bool) – Enable automatic signature discovery.
  • args (sequence) – Positional arguments passed to the function.
  • mapping – Named arguments passed to the function.
sigtools.specifiers.forwards_to_function(wrapped, num_args=0, *named_args, emulate=None, hide_args=False, hide_kwargs=False, use_varargs=True, use_varkwargs=True, partial=False)[source]

Wraps the decorated function to give it the effective signature it has when it forwards its *args and **kwargs to the static callable wrapped.

>>> from sigtools.specifiers import forwards_to_function
>>> def wrapped(x, y):
...     return x * y
...
>>> @forwards_to_function(wrapped)
... def wrapper(a, *args, **kwargs):
...     return a + wrapped(*args, **kwargs)
...
>>> from inspect import signature
>>> print(signature(wrapper))
(a, x, y)

See Picking the appropriate arguments for forwards for more information on the parameters.

sigtools.specifiers.forwards_to_method(wrapped_name, num_args=0, *named_args, emulate=None, hide_args=False, hide_kwargs=False, use_varargs=True, use_varkwargs=True, partial=False)[source]

Wraps the decorated method to give it the effective signature it has when it forwards its *args and **kwargs to the method or attribute named by wrapped_name.

Parameters:wrapped_name (str) – The name of the wrapped method or attribute. Passing a name with dots(.) will do a deep attribute search.

See Picking the appropriate arguments for forwards for more information on the parameters.

>>> from sigtools.specifiers import signature, forwards_to_method
>>> class Ham(object):
...     def egg(self, a, b):
...         return a + b
...     @forwards_to_method('egg')
...     def spam(self, c, *args, **kwargs):
...         return c * self.egg(*args, **kwargs)
...
>>> h = Ham()
>>> print(signature(h.spam))
(c, a, b)
sigtools.specifiers.forwards_to_super(num_args=0, *named_args, emulate=None, cls=None, hide_args=False, hide_kwargs=False, use_varargs=True, use_varkwargs=True, partial=False)[source]

Wraps the decorated method to give it the effective signature it has when it forwards its *args and **kwargs to the same method on the super object for the class it belongs in.

You can only use this decorator directly in Python versions 3.3 and up, and the wrapped function must make use of the arg-less form of super:

>>> from sigtools.specifiers import forwards_to_super
>>> class Base:
...     def func(self, x, y):
...         return x * y
..
>>> class Subclass(Base):
...     @forwards_to_super()
...     def func(self, a, *args, **kwargs):
...         return a + super().func(*args, **kwargs)
...
>>> from inspect import signature
>>> print(signature(Subclass.func))
(self, a, x, y)
>>> print(signature(Subclass().func))
(a, x, y)

If you need to use similar functionality in older python versions, use apply_forwards_to_super instead.

See Picking the appropriate arguments for forwards for more information on the parameters.

sigtools.specifiers.apply_forwards_to_super(*member_names, num_args=0, named_args=(), **kwargs)[source]

Applies the forwards_to_super decorator on member_names in the decorated class, in a way which works in Python 2.6 and up.

>>> from sigtools.specifiers import apply_forwards_to_super
>>> class Base:
...     def func(self, x, y):
...         return x * y
...
>>> @apply_forwards_to_super('func')
... class Subclass(Base):
...     def func(self, a, *args, **kwargs):
...         return a + super(Subclass, self).func(*args, **kwargs)
...
>>> from inspect import signature
>>> print(signature(Subclass.func))
(self, a, x, y)
>>> print(signature(Subclass().func))
(a, x, y)

See Picking the appropriate arguments for forwards for more information on the parameters.

sigtools.specifiers.forwards(wrapper, wrapped, num_args=0, *named_args, hide_args=False, hide_kwargs=False, use_varargs=True, use_varkwargs=True, partial=False)[source]

Returns an effective signature of wrapper when it forwards its *args and **kwargs to wrapped.

Parameters:
  • wrapper (callable) – The outer callable
  • wrapped (callable) – The callable wrapper’s extra arguments are passed to.
Returns:

a inspect.Signature object

See Picking the appropriate arguments for forwards for more information on the parameters.

sigtools.specifiers.forger_function(func)[source]

Creates a decorator factory which, when applied will set func as the forger function of the decorated object.

Parameters:func (callable) – Must return a fake signature for the object passed as the named argument obj. Any arguments supplied during decoration are also passed.

The decorator produced by this function also accepts an emulate parameter. See set_signature_forger for information on it.

This function can be used as a decorator:

>>> from sigtools import specifiers, modifiers, support
>>> @specifiers.forger_function
... @modifiers.kwoargs('obj')
... def static_signature(obj, sig):
...     return sig
...
>>> @static_signature(support.s('a, b, /'))
... def my_func(d, e):
...     pass
...
>>> print(specifiers.signature(my_func))
(a, b, /)
sigtools.specifiers.set_signature_forger(obj, forger, emulate=None)[source]

Attempts to set the given signature forger on the supplied object.

This function first tries to set an attribute on obj and returns it. If that fails, it wraps the object that advertises the correct signature (even to inspect.signature) and forwards calls.

Parameters:emulate – If supplied, forces the function to adhere to one strategy: either set the attribute or fail(False), or always wrap the object(True). If something else is passed, it is called with (obj, forger) and the return value is used.
sigtools.specifiers.as_forged

Descriptor that returns the computer signature for the object it is an attribute of. Most useful as __signature__.

Allows inspect.signature to read forged signatures from your own objects.

>>> from sigtools import specifiers
>>> import inspect
>>> class MyClass(object):
...     __signature__ = specifiers.as_forged
...     @specifiers.forwards_to_method('method')
...     def __call__(self, x, *args, **kwargs):
...         pass
...     def method(self, a, b, c):
...         pass
...
>>> print(inspect.signature(MyClass()))
(x, a, b, c)

sigtools.wrappers: Combine multiple functions

The functions here help you combine multiple functions into a new callable which will automatically advertise the correct signature.

class sigtools.wrappers.Combination(*functions)[source]

Bases: object

Creates a callable that passes the first argument through each callable, using the result of each pass as the argument to the next

get_signature(obj)[source]
sigtools.wrappers.decorator(func)[source]

Turns a function into a decorator.

The function received the decorated function as first argument.

from sigtools import wrappers

@wrappers.decorator
def my_decorator(func, *args, deco_param=False, **kwargs):
    ... # Do stuff with deco_param
    return func(*args, **kwargs)

@my_decorator
def my_function(func_param):
    ...

my_function('value for func_param', deco_param=True)

from sigtools import specifiers
print(specifiers.signature(my_function))
# (func_param, *, deco_param=False)

Unlike wrapper_decorator, decorator does not require you to specify how your function uses *args, **kwargs and lets automatic signature discovery figure it out.

Note

Signature reporting will not work in interactive sessions, as per Limitations of automatic signature discovery.

sigtools.wrappers.wrapper_decorator(num_args=0, *named_args, hide_args=False, hide_kwargs=False, use_varargs=True, use_varkwargs=True, partial=False)[source]

Turns a function into a decorator that wraps callables with that function.

Consult signatures.forwards’s documentation for help picking the correct values for the parameters.

The wrapped function is passed as first argument to the wrapper.

As an example, here we create a @print_call decorator which wraps the decorated function and prints a line everytime the function is called:

>>> from sigtools import modifiers, wrappers
>>> @wrappers.wrapper_decorator
... @modifiers.autokwoargs
... def print_call(func, _show_return=True, *args, **kwargs):
...     print('Calling {0.__name__}(*{1}, **{2})'.format(func, args, kwargs))
...     ret = func(*args, **kwargs)
...     if _show_return:
...         print('Return: {0!r}'.format(ret))
...     return ret
...
>>> print_call
<decorate with <<function print_call at 0x7f28d721a950> with signature print_cal
l(func, *args, _show_return=True, **kwargs)>>
>>> @print_call
... def as_list(obj):
...     return [obj]
...
>>> as_list
<<function as_list at 0x7f28d721ad40> decorated with <<function print_call at 0x
7f28d721a950> with signature print_call(func, *args, _show_return=True, **kwargs
)>>
>>> from inspect import signature
>>> print(signature(as_list))
(obj, *, _show_return=True)
>>> as_list('ham')
Calling as_list(*('ham',), **{})
Return: ['ham']
['ham']
>>> as_list('spam', _show_return=False)
Calling as_list(*('spam',), **{})
['spam']
sigtools.wrappers.wrappers(obj)[source]

For introspection purposes, returns an iterable that yields each wrapping function of obj(as done through wrapper_decorator, outermost wrapper first.

Continuing from the wrapper_decorator example:

>>> list(wrappers.wrappers(as_list))
[<<function print_call at 0x7f28d721a950> with signature print_call(func, *args,
 _show_return=True, **kwargs)>]

sigtools.signatures: Signature object manipulation

The functions here are high-level operations that produce a signature from other signature objects, as opposed to dealing with each parameter individually. They are most notably used by the decorators from sigtools.specifiers to compute combined signatures.

sigtools.signatures.signature(obj)[source]

Retrieves to unmodified signature from obj, without taking sigtools.specifiers decorators into account or attempting automatic signature discovery.

sigtools.signatures.merge(*signatures)[source]

Tries to compute a signature for which a valid call would also validate the given signatures.

It guarantees any call that conforms to the merged signature will conform to all the given signatures. However, some calls that don’t conform to the merged signature may actually work on all the given ones regardless.

Parameters:signatures (inspect.Signature) – The signatures to merge together.
Returns:a inspect.Signature object
Raises:IncompatibleSignatures
>>> from sigtools import signatures, support
>>> print(signatures.merge(
...     support.s('one, two, *args, **kwargs'),
...     support.s('one, two, three, *, alpha, **kwargs'),
...     support.s('one, *args, beta, **kwargs')
...     ))
(one, two, three, *, alpha, beta, **kwargs)

The resulting signature does not necessarily validate all ways of conforming to the underlying signatures:

>>> from sigtools import signatures
>>> from inspect import signature
>>>
>>> def left(alpha, *args, **kwargs):
...     return alpha
...
>>> def right(beta, *args, **kwargs):
...     return beta
...
>>> sig_left = signature(left)
>>> sig_right = signature(right)
>>> sig_merged = signatures.merge(sig_left, sig_right)
>>>
>>> print(sig_merged)
(alpha, /, *args, **kwargs)
>>>
>>> kwargs = {'alpha': 'a', 'beta': 'b'}
>>> left(**kwargs), right(**kwargs) # both functions accept the call
('a', 'b')
>>>
>>> sig_merged.bind(**kwargs) # the merged signature doesn't
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/usr/lib64/python3.4/inspect.py", line 2642, in bind
    return args[0]._bind(args[1:], kwargs)
  File "/usr/lib64/python3.4/inspect.py", line 2542, in _bind
    raise TypeError(msg) from None
TypeError: 'alpha' parameter is positional only, but was passed as a keyword
sigtools.signatures.embed(*signatures, use_varargs=True, use_varkwargs=True)[source]

Embeds a signature within another’s *args and **kwargs parameters, as if a function with the outer signature called a function with the inner signature with just f(*args, **kwargs).

Parameters:
  • signatures (inspect.Signature) – The signatures to embed within one-another, outermost first.
  • use_varargs (bool) – Make use of the *args-like parameter.
  • use_varkwargs (bool) – Make use of the *kwargs-like parameter.
Returns:

a inspect.Signature object

Raises:

IncompatibleSignatures

>>> from sigtools import signatures, support
>>> print(signatures.embed(
...     support.s('one, *args, **kwargs'),
...     support.s('two, *args, kw, **kwargs'),
...     support.s('last'),
...     ))
(one, two, last, *, kw)
>>> # use signatures.mask() to remove self-like parameters
>>> print(signatures.embed(
...     support.s('self, *args, **kwargs'),
...     signatures.mask(
...         support.s('self, *args, keyword, **kwargs'), 1),
...     ))
(self, *args, keyword, **kwargs)
sigtools.signatures.mask(sig, num_args=0, *named_args, hide_args=False, hide_kwargs=False, hide_varargs=False, hide_varkwargs=False)[source]

Removes the given amount of positional parameters and the given named parameters from sig.

Parameters:
  • sig (inspect.Signature) – The signature to operate on
  • num_args (int) – The amount of positional arguments passed
  • named_args (str) – The names of named arguments passed
  • hide_args – If true, mask all positional parameters
  • hide_kwargs – If true, mask all keyword parameters
  • hide_varargs – If true, mask the *args-like parameter completely if present.
  • hide_varkwargs – If true, mask the *kwargs-like parameter completely if present.
Returns:

a inspect.Signature object

Raises:

ValueError if the signature cannot handle the arguments to be passed.

>>> from sigtools import signatures, support
>>> print(signatures.mask(support.s('a, b, *, c, d'), 1, 'd'))
(b, *, c)
>>> print(signatures.mask(support.s('a, b, *args, c, d'), 3, 'd'))
(*args, c)
>>> print(signatures.mask(support.s('*args, c, d'), 2, 'd', hide_varargs=True))
(*, c)
sigtools.signatures.forwards(outer, inner, num_args=0, *named_args, hide_args=False, hide_kwargs=False, use_varargs=True, use_varkwargs=True, partial=False)[source]

Calls mask on inner, then returns the result of calling embed with outer and the result of mask.

Parameters:
  • outer (inspect.Signature) – The outermost signature.
  • inner (inspect.Signature) – The inner signature.
  • partial (bool) – Set to True if the arguments are passed to partial(func_with_inner, *args, **kwargs) rather than func_with_inner.

use_varargs and use_varkwargs are the same parameters as in embed, and num_args, named_args, hide_args and hide_kwargs are parameters of mask.

Returns:the resulting inspect.Signature object
Raises:IncompatibleSignatures
>>> from sigtools import support, signatures
>>> print(signatures.forwards(
...     support.s('a, *args, x, **kwargs'),
...     support.s('b, c, *, y, z'),
...     1, 'y'))
(a, c, *, x, z)
exception sigtools.signatures.IncompatibleSignatures(sig, others)[source]

Bases: ValueError

Raised when two or more signatures are incompatible for the requested operation.

Variables:
  • sig (inspect.Signature) – The signature at which point the incompatibility was discovered
  • others – The signatures up until sig
sigtools.signatures.sort_params(sig, sources=False)[source]

Classifies the parameters from sig.

Parameters:sig (inspect.Signature) – The signature to operate on
Returns:A tuple (posargs, pokargs, varargs, kwoargs, varkwas)
Return type:(list, list, Parameter or None, dict, Parameter or None)
>>> from sigtools import signatures, support
>>> from pprint import pprint
>>> pprint(signatures.sort_params(support.s('a, /, b, *args, c, d')))
([<Parameter at 0x7fdda4e89418 'a'>],
 [<Parameter at 0x7fdda4e89470 'b'>],
 <Parameter at 0x7fdda4e89c58 'args'>,
 {'c': <Parameter at 0x7fdda4e89c00 'c'>,
  'd': <Parameter at 0x7fdda4e89db8 'd'>},
 None)
sigtools.signatures.apply_params(sig, posargs, pokargs, varargs, kwoargs, varkwargs, sources=None)[source]

Reverses sort_params’s operation.

Returns:A new inspect.Signature object based off sig, with the given parameters.

sigtools.support: Utilities for use in interactive sessions and unit tests

sigtools.support.s(sig_str, ret=None, *, pre='', locals=None, name='func')[source]

Creates a signature from the given string representation of one.

Warning

The contents of the arguments are eventually passed to exec. Do not use with untrusted input.

>>> from sigtools.support import s
>>> sig = s('a, b=2, *args, c:"annotation", **kwargs')
>>> sig
<inspect.Signature object at 0x7f15e6055550>
>>> print(sig)
(a, b=2, *args, c:'annotation', **kwargs)
sigtools.support.f(sig_str, ret=None, *, pre='', locals=None, name='func')[source]

Creates a dummy function that has the signature represented by sig_str and returns a tuple containing the arguments passed, in order.

Warning

The contents of the arguments are eventually passed to exec. Do not use with untrusted input.

>>> from sigtools.support import f
>>> import inspect
>>> func = f('a, b=2, *args, c:"annotation", **kwargs')
>>> print(inspect.signature(func))
(a, b=2, *args, c:'annotation', **kwargs)
>>> func(1, c=3)
{'b': 2, 'a': 1, 'kwargs': {}, 'args': ()}
>>> func(1, 2, 3, 4, c=5, d=6)
{'b': 2, 'a': 1, 'kwargs': {'d': 6}, 'args': (3, 4)}
sigtools.support.read_sig(sig_str, ret=None)[source]

Reads a string representation of a signature and returns a tuple func_code can understand.

sigtools.support.func_code(names, return_annotation, annotations, posoarg_n, kwoarg_n, params, pre='', name='func')[source]

Formats the code to construct a function to read_sig’s design.

sigtools.support.make_func(code, locals=None, name='func')[source]

Executes the given code and returns the object named func from the resulting namespace.

sigtools.support.func_from_sig(sig)[source]

Creates a dummy function from the given signature object

Warning

The contents of the arguments are eventually passed to exec. Do not use with untrusted input.

sigtools.support.make_up_callsigs(sig, extra=2)[source]

Figures out reasonably as many ways as possible to call a callable with the given signature.

sigtools.support.bind_callsig(sig, args, kwargs)[source]

Returns a dict with each parameter name from sig mapped to values from args, kwargs as if a function with sig was called with (*args, **kwargs).

Similar to inspect.Signature.bind.

sigtools.support.sort_callsigs(sig, callsigs)[source]

Determines which ways to call sig in callsigs are valid or not.

Returns:Two lists: (valid, invalid).
valid
(args, kwargs, bound) in which bound is the dict returned by bind_callsig. It will be equal to the return value of a function with sig returned by f
ìnvalid
(args, kwargs)
sigtools.support.test_func_sig_coherent(func, check_return=True, check_invalid=True)[source]

Tests if a function is coherent with its signature.

Parameters:
  • check_return (bool) – Check if the return value is correct (see sort_callsigs)
  • check_invalid (bool) – Make sure call signatures invalid for the signature are also invalid for the passed callable.
Raises:

AssertionError

sigtools.sphinxext: Extension to make Sphinx use signature objects

sphinx.ext.autodoc can only automatically discover the signatures of basic callables. This extension makes it use sigtools.specifiers.signature on the callable instead.

Enable it by appending 'sigtools.sphinxext' to the extensions list in your Sphinx conf.py