PEP-3107 specifies function annotations for Python 3.0. This module implements decorators for annotating procedures in Python 2.x, operators for reflecting on a procedure’s annotations, and primitives for compiling an annotated procedure into a wrapper procedure with an identical signature.
>>> from md.annotate import *
>>> def show(proc):
... return list(zip_annotations(proc, None))
A decorator for annotating procedures. The annotations given as args will be matched to the corresponding parameter in the procedure’s argspec. Annotations given in kwargs will be matched to the named parameter.
To specify a return annotation, args should be one element longer than the number of parameters in the procedure or the special keyword argument __return__ can be used.
>>> @annotated(int, int, bool)
... def greater(a, b):
... return a > b
>>> show(greater)
[('a', <type 'int'>), ('b', <type 'int'>), ('return', <type 'bool'>)]
>>> @annotated(basestring, __return__=basestring)
... def join(glue, *args):
... return glue.join(str(a) for a in args)
>>> show(join)
[('glue', <type 'basestring'>), ('args', None), ('return', <type 'basestring'>)]
Bind the (name, annotation) items given in bindings as annotations of procedure.
>>> def less(a, b):
... return a < b
>>> annotate(less, [('a', int), ('b', int), ('return', bool)])
<function less ...>
>>> show(less)
[('a', <type 'int'>), ('b', <type 'int'>), ('return', <type 'bool'>)]
Copy the annotations of source onto dest. The parameter names don’t have to match, but both procedures must have identical arity.
>>> def lte(n1, n2):
... return n1 <= n2
>>> show(transfer(less, lte))
[('n1', <type 'int'>), ('n2', <type 'int'>), ('return', <type 'bool'>)]
>>> def product(*args):
... from operator import mul
... return reduce(mul, args, 1)
>>> transfer(less, product)
...
TypeError: ('inconsistent structure', <function less ...>, <function product ...>)
This is a decorator for producing specialized annotating decorators like annotated(). For example, annotated() could be implemented like this:
>>> @annotating
... def annotated(procedure):
... return procedure
The decorator consumed by annotating() is transformed to have the same semantics as annotate() (it accepts annotation parameters and keyword annotations), but the implementation can be focused on what to do with an annotated procedure (see Annotations for an example).
Return the annotations of procedure. An empty dictionary is returned if the procedure has not been annotated.
>>> sorted(annotations(lte).items())
[('n1', <type 'int'>), ('n2', <type 'int'>), ('return', <type 'bool'>)]
>>> annotations(product)
{}
Return True if procedure is annotated.
>>> is_annotated(less)
True
>>> is_annotated(product)
False
Zip the annotated parameters of procedure to their annotations in parameter-order. If default is given, unannotated parameters are zipped to this value.
>>> @annotated(str)
... def partial(a, b):
... pass
>>> list(zip_annotations(partial))
[('a', <type 'str'>)]
>>> list(zip_annotations(partial, None))
[('a', <type 'str'>), ('b', None), ('return', None)]
See Annotations for examples.
Compile the annotations of procedure by producing a wrapper where each annotation is used to check an argument value or return value. A unifier is a factory that produces a check procedure. The check procedure takes two arguments, a value and the corresponding annotation; it must return a good value or raise an exception.
The biggest advantage of using compiler.compile_annotations() is it produces a wrapped procedure with an identical signature. This makes compilation transparent to other decorators or signature analysis.
If there is some procedure defined like this,
@annotated(FooType, ResultType)
def procedure(foo)
pass
Then compile_annotations(procedure, unifier) produces something like this,
__procedure = procedure
def procedure(foo):
check = unifier(procedure)
return check(__procedure(check(foo, FooType)), ResultType)