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>,
|
2019-09-12 07:36:34 -04:00
|
|
|
|
Yury Selivanov <yury@edgedb.com>, Larry Hastings <larry@hastings.org>
|
2012-06-22 18:16:35 -04:00
|
|
|
|
Status: Final
|
2006-08-21 19:54:07 -04:00
|
|
|
|
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
|
2017-06-11 15:02:39 -04:00
|
|
|
|
Resolution: https://mail.python.org/pipermail/python-dev/2012-June/120682.html
|
2006-08-21 19:54:07 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
Python has always supported powerful introspection capabilities,
|
2012-06-13 22:15:01 -04:00
|
|
|
|
including introspecting functions and methods (for the rest of
|
2012-06-05 20:20:53 -04:00
|
|
|
|
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-13 22:15:01 -04:00
|
|
|
|
A Signature object represents the call signature of a function and
|
|
|
|
|
its return annotation. For each parameter accepted by the function
|
|
|
|
|
it stores a `Parameter object`_ in its ``parameters`` collection.
|
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
|
|
|
|
|
2022-05-08 15:28:39 -04:00
|
|
|
|
* return_annotation \: object
|
2012-06-21 04:44:15 -04:00
|
|
|
|
The "return" annotation for the function. If the function
|
2012-06-22 16:12:59 -04:00
|
|
|
|
has no "return" annotation, this attribute is set to
|
|
|
|
|
``Signature.empty``.
|
2012-06-21 04:44:15 -04:00
|
|
|
|
|
2022-05-08 15:28:39 -04:00
|
|
|
|
* parameters \: OrderedDict
|
2012-06-05 20:20:53 -04:00
|
|
|
|
An ordered mapping of parameters' names to the corresponding
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Parameter objects.
|
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
* bind(\*args, \*\*kwargs) -> BoundArguments
|
|
|
|
|
Creates a mapping from positional and keyword arguments to
|
2012-06-15 13:56:20 -04:00
|
|
|
|
parameters. Raises a ``TypeError`` if the passed arguments do
|
|
|
|
|
not match the signature.
|
2012-06-21 04:44:15 -04:00
|
|
|
|
|
2012-06-13 22:15:01 -04:00
|
|
|
|
* bind_partial(\*args, \*\*kwargs) -> BoundArguments
|
|
|
|
|
Works the same way as ``bind()``, but allows the omission
|
|
|
|
|
of some required arguments (mimics ``functools.partial``
|
2012-06-15 13:56:20 -04:00
|
|
|
|
behavior.) Raises a ``TypeError`` if the passed arguments do
|
|
|
|
|
not match the signature.
|
2012-06-13 22:15:01 -04:00
|
|
|
|
|
2012-06-22 16:12:59 -04:00
|
|
|
|
* replace(parameters=<optional>, \*, return_annotation=<optional>) -> Signature
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Creates a new Signature instance based on the instance
|
|
|
|
|
``replace`` was invoked on. It is possible to pass different
|
|
|
|
|
``parameters`` and/or ``return_annotation`` to override the
|
|
|
|
|
corresponding properties of the base signature. To remove
|
|
|
|
|
``return_annotation`` from the copied ``Signature``, pass in
|
|
|
|
|
``Signature.empty``.
|
|
|
|
|
|
2012-06-22 16:12:59 -04:00
|
|
|
|
Note that the '=<optional>' notation, means that the argument is
|
|
|
|
|
optional. This notation applies to the rest of this PEP.
|
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Signature objects are immutable. Use ``Signature.replace()`` to
|
|
|
|
|
make a modified copy:
|
|
|
|
|
::
|
|
|
|
|
|
2012-06-21 13:06:19 -04:00
|
|
|
|
>>> def foo() -> None:
|
|
|
|
|
... pass
|
2012-06-21 04:44:15 -04:00
|
|
|
|
>>> sig = signature(foo)
|
2012-06-21 13:06:19 -04:00
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
>>> new_sig = sig.replace(return_annotation="new return annotation")
|
|
|
|
|
>>> new_sig is not sig
|
|
|
|
|
True
|
2012-06-21 13:06:19 -04:00
|
|
|
|
>>> new_sig.return_annotation != sig.return_annotation
|
2012-06-21 04:44:15 -04:00
|
|
|
|
True
|
|
|
|
|
>>> new_sig.parameters == sig.parameters
|
|
|
|
|
True
|
|
|
|
|
|
2012-06-21 13:06:19 -04:00
|
|
|
|
>>> new_sig = new_sig.replace(return_annotation=new_sig.empty)
|
2012-09-21 11:08:27 -04:00
|
|
|
|
>>> new_sig.return_annotation is Signature.empty
|
|
|
|
|
True
|
2012-06-21 13:06:19 -04:00
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
There are two ways to instantiate a Signature class:
|
|
|
|
|
|
2012-06-22 16:12:59 -04:00
|
|
|
|
* Signature(parameters=<optional>, \*, return_annotation=Signature.empty)
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Default Signature constructor. Accepts an optional sequence
|
|
|
|
|
of ``Parameter`` objects, and an optional ``return_annotation``.
|
|
|
|
|
Parameters sequence is validated to check that there are no
|
|
|
|
|
parameters with duplicate names, and that the parameters
|
|
|
|
|
are in the right order, i.e. positional-only first, then
|
|
|
|
|
positional-or-keyword, etc.
|
|
|
|
|
* Signature.from_function(function)
|
|
|
|
|
Returns a Signature object reflecting the signature of the
|
|
|
|
|
function passed in.
|
|
|
|
|
|
2012-06-19 05:38:15 -04:00
|
|
|
|
It's possible to test Signatures for equality. Two signatures are
|
|
|
|
|
equal when their parameters are equal, their positional and
|
|
|
|
|
positional-only parameters appear in the same order, and they
|
|
|
|
|
have equal return annotations.
|
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
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Signature also implements ``__str__``:
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
>>> str(Signature.from_function((lambda *args: None)))
|
|
|
|
|
'(*args)'
|
|
|
|
|
|
|
|
|
|
>>> str(Signature())
|
|
|
|
|
'()'
|
2012-06-19 05:38:15 -04:00
|
|
|
|
|
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
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
A Parameter object has the following public attributes and methods:
|
2006-08-21 19:54:07 -04:00
|
|
|
|
|
2022-05-08 15:28:39 -04:00
|
|
|
|
* name \: str
|
2012-06-21 04:44:15 -04:00
|
|
|
|
The name of the parameter as a string. Must be a valid
|
|
|
|
|
python identifier name (with the exception of ``POSITIONAL_ONLY``
|
|
|
|
|
parameters, which can have it set to ``None``.)
|
2012-06-15 13:56:20 -04:00
|
|
|
|
|
2022-05-08 15:28:39 -04:00
|
|
|
|
* default \: object
|
2012-06-21 04:44:15 -04:00
|
|
|
|
The default value for the parameter. If the parameter has no
|
2012-06-22 16:12:59 -04:00
|
|
|
|
default value, this attribute is set to ``Parameter.empty``.
|
2012-06-15 13:56:20 -04:00
|
|
|
|
|
2022-05-08 15:28:39 -04:00
|
|
|
|
* annotation \: object
|
2012-06-21 04:44:15 -04:00
|
|
|
|
The annotation for the parameter. If the parameter has no
|
2012-06-22 16:12:59 -04:00
|
|
|
|
annotation, this attribute is set to ``Parameter.empty``.
|
2012-06-15 13:56:20 -04:00
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
* kind
|
2012-06-15 13:56:20 -04:00
|
|
|
|
Describes how argument values are bound to the parameter.
|
|
|
|
|
Possible values:
|
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* ``Parameter.POSITIONAL_ONLY`` - value must be supplied
|
|
|
|
|
as a positional argument.
|
2012-06-15 13:56:20 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
Python has no explicit syntax for defining positional-only
|
|
|
|
|
parameters, but many built-in and extension module functions
|
|
|
|
|
(especially those that accept only one or two parameters)
|
|
|
|
|
accept them.
|
2012-06-15 13:56:20 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* ``Parameter.POSITIONAL_OR_KEYWORD`` - value may be
|
|
|
|
|
supplied as either a keyword or positional argument
|
|
|
|
|
(this is the standard binding behaviour for functions
|
|
|
|
|
implemented in Python.)
|
2012-06-15 13:56:20 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* ``Parameter.KEYWORD_ONLY`` - value must be supplied
|
|
|
|
|
as a keyword argument. Keyword only parameters are those
|
|
|
|
|
which appear after a "*" or "\*args" entry in a Python
|
|
|
|
|
function definition.
|
2012-06-15 13:56:20 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* ``Parameter.VAR_POSITIONAL`` - a tuple of positional
|
|
|
|
|
arguments that aren't bound to any other parameter.
|
|
|
|
|
This corresponds to a "\*args" parameter in a Python
|
|
|
|
|
function definition.
|
2012-06-15 13:56:20 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* ``Parameter.VAR_KEYWORD`` - a dict of keyword arguments
|
|
|
|
|
that aren't bound to any other parameter. This corresponds
|
|
|
|
|
to a "\*\*kwargs" parameter in a Python function definition.
|
2012-06-15 13:56:20 -04:00
|
|
|
|
|
2012-06-21 13:06:19 -04:00
|
|
|
|
Always use ``Parameter.*`` constants for setting and checking
|
|
|
|
|
value of the ``kind`` attribute.
|
|
|
|
|
|
2012-06-22 16:12:59 -04:00
|
|
|
|
* replace(\*, name=<optional>, kind=<optional>, default=<optional>, annotation=<optional>) -> Parameter
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Creates a new Parameter instance based on the instance
|
|
|
|
|
``replaced`` was invoked on. To override a Parameter
|
|
|
|
|
attribute, pass the corresponding argument. To remove
|
|
|
|
|
an attribute from a ``Parameter``, pass ``Parameter.empty``.
|
|
|
|
|
|
|
|
|
|
|
2012-06-21 13:06:19 -04:00
|
|
|
|
Parameter constructor:
|
|
|
|
|
|
2012-06-22 16:12:59 -04:00
|
|
|
|
* Parameter(name, kind, \*, annotation=Parameter.empty, default=Parameter.empty)
|
2012-06-21 13:06:19 -04:00
|
|
|
|
Instantiates a Parameter object. ``name`` and ``kind`` are required,
|
|
|
|
|
while ``annotation`` and ``default`` are optional.
|
|
|
|
|
|
2012-06-19 05:38:15 -04:00
|
|
|
|
Two parameters are equal when they have equal names, kinds, defaults,
|
|
|
|
|
and annotations.
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Parameter objects are immutable. Instead of modifying a Parameter object,
|
|
|
|
|
you can use ``Parameter.replace()`` to create a modified copy like so:
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
>>> param = Parameter('foo', Parameter.KEYWORD_ONLY, default=42)
|
|
|
|
|
>>> str(param)
|
|
|
|
|
'foo=42'
|
|
|
|
|
|
|
|
|
|
>>> str(param.replace())
|
|
|
|
|
'foo=42'
|
|
|
|
|
|
|
|
|
|
>>> str(param.replace(default=Parameter.empty, annotation='spam'))
|
|
|
|
|
"foo:'spam'"
|
|
|
|
|
|
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:
|
|
|
|
|
|
2022-05-08 15:28:39 -04:00
|
|
|
|
* arguments \: OrderedDict
|
2012-06-13 22:15:01 -04:00
|
|
|
|
An ordered, mutable mapping of parameters' names to arguments' values.
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Contains only explicitly bound arguments. Arguments for
|
|
|
|
|
which ``bind()`` relied on a default value are skipped.
|
2022-05-08 15:28:39 -04:00
|
|
|
|
* args \: tuple
|
2012-06-05 20:20:53 -04:00
|
|
|
|
Tuple of positional arguments values. Dynamically computed from
|
|
|
|
|
the 'arguments' attribute.
|
2022-05-08 15:28:39 -04:00
|
|
|
|
* kwargs \: dict
|
2012-06-05 20:20:53 -04:00
|
|
|
|
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.
|
|
|
|
|
|
2012-06-13 22:15:01 -04:00
|
|
|
|
``args`` and ``kwargs`` properties can be used to invoke functions:
|
2012-06-05 20:20:53 -04:00
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Arguments which could be passed as part of either ``*args`` or ``**kwargs``
|
|
|
|
|
will be included only in the ``BoundArguments.args`` attribute. Consider the
|
|
|
|
|
following example:
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
def test(a=1, b=2, c=3):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
sig = signature(test)
|
|
|
|
|
ba = sig.bind(a=10, c=13)
|
|
|
|
|
|
|
|
|
|
>>> ba.args
|
|
|
|
|
(10,)
|
|
|
|
|
|
|
|
|
|
>>> ba.kwargs:
|
|
|
|
|
{'c': 13}
|
|
|
|
|
|
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:
|
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
- If the object is not callable - raise a TypeError
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
- If the object has a ``__signature__`` attribute and if it
|
|
|
|
|
is not ``None`` - return it
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
- If it has a ``__wrapped__`` attribute, return
|
|
|
|
|
``signature(object.__wrapped__)``
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
- If the object is an instance of ``FunctionType``, construct
|
|
|
|
|
and return a new ``Signature`` for it
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
- If the object is a bound method, construct and return a new ``Signature``
|
|
|
|
|
object, with its first parameter (usually ``self`` or ``cls``)
|
|
|
|
|
removed. (``classmethod`` and ``staticmethod`` are supported
|
|
|
|
|
too. Since both are descriptors, the former returns a bound method,
|
|
|
|
|
and the latter returns its wrapped function.)
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -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:25:42 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
- If the object is a class or metaclass:
|
2012-06-13 22:15:01 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
- If the object's type has a ``__call__`` method defined in
|
|
|
|
|
its MRO, return a Signature for it
|
2012-06-13 22:15:01 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
- If the object has a ``__new__`` method defined in its MRO,
|
|
|
|
|
return a Signature object for it
|
2012-06-13 22:15:01 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
- If the object has a ``__init__`` method defined in its MRO,
|
|
|
|
|
return a Signature object for it
|
2012-06-13 22:15:01 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
- Return ``signature(object.__call__)``
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Note that the ``Signature`` object is created in a lazy manner, and
|
2012-06-21 13:06:19 -04:00
|
|
|
|
is not automatically cached. However, the user can manually cache a
|
|
|
|
|
Signature by storing it in the ``__signature__`` attribute.
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
2012-06-13 22:15:01 -04:00
|
|
|
|
An implementation for Python 3.3 can be found at [#impl]_.
|
|
|
|
|
The python issue tracking the patch is [#issue]_.
|
2012-06-05 20:20:53 -04:00
|
|
|
|
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
|
|
|
|
Design Considerations
|
|
|
|
|
=====================
|
|
|
|
|
|
2012-06-13 22:15:01 -04:00
|
|
|
|
No implicit caching of Signature objects
|
2012-06-07 10:18:08 -04:00
|
|
|
|
----------------------------------------
|
|
|
|
|
|
|
|
|
|
The first PEP design had a provision for implicit caching of ``Signature``
|
|
|
|
|
objects in the ``inspect.signature()`` function. However, this has the
|
|
|
|
|
following downsides:
|
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* 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
|
2012-06-07 10:18:08 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* 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
|
|
|
|
|
|
|
|
|
|
2012-06-19 05:38:15 -04:00
|
|
|
|
Some functions may not be introspectable
|
|
|
|
|
----------------------------------------
|
|
|
|
|
|
|
|
|
|
Some functions may not be introspectable in certain implementations of
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Python. For example, in CPython, built-in functions defined in C provide
|
2012-06-19 05:38:15 -04:00
|
|
|
|
no metadata about their arguments. Adding support for them is out of
|
|
|
|
|
scope for this PEP.
|
|
|
|
|
|
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
Signature and Parameter equivalence
|
|
|
|
|
-----------------------------------
|
|
|
|
|
|
|
|
|
|
We assume that parameter names have semantic significance--two
|
2012-06-21 13:06:19 -04:00
|
|
|
|
signatures are equal only when their corresponding parameters are equal
|
|
|
|
|
and have the exact same names. Users who want looser equivalence tests,
|
|
|
|
|
perhaps ignoring names of VAR_KEYWORD or VAR_POSITIONAL parameters, will
|
2012-06-21 04:44:15 -04:00
|
|
|
|
need to implement those themselves.
|
|
|
|
|
|
|
|
|
|
|
2007-11-16 23:20:22 -05:00
|
|
|
|
Examples
|
|
|
|
|
========
|
|
|
|
|
|
2012-06-13 22:15:01 -04:00
|
|
|
|
Visualizing Callable Objects' Signature
|
|
|
|
|
---------------------------------------
|
2012-06-15 13:56:20 -04:00
|
|
|
|
|
|
|
|
|
Let's define some classes and functions:
|
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
::
|
|
|
|
|
|
2012-06-13 22:15:01 -04:00
|
|
|
|
from inspect import signature
|
|
|
|
|
from functools import partial, wraps
|
2012-06-05 20:20:53 -04:00
|
|
|
|
|
|
|
|
|
|
2012-06-13 22:15:01 -04:00
|
|
|
|
class FooMeta(type):
|
|
|
|
|
def __new__(mcls, name, bases, dct, *, bar:bool=False):
|
|
|
|
|
return super().__new__(mcls, name, bases, dct)
|
|
|
|
|
|
|
|
|
|
def __init__(cls, name, bases, dct, **kwargs):
|
|
|
|
|
return super().__init__(name, bases, dct)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Foo(metaclass=FooMeta):
|
|
|
|
|
def __init__(self, spam:int=42):
|
|
|
|
|
self.spam = spam
|
|
|
|
|
|
|
|
|
|
def __call__(self, a, b, *, c) -> tuple:
|
|
|
|
|
return a, b, c
|
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
@classmethod
|
|
|
|
|
def spam(cls, a):
|
|
|
|
|
return a
|
|
|
|
|
|
2012-06-13 22:15:01 -04:00
|
|
|
|
|
2012-06-15 13:56:20 -04:00
|
|
|
|
def shared_vars(*shared_args):
|
|
|
|
|
"""Decorator factory that defines shared variables that are
|
|
|
|
|
passed to every invocation of the function"""
|
|
|
|
|
|
|
|
|
|
def decorator(f):
|
|
|
|
|
@wraps(f)
|
2012-08-13 14:31:39 -04:00
|
|
|
|
def wrapper(*args, **kwargs):
|
2012-06-15 13:56:20 -04:00
|
|
|
|
full_args = shared_args + args
|
2012-08-13 14:31:39 -04:00
|
|
|
|
return f(*full_args, **kwargs)
|
2012-06-21 04:44:15 -04:00
|
|
|
|
|
2012-06-15 13:56:20 -04:00
|
|
|
|
# Override signature
|
2012-06-21 04:44:15 -04:00
|
|
|
|
sig = signature(f)
|
|
|
|
|
sig = sig.replace(tuple(sig.parameters.values())[1:])
|
|
|
|
|
wrapper.__signature__ = sig
|
|
|
|
|
|
2012-06-15 13:56:20 -04:00
|
|
|
|
return wrapper
|
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@shared_vars({})
|
|
|
|
|
def example(_state, a, b, c):
|
|
|
|
|
return _state, a, b, c
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def format_signature(obj):
|
|
|
|
|
return str(signature(obj))
|
|
|
|
|
|
2012-06-13 22:15:01 -04:00
|
|
|
|
|
2012-06-15 13:56:20 -04:00
|
|
|
|
Now, in the python REPL:
|
2012-06-13 22:15:01 -04:00
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
2012-06-15 13:56:20 -04:00
|
|
|
|
>>> format_signature(FooMeta)
|
|
|
|
|
'(name, bases, dct, *, bar:bool=False)'
|
|
|
|
|
|
|
|
|
|
>>> format_signature(Foo)
|
|
|
|
|
'(spam:int=42)'
|
|
|
|
|
|
|
|
|
|
>>> format_signature(Foo.__call__)
|
|
|
|
|
'(self, a, b, *, c) -> tuple'
|
|
|
|
|
|
|
|
|
|
>>> format_signature(Foo().__call__)
|
|
|
|
|
'(a, b, *, c) -> tuple'
|
|
|
|
|
|
2012-06-21 04:44:15 -04:00
|
|
|
|
>>> format_signature(Foo.spam)
|
|
|
|
|
'(a)'
|
|
|
|
|
|
2012-06-15 13:56:20 -04:00
|
|
|
|
>>> format_signature(partial(Foo().__call__, 1, c=3))
|
|
|
|
|
'(b, *, c=3) -> tuple'
|
|
|
|
|
|
|
|
|
|
>>> format_signature(partial(partial(Foo().__call__, 1, c=3), 2, c=20))
|
|
|
|
|
'(*, c=20) -> tuple'
|
|
|
|
|
|
|
|
|
|
>>> format_signature(example)
|
|
|
|
|
'(a, b, c)'
|
|
|
|
|
|
|
|
|
|
>>> format_signature(partial(example, 1, 2))
|
|
|
|
|
'(c)'
|
|
|
|
|
|
|
|
|
|
>>> format_signature(partial(partial(example, 1, b=2), c=3))
|
|
|
|
|
'(b=2, c=3)'
|
2012-06-05 20:20:53 -04:00
|
|
|
|
|
|
|
|
|
|
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
|
2012-06-22 16:12:59 -04:00
|
|
|
|
type_ = param.annotation
|
|
|
|
|
if type_ is param.empty or not inspect.isclass(type_):
|
|
|
|
|
# Missing annotation or 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.
|
2012-08-13 14:31:39 -04:00
|
|
|
|
if param.default is not param.empty and not isinstance(param.default, type_):
|
|
|
|
|
raise ValueError("{func}: wrong type of a default value for {arg!r}". \
|
|
|
|
|
format(func=func.__qualname__, arg=param.name))
|
2012-06-05 20:20:53 -04:00
|
|
|
|
|
|
|
|
|
def check_type(sig, arg_name, arg_type, arg_value):
|
2012-06-14 10:10:28 -04:00
|
|
|
|
# Internal function that encapsulates arguments type checking
|
2012-06-05 20:20:53 -04:00
|
|
|
|
if not isinstance(arg_value, arg_type):
|
|
|
|
|
raise ValueError("{func}: wrong type of {arg!r} argument, " \
|
|
|
|
|
"{exp!r} expected, got {got!r}". \
|
2012-06-15 13:56:20 -04:00
|
|
|
|
format(func=func.__qualname__, arg=arg_name,
|
2012-06-05 20:20:53 -04:00
|
|
|
|
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]
|
2012-06-15 13:56:20 -04:00
|
|
|
|
if param.kind == param.VAR_POSITIONAL:
|
2012-06-05 20:20:53 -04:00
|
|
|
|
# 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)
|
2012-06-15 13:56:20 -04:00
|
|
|
|
elif param.kind == param.VAR_KEYWORD:
|
2012-06-05 20:20:53 -04:00
|
|
|
|
# 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)
|
2012-06-22 16:12:59 -04:00
|
|
|
|
|
2012-06-05 20:20:53 -04:00
|
|
|
|
# The last bit - let's check that the result is correct
|
2012-06-22 16:12:59 -04:00
|
|
|
|
return_type = sig.return_annotation
|
|
|
|
|
if (return_type is not sig._empty and
|
|
|
|
|
isinstance(return_type, type) and
|
|
|
|
|
not isinstance(result, return_type)):
|
|
|
|
|
|
|
|
|
|
raise ValueError('{func}: wrong return type, {exp} expected, got {got}'. \
|
|
|
|
|
format(func=func.__qualname__, exp=return_type.__name__,
|
|
|
|
|
got=type(result).__name__))
|
2012-06-05 20:20:53 -04:00
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
return wrapper
|
2007-11-16 23:20:22 -05:00
|
|
|
|
|
2012-06-22 18:16:35 -04:00
|
|
|
|
Acceptance
|
|
|
|
|
==========
|
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
:pep:`362` was accepted by Guido, Friday, June 22, 2012 [#accepted]_ .
|
2012-06-22 18:16:35 -04:00
|
|
|
|
The reference implementation was committed to trunk later that day.
|
|
|
|
|
|
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)
|
2017-06-11 15:02:39 -04:00
|
|
|
|
.. [#accepted] "A Desperate Plea For Introspection (aka: BDFAP Needed)" (https://mail.python.org/pipermail/python-dev/2012-June/120682.html)
|
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:
|