2006-08-21 19:54:07 -04:00
|
|
|
|
PEP: 362
|
|
|
|
|
Title: Function Signature Object
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
2012-06-05 20:20:53 -04:00
|
|
|
|
Author: Brett Cannon <brett@python.org>, Jiwon Seo <seojiwon@gmail.com>,
|
|
|
|
|
Yury Selivanov <yselivanov@sprymix.com>, Larry Hastings <larry@hastings.org>
|
2006-08-21 19:54:07 -04:00
|
|
|
|
Status: Draft
|
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 21-Aug-2006
|
2012-06-05 20:20:53 -04:00
|
|
|
|
Python-Version: 3.3
|
|
|
|
|
Post-History: 04-Jun-2012
|
2006-08-21 19:54:07 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
Python has always supported powerful introspection capabilities,
|
2012-06-05 20:20:53 -04:00
|
|
|
|
including introspecting functions and methods. (For the rest of
|
|
|
|
|
this PEP, "function" refers to both functions and methods). By
|
|
|
|
|
examining a function object you can fully reconstruct the function's
|
|
|
|
|
signature. Unfortunately this information is stored in an inconvenient
|
|
|
|
|
manner, and is spread across a half-dozen deeply nested attributes.
|
|
|
|
|
|
|
|
|
|
This PEP proposes a new representation for function signatures.
|
|
|
|
|
The new representation contains all necessary information about a function
|
|
|
|
|
and its parameters, and makes introspection easy and straightforward.
|
|
|
|
|
|
|
|
|
|
However, this object does not replace the existing function
|
|
|
|
|
metadata, which is used by Python itself to execute those
|
|
|
|
|
functions. The new metadata object is intended solely to make
|
|
|
|
|
function introspection easier for Python programmers.
|
2007-09-07 21:07:12 -04:00
|
|
|
|
|
|
|
|
|
|
2006-08-21 19:54:07 -04:00
|
|
|
|
Signature Object
|
|
|
|
|
================
|
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
A Signature object represents the overall signature of a function.
|
|
|
|
|
It stores a `Parameter object`_ for each parameter accepted by the
|
|
|
|
|
function, as well as information specific to the function itself.
|
2006-08-21 19:54:07 -04:00
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
A Signature object has the following public attributes and methods:
|
2006-08-21 19:54:07 -04:00
|
|
|
|
|
2007-02-26 10:30:51 -05:00
|
|
|
|
* name : str
|
2012-06-05 20:20:53 -04:00
|
|
|
|
Name of the function.
|
|
|
|
|
* qualname : str
|
|
|
|
|
Fully qualified name of the function.
|
2007-11-16 23:20:22 -05:00
|
|
|
|
* return_annotation : object
|
2012-06-05 20:20:53 -04:00
|
|
|
|
The annotation for the return type of the function if specified.
|
|
|
|
|
If the function has no annotation for its return type, this
|
|
|
|
|
attribute is not set.
|
|
|
|
|
* parameters : OrderedDict
|
|
|
|
|
An ordered mapping of parameters' names to the corresponding
|
|
|
|
|
Parameter objects (keyword-only arguments are in the same order
|
|
|
|
|
as listed in ``code.co_varnames``).
|
|
|
|
|
* bind(\*args, \*\*kwargs) -> BoundArguments
|
|
|
|
|
Creates a mapping from positional and keyword arguments to
|
2012-06-07 10:18:08 -04:00
|
|
|
|
parameters. Raises a ``BindError`` if the passed arguments
|
|
|
|
|
do not match the signature.
|
2012-06-05 20:20:53 -04:00
|
|
|
|
|
|
|
|
|
Changes to the Signature object, or to any of its data members,
|
|
|
|
|
do not affect the function itself.
|
2006-08-21 19:54:07 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Parameter Object
|
|
|
|
|
================
|
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
Python's expressive syntax means functions can accept many different
|
|
|
|
|
kinds of parameters with many subtle semantic differences. We
|
|
|
|
|
propose a rich Parameter object designed to represent any possible
|
|
|
|
|
function parameter.
|
2006-08-21 19:54:07 -04:00
|
|
|
|
|
|
|
|
|
The structure of the Parameter object is:
|
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
* name : str
|
|
|
|
|
The name of the parameter as a string.
|
|
|
|
|
* default : object
|
|
|
|
|
The default value for the parameter if specified. If the
|
|
|
|
|
parameter has no default value, this attribute is not set.
|
|
|
|
|
* annotation : object
|
|
|
|
|
The annotation for the parameter if specified. If the
|
|
|
|
|
parameter has no annotation, this attribute is not set.
|
|
|
|
|
* is_keyword_only : bool
|
2007-02-26 10:30:51 -05:00
|
|
|
|
True if the parameter is keyword-only, else False.
|
2012-06-05 20:20:53 -04:00
|
|
|
|
* is_args : bool
|
|
|
|
|
True if the parameter accepts variable number of arguments
|
2012-06-07 10:18:08 -04:00
|
|
|
|
(``*args``-like), else False.
|
2012-06-05 20:20:53 -04:00
|
|
|
|
* is_kwargs : bool
|
|
|
|
|
True if the parameter accepts variable number of keyword
|
2012-06-07 10:18:08 -04:00
|
|
|
|
arguments (``**kwargs``-like), else False.
|
2012-06-05 20:20:53 -04:00
|
|
|
|
* is_implemented : bool
|
|
|
|
|
True if the parameter is implemented for use. Some platforms
|
|
|
|
|
implement functions but can't support specific parameters
|
2012-06-07 10:18:08 -04:00
|
|
|
|
(e.g. "mode" for ``os.mkdir``). Passing in an unimplemented
|
2012-06-05 20:20:53 -04:00
|
|
|
|
parameter may result in the parameter being ignored,
|
|
|
|
|
or in NotImplementedError being raised. It is intended that
|
|
|
|
|
all conditions where ``is_implemented`` may be False be
|
|
|
|
|
thoroughly documented.
|
|
|
|
|
|
2012-06-07 10:18:08 -04:00
|
|
|
|
Parameter objects support testing for equality. Two Parameter
|
|
|
|
|
objects are equal, when all their properties are equal. Those
|
|
|
|
|
who need to test if one signature has the same parameters as
|
|
|
|
|
another, can do a direct comparison of ``Signature.parameters``
|
|
|
|
|
collections: ``signature(foo).parameters == signature(bar).parameters``.
|
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
|
|
|
|
|
BoundArguments Object
|
|
|
|
|
=====================
|
|
|
|
|
|
|
|
|
|
Result of a ``Signature.bind`` call. Holds the mapping of arguments
|
|
|
|
|
to the function's parameters.
|
|
|
|
|
|
|
|
|
|
Has the following public attributes:
|
|
|
|
|
|
|
|
|
|
* arguments : OrderedDict
|
|
|
|
|
An ordered mutable mapping of parameters' names to arguments' values.
|
|
|
|
|
Does not contain arguments' default values.
|
|
|
|
|
* args : tuple
|
|
|
|
|
Tuple of positional arguments values. Dynamically computed from
|
|
|
|
|
the 'arguments' attribute.
|
|
|
|
|
* kwargs : dict
|
|
|
|
|
Dict of keyword arguments values. Dynamically computed from
|
|
|
|
|
the 'arguments' attribute.
|
|
|
|
|
|
|
|
|
|
The ``arguments`` attribute should be used in conjunction with
|
|
|
|
|
``Signature.parameters`` for any arguments processing purposes.
|
|
|
|
|
|
|
|
|
|
``args`` and ``kwargs`` properties should be used to invoke functions:
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
def test(a, *, b):
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
sig = signature(test)
|
|
|
|
|
ba = sig.bind(10, b=20)
|
|
|
|
|
test(*ba.args, **ba.kwargs)
|
2006-08-21 19:54:07 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Implementation
|
|
|
|
|
==============
|
|
|
|
|
|
2012-06-07 10:18:08 -04:00
|
|
|
|
The implementation adds a new function ``signature()`` to the ``inspect``
|
|
|
|
|
module. The function is the preferred way of getting a ``Signature`` for
|
|
|
|
|
a callable object.
|
|
|
|
|
|
|
|
|
|
The function implements the following algorithm:
|
|
|
|
|
|
|
|
|
|
- If the object is not callable - raise a TypeError
|
|
|
|
|
|
|
|
|
|
- If the object has a ``__signature__`` attribute and if it
|
|
|
|
|
is not ``None`` - return it
|
|
|
|
|
|
|
|
|
|
- If it is ``None`` and the object is an instance of
|
|
|
|
|
``BuiltinFunction``, raise a ``ValueError``
|
|
|
|
|
|
|
|
|
|
- If the object is a an instance of ``FunctionType``:
|
|
|
|
|
|
|
|
|
|
- If it has a ``__wrapped__`` attribute, return
|
|
|
|
|
``signature(object.__wrapped__)``
|
|
|
|
|
|
|
|
|
|
- Or else construct a new ``Signature`` object and return it
|
|
|
|
|
|
2012-06-07 10:25:42 -04:00
|
|
|
|
- if the object is a method or a classmethod, construct and return
|
|
|
|
|
a new ``Signature`` object, with its first parameter (usually
|
|
|
|
|
``self`` or ``cls``) removed
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
|
|
|
|
- If the object is a class return ``signature(object.__init__)``
|
|
|
|
|
|
2012-06-07 10:25:42 -04:00
|
|
|
|
- If the object is an instance of ``functools.partial``, construct
|
|
|
|
|
a new ``Signature`` from its ``partial.func`` attribute, and
|
|
|
|
|
account for already bound ``partial.args`` and ``partial.kwargs``
|
|
|
|
|
|
2012-06-07 10:18:08 -04:00
|
|
|
|
- Return ``signature(object.__call__)``
|
|
|
|
|
|
|
|
|
|
Note, that the ``Signature`` object is created in a lazy manner, and
|
|
|
|
|
is not automatically cached.
|
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
An implementation for Python 3.3 can be found here: [#impl]_.
|
|
|
|
|
A python issue was also created: [#issue]_.
|
|
|
|
|
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
|
|
|
|
Design Considerations
|
|
|
|
|
=====================
|
|
|
|
|
|
|
|
|
|
No Implicit Caching of Signature Objects
|
|
|
|
|
----------------------------------------
|
|
|
|
|
|
|
|
|
|
The first PEP design had a provision for implicit caching of ``Signature``
|
|
|
|
|
objects in the ``inspect.signature()`` function. However, this has the
|
|
|
|
|
following downsides:
|
|
|
|
|
|
|
|
|
|
* If the ``Signature`` object is cached then any changes to the function
|
|
|
|
|
it describes will not be reflected in it. However, If the caching is
|
|
|
|
|
needed, it can be always done manually and explicitly
|
|
|
|
|
|
|
|
|
|
* It is better to reserve the ``__signature__`` attribute for the cases
|
|
|
|
|
when there is a need to explicitly set to a ``Signature`` object that
|
|
|
|
|
is different from the actual one
|
2006-08-21 19:54:07 -04:00
|
|
|
|
|
|
|
|
|
|
2007-11-16 23:20:22 -05:00
|
|
|
|
Examples
|
|
|
|
|
========
|
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
Function Signature Renderer
|
|
|
|
|
---------------------------
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
def render_signature(signature):
|
|
|
|
|
'''Renders function definition by its signature.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
|
|
>>> def test(a:'foo', *, b:'bar', c=True, **kwargs:None) -> 'spam':
|
|
|
|
|
... pass
|
|
|
|
|
|
|
|
|
|
>>> render_signature(inspect.signature(test))
|
|
|
|
|
test(a:'foo', *, b:'bar', c=True, **kwargs:None) -> 'spam'
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
result = []
|
|
|
|
|
render_kw_only_separator = True
|
|
|
|
|
for param in signature.parameters.values():
|
|
|
|
|
formatted = param.name
|
|
|
|
|
|
|
|
|
|
# Add annotation and default value
|
|
|
|
|
if hasattr(param, 'annotation'):
|
|
|
|
|
formatted = '{}:{!r}'.format(formatted, param.annotation)
|
|
|
|
|
if hasattr(param, 'default'):
|
|
|
|
|
formatted = '{}={!r}'.format(formatted, param.default)
|
|
|
|
|
|
|
|
|
|
# Handle *args and **kwargs -like parameters
|
|
|
|
|
if param.is_args:
|
|
|
|
|
formatted = '*' + formatted
|
|
|
|
|
elif param.is_kwargs:
|
|
|
|
|
formatted = '**' + formatted
|
|
|
|
|
|
|
|
|
|
if param.is_args:
|
|
|
|
|
# OK, we have an '*args'-like parameter, so we won't need
|
|
|
|
|
# a '*' to separate keyword-only arguments
|
|
|
|
|
render_kw_only_separator = False
|
|
|
|
|
elif param.is_keyword_only and render_kw_only_separator:
|
|
|
|
|
# We have a keyword-only parameter to render and we haven't
|
|
|
|
|
# rendered an '*args'-like parameter before, so add a '*'
|
|
|
|
|
# separator to the parameters list ("foo(arg1, *, arg2)" case)
|
|
|
|
|
result.append('*')
|
|
|
|
|
# This condition should be only triggered once, so
|
|
|
|
|
# reset the flag
|
|
|
|
|
render_kw_only_separator = False
|
|
|
|
|
|
|
|
|
|
result.append(formatted)
|
|
|
|
|
|
|
|
|
|
rendered = '{}({})'.format(signature.name, ', '.join(result))
|
|
|
|
|
|
|
|
|
|
if hasattr(signature, 'return_annotation'):
|
|
|
|
|
rendered += ' -> {!r}'.format(signature.return_annotation)
|
|
|
|
|
|
|
|
|
|
return rendered
|
|
|
|
|
|
|
|
|
|
|
2007-11-16 23:20:22 -05:00
|
|
|
|
Annotation Checker
|
|
|
|
|
------------------
|
|
|
|
|
::
|
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
import inspect
|
|
|
|
|
import functools
|
|
|
|
|
|
|
|
|
|
def checktypes(func):
|
|
|
|
|
'''Decorator to verify arguments and return types
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
|
|
>>> @checktypes
|
|
|
|
|
... def test(a:int, b:str) -> int:
|
|
|
|
|
... return int(a * b)
|
|
|
|
|
|
|
|
|
|
>>> test(10, '1')
|
|
|
|
|
1111111111
|
|
|
|
|
|
|
|
|
|
>>> test(10, 1)
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
...
|
|
|
|
|
ValueError: foo: wrong type of 'b' argument, 'str' expected, got 'int'
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
sig = inspect.signature(func)
|
|
|
|
|
|
|
|
|
|
types = {}
|
|
|
|
|
for param in sig.parameters.values():
|
|
|
|
|
# Iterate through function's parameters and build the list of
|
|
|
|
|
# arguments types
|
2007-11-16 23:20:22 -05:00
|
|
|
|
try:
|
2012-06-05 20:20:53 -04:00
|
|
|
|
type_ = param.annotation
|
|
|
|
|
except AttributeError:
|
|
|
|
|
continue
|
2007-11-16 23:20:22 -05:00
|
|
|
|
else:
|
2012-06-05 20:20:53 -04:00
|
|
|
|
if not inspect.isclass(type_):
|
|
|
|
|
# Not a type, skip it
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
types[param.name] = type_
|
|
|
|
|
|
|
|
|
|
# If the argument has a type specified, let's check that its
|
|
|
|
|
# default value (if present) conforms with the type.
|
2007-11-16 23:20:22 -05:00
|
|
|
|
try:
|
2012-06-05 20:20:53 -04:00
|
|
|
|
default = param.default
|
2007-11-16 23:20:22 -05:00
|
|
|
|
except AttributeError:
|
|
|
|
|
continue
|
2012-06-05 20:20:53 -04:00
|
|
|
|
else:
|
|
|
|
|
if not isinstance(default, type_):
|
|
|
|
|
raise ValueError("{func}: wrong type of a default value for {arg!r}". \
|
|
|
|
|
format(func=sig.qualname, arg=param.name))
|
|
|
|
|
|
|
|
|
|
def check_type(sig, arg_name, arg_type, arg_value):
|
|
|
|
|
# Internal function that incapsulates arguments type checking
|
|
|
|
|
if not isinstance(arg_value, arg_type):
|
|
|
|
|
raise ValueError("{func}: wrong type of {arg!r} argument, " \
|
|
|
|
|
"{exp!r} expected, got {got!r}". \
|
|
|
|
|
format(func=sig.qualname, arg=arg_name,
|
|
|
|
|
exp=arg_type.__name__, got=type(arg_value).__name__))
|
|
|
|
|
|
|
|
|
|
@functools.wraps(func)
|
|
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
|
# Let's bind the arguments
|
|
|
|
|
ba = sig.bind(*args, **kwargs)
|
|
|
|
|
for arg_name, arg in ba.arguments.items():
|
|
|
|
|
# And iterate through the bound arguments
|
2007-11-16 23:20:22 -05:00
|
|
|
|
try:
|
2012-06-05 20:20:53 -04:00
|
|
|
|
type_ = types[arg_name]
|
|
|
|
|
except KeyError:
|
|
|
|
|
continue
|
|
|
|
|
else:
|
|
|
|
|
# OK, we have a type for the argument, lets get the corresponding
|
|
|
|
|
# parameter description from the signature object
|
|
|
|
|
param = sig.parameters[arg_name]
|
|
|
|
|
if param.is_args:
|
|
|
|
|
# If this parameter is a variable-argument parameter,
|
|
|
|
|
# then we need to check each of its values
|
|
|
|
|
for value in arg:
|
|
|
|
|
check_type(sig, arg_name, type_, value)
|
|
|
|
|
elif param.is_kwargs:
|
|
|
|
|
# If this parameter is a variable-keyword-argument parameter:
|
|
|
|
|
for subname, value in arg.items():
|
|
|
|
|
check_type(sig, arg_name + ':' + subname, type_, value)
|
|
|
|
|
else:
|
|
|
|
|
# And, finally, if this parameter a regular one:
|
|
|
|
|
check_type(sig, arg_name, type_, arg)
|
|
|
|
|
|
|
|
|
|
result = func(*ba.args, **ba.kwargs)
|
|
|
|
|
# The last bit - let's check that the result is correct
|
|
|
|
|
try:
|
|
|
|
|
return_type = sig.return_annotation
|
|
|
|
|
except AttributeError:
|
|
|
|
|
# Looks like we don't have any restriction on the return type
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
if isinstance(return_type, type) and not isinstance(result, return_type):
|
|
|
|
|
raise ValueError('{func}: wrong return type, {exp} expected, got {got}'. \
|
|
|
|
|
format(func=sig.qualname, exp=return_type.__name__,
|
|
|
|
|
got=type(result).__name__))
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
return wrapper
|
2007-11-16 23:20:22 -05:00
|
|
|
|
|
|
|
|
|
|
2007-02-26 10:30:51 -05:00
|
|
|
|
References
|
|
|
|
|
==========
|
2006-08-22 18:03:11 -04:00
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
.. [#impl] pep362 branch (https://bitbucket.org/1st1/cpython/overview)
|
|
|
|
|
.. [#issue] issue 15008 (http://bugs.python.org/issue15008)
|
2006-12-30 01:17:05 -05:00
|
|
|
|
|
2006-08-21 19:54:07 -04:00
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
coding: utf-8
|
|
|
|
|
End:
|