The latest changes from Yury Selivanov. I can almost taste the acceptance!
This commit is contained in:
parent
11dadc9c41
commit
ff243784df
159
pep-0362.txt
159
pep-0362.txt
|
@ -42,23 +42,58 @@ it stores a `Parameter object`_ in its ``parameters`` collection.
|
|||
A Signature object has the following public attributes and methods:
|
||||
|
||||
* return_annotation : object
|
||||
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.
|
||||
The "return" annotation for the function. If the function
|
||||
has no "return" annotation, 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``).
|
||||
Parameter objects.
|
||||
|
||||
* bind(\*args, \*\*kwargs) -> BoundArguments
|
||||
Creates a mapping from positional and keyword arguments to
|
||||
parameters. Raises a ``TypeError`` if the passed arguments do
|
||||
not match the signature.
|
||||
|
||||
* bind_partial(\*args, \*\*kwargs) -> BoundArguments
|
||||
Works the same way as ``bind()``, but allows the omission
|
||||
of some required arguments (mimics ``functools.partial``
|
||||
behavior.) Raises a ``TypeError`` if the passed arguments do
|
||||
not match the signature.
|
||||
|
||||
* replace(parameters, \*, return_annotation) -> Signature
|
||||
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``.
|
||||
|
||||
Signature objects are immutable. Use ``Signature.replace()`` to
|
||||
make a modified copy:
|
||||
::
|
||||
|
||||
>>> sig = signature(foo)
|
||||
>>> new_sig = sig.replace(return_annotation="new return annotation")
|
||||
>>> new_sig is not sig
|
||||
True
|
||||
>>> new_sig.return_annotation == sig.return_annotation
|
||||
True
|
||||
>>> new_sig.parameters == sig.parameters
|
||||
True
|
||||
|
||||
There are two ways to instantiate a Signature class:
|
||||
|
||||
* Signature(parameters, *, return_annotation)
|
||||
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.
|
||||
|
||||
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
|
||||
|
@ -67,9 +102,14 @@ have equal return annotations.
|
|||
Changes to the Signature object, or to any of its data members,
|
||||
do not affect the function itself.
|
||||
|
||||
Signature also implements ``__str__`` and ``__copy__`` methods.
|
||||
The latter creates a shallow copy of Signature, with all Parameter
|
||||
objects copied as well.
|
||||
Signature also implements ``__str__``:
|
||||
::
|
||||
|
||||
>>> str(Signature.from_function((lambda *args: None)))
|
||||
'(*args)'
|
||||
|
||||
>>> str(Signature())
|
||||
'()'
|
||||
|
||||
|
||||
Parameter Object
|
||||
|
@ -80,20 +120,22 @@ kinds of parameters with many subtle semantic differences. We
|
|||
propose a rich Parameter object designed to represent any possible
|
||||
function parameter.
|
||||
|
||||
The structure of the Parameter object is:
|
||||
A Parameter object has the following public attributes and methods:
|
||||
|
||||
* name : str
|
||||
The name of the parameter as a string.
|
||||
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``.)
|
||||
|
||||
* default : object
|
||||
The default value for the parameter, if specified. If the
|
||||
parameter has no default value, this attribute is not set.
|
||||
The default value for the parameter. 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.
|
||||
The annotation for the parameter. If the parameter has no
|
||||
annotation, this attribute is not set.
|
||||
|
||||
* kind : str
|
||||
* kind
|
||||
Describes how argument values are bound to the parameter.
|
||||
Possible values:
|
||||
|
||||
|
@ -101,7 +143,7 @@ The structure of the Parameter object is:
|
|||
as a positional argument.
|
||||
|
||||
Python has no explicit syntax for defining positional-only
|
||||
parameters, but many builtin and extension module functions
|
||||
parameters, but many built-in and extension module functions
|
||||
(especially those that accept only one or two parameters)
|
||||
accept them.
|
||||
|
||||
|
@ -124,9 +166,30 @@ The structure of the Parameter object is:
|
|||
that aren't bound to any other parameter. This corresponds
|
||||
to a "\*\*kwds" parameter in a Python function definition.
|
||||
|
||||
* replace(\*, name, kind, default, annotation) -> Parameter
|
||||
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``.
|
||||
|
||||
|
||||
Two parameters are equal when they have equal names, kinds, defaults,
|
||||
and annotations.
|
||||
|
||||
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'"
|
||||
|
||||
|
||||
BoundArguments Object
|
||||
=====================
|
||||
|
@ -138,7 +201,8 @@ Has the following public attributes:
|
|||
|
||||
* arguments : OrderedDict
|
||||
An ordered, mutable mapping of parameters' names to arguments' values.
|
||||
Does not contain arguments' default values.
|
||||
Contains only explicitly bound arguments. Arguments for
|
||||
which ``bind()`` relied on a default value are skipped.
|
||||
* args : tuple
|
||||
Tuple of positional arguments values. Dynamically computed from
|
||||
the 'arguments' attribute.
|
||||
|
@ -159,6 +223,23 @@ The ``arguments`` attribute should be used in conjunction with
|
|||
ba = sig.bind(10, b=20)
|
||||
test(*ba.args, **ba.kwargs)
|
||||
|
||||
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}
|
||||
|
||||
|
||||
Implementation
|
||||
==============
|
||||
|
@ -172,7 +253,7 @@ 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 a shallow copy of it
|
||||
is not ``None`` - return it
|
||||
|
||||
- If it has a ``__wrapped__`` attribute, return
|
||||
``signature(object.__wrapped__)``
|
||||
|
@ -180,12 +261,9 @@ The function implements the following algorithm:
|
|||
- If the object is a an instance of ``FunctionType`` construct
|
||||
and return a new ``Signature`` for it
|
||||
|
||||
- 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
|
||||
|
||||
- If the object is a staticmethod, construct and return
|
||||
a new ``Signature`` object
|
||||
- If the object is a method, construct and return a new ``Signature``
|
||||
object, with its first parameter (usually ``self`` or ``cls``)
|
||||
removed
|
||||
|
||||
- If the object is an instance of ``functools.partial``, construct
|
||||
a new ``Signature`` from its ``partial.func`` attribute, and
|
||||
|
@ -196,15 +274,15 @@ The function implements the following algorithm:
|
|||
- If the object's type has a ``__call__`` method defined in
|
||||
its MRO, return a Signature for it
|
||||
|
||||
- If the object has a ``__new__`` method defined in its class,
|
||||
- If the object has a ``__new__`` method defined in its MRO,
|
||||
return a Signature object for it
|
||||
|
||||
- If the object has a ``__init__`` method defined in its class,
|
||||
- If the object has a ``__init__`` method defined in its MRO,
|
||||
return a Signature object for it
|
||||
|
||||
- Return ``signature(object.__call__)``
|
||||
|
||||
Note, that the ``Signature`` object is created in a lazy manner, and
|
||||
Note that the ``Signature`` object is created in a lazy manner, and
|
||||
is not automatically cached. If, however, the Signature object was
|
||||
explicitly cached by the user, ``signature()`` returns a new shallow copy
|
||||
of it on each invocation.
|
||||
|
@ -236,11 +314,21 @@ Some functions may not be introspectable
|
|||
----------------------------------------
|
||||
|
||||
Some functions may not be introspectable in certain implementations of
|
||||
Python. For example, in CPython, builtin functions defined in C provide
|
||||
Python. For example, in CPython, built-in functions defined in C provide
|
||||
no metadata about their arguments. Adding support for them is out of
|
||||
scope for this PEP.
|
||||
|
||||
|
||||
Signature and Parameter equivalence
|
||||
-----------------------------------
|
||||
|
||||
We assume that parameter names have semantic significance--two
|
||||
signatures are equal only when their corresponding parameters have
|
||||
the exact same names. Users who want looser equivalence tests, perhaps
|
||||
ignoring names of VAR_KEYWORD or VAR_POSITIONAL parameters, will
|
||||
need to implement those themselves.
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
|
@ -270,6 +358,10 @@ Let's define some classes and functions:
|
|||
def __call__(self, a, b, *, c) -> tuple:
|
||||
return a, b, c
|
||||
|
||||
@classmethod
|
||||
def spam(cls, a):
|
||||
return a
|
||||
|
||||
|
||||
def shared_vars(*shared_args):
|
||||
"""Decorator factory that defines shared variables that are
|
||||
|
@ -280,10 +372,12 @@ Let's define some classes and functions:
|
|||
def wrapper(*args, **kwds):
|
||||
full_args = shared_args + args
|
||||
return f(*full_args, **kwds)
|
||||
|
||||
# Override signature
|
||||
sig = wrapper.__signature__ = signature(f)
|
||||
for __ in shared_args:
|
||||
sig.parameters.popitem(last=False)
|
||||
sig = signature(f)
|
||||
sig = sig.replace(tuple(sig.parameters.values())[1:])
|
||||
wrapper.__signature__ = sig
|
||||
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
@ -313,6 +407,9 @@ Now, in the python REPL:
|
|||
>>> format_signature(Foo().__call__)
|
||||
'(a, b, *, c) -> tuple'
|
||||
|
||||
>>> format_signature(Foo.spam)
|
||||
'(a)'
|
||||
|
||||
>>> format_signature(partial(Foo().__call__, 1, c=3))
|
||||
'(b, *, c=3) -> tuple'
|
||||
|
||||
|
|
Loading…
Reference in New Issue