Update the PEP to be in line with the working implementation.
This commit is contained in:
parent
89710fca07
commit
9bb90a8fea
235
pep-0362.txt
235
pep-0362.txt
|
@ -17,49 +17,54 @@ Abstract
|
||||||
Python has always supported powerful introspection capabilities,
|
Python has always supported powerful introspection capabilities,
|
||||||
including that for functions and methods (for the rest of this PEP the
|
including that for functions and methods (for the rest of this PEP the
|
||||||
word "function" refers to both functions and methods). Taking a
|
word "function" refers to both functions and methods). Taking a
|
||||||
function object, you can fully reconstruct the function's signature
|
function object, you can fully reconstruct the function's signature.
|
||||||
using ``func_defaults``, ``func_code.co_argcount``,
|
Unfortunately it is a little unruly having to look at all the
|
||||||
``func_code.co_flags``, and ``func_code.co_varnames``. Unfortunately
|
different attributes to pull together complete information for a
|
||||||
it is a little unruly having to look at four different attributes
|
function's signature.
|
||||||
to pull together complete information for a function's signature.
|
|
||||||
|
|
||||||
This PEP proposes an object representation for function signatures.
|
This PEP proposes an object representation for function signatures.
|
||||||
This should help facilitate introspection on functions. It also helps
|
This should help facilitate introspection on functions for various
|
||||||
for introspection for decorators that wrap the function they are
|
usese (e.g., decorators). The introspection information contains all
|
||||||
applied to by allowing the wrapped function's signature object be set
|
possible information about the parameters in a signature (including
|
||||||
for the wrapping function.
|
Python 3.0 features).
|
||||||
|
|
||||||
|
|
||||||
Signature Object
|
Signature Object
|
||||||
================
|
================
|
||||||
|
|
||||||
The overall signature of an object is represented by the Signature
|
The overall signature of an object is represented by the Signature
|
||||||
object. This object is to store a `Parameter Object`_ for each
|
object. This object is to store a `Parameter object`_ for each
|
||||||
parameter in the signature. It is also to store any information
|
parameter in the signature. It is also to store any information
|
||||||
about the function itself that is pertinent to the signature.
|
about the function itself that is pertinent to the signature.
|
||||||
|
|
||||||
A Signature object has the following structure attributes:
|
A Signature object has the following structure attributes:
|
||||||
|
|
||||||
* name:str
|
* name : str
|
||||||
Name of the function. This is not fully qualified because
|
Name of the function. This is not fully qualified because
|
||||||
function objects for methods do not know the class they are
|
function objects for methods do not know the class they are
|
||||||
contained within. This makes functions and methods
|
contained within. This makes functions and methods
|
||||||
indistinguishable from one another when passed to decorators,
|
indistinguishable from one another when passed to decorators,
|
||||||
preventing proper creation of a fully qualified name.
|
preventing proper creation of a fully qualified name.
|
||||||
* var_args:str
|
* var_args : str
|
||||||
Name of the ``*args`` parameter, if present, or the empty
|
Name of the variable positional parameter (i.e., ``*args``), if
|
||||||
string.
|
present, or the empty string.
|
||||||
* var_kw_args:str
|
* var_kw_args : str
|
||||||
Name of the ``**kwargs`` parameter, if present, or the empty
|
Name of the variable keyword parameter (i.e., ``**kwargs``), if
|
||||||
string.
|
present, or the empty string.
|
||||||
* parameters:list(Parameter)
|
* var_annotations: dict(str, object)
|
||||||
|
Dict that contains the annotations for the variable parameters.
|
||||||
|
The keys are of the variable parameter with values of the
|
||||||
|
annotation. If an annotation does not exist for a variable
|
||||||
|
parameter then the key does not exist in the dict.
|
||||||
|
* parameters : list(Parameter)
|
||||||
List of the parameters of the function as represented by
|
List of the parameters of the function as represented by
|
||||||
Parameter objects (see `Parameter Object`_).
|
Parameter objects in the order of its definition (keyword-only
|
||||||
* __str__() -> str
|
arguments are in the order listed by ``code.co_varnames``).
|
||||||
Return the string representation of the signature as it might
|
* bind(\*args, \*\*kwargs) -> dict(str, Parameter)
|
||||||
appear in source code.
|
Create a mapping from arguments to parameters. The keys are the
|
||||||
* bind(\*args, \*\*kwargs) -> dict
|
names of the parameter that an argument maps to with the value
|
||||||
Create a mapping from parameter to argument for the signature.
|
being the value the parameter would have if this function was
|
||||||
|
called with the given arguments.
|
||||||
|
|
||||||
The Signature object is stored in the ``__signature__`` attribute of
|
The Signature object is stored in the ``__signature__`` attribute of
|
||||||
the function. When it is to be created is discussed in
|
the function. When it is to be created is discussed in
|
||||||
|
@ -69,167 +74,105 @@ the function. When it is to be created is discussed in
|
||||||
Parameter Object
|
Parameter Object
|
||||||
================
|
================
|
||||||
|
|
||||||
A function's signature is partially made up of several parameters.
|
A function's signature is made up of several parameters. Python's
|
||||||
Python's different kinds of parameters is quite large and rich and
|
different kinds of parameters is quite large and rich and continues to
|
||||||
continues to grow. This means that Parameter objects require they
|
grow. Parameter objects represent any possible parameter.
|
||||||
represent any possible parameter.
|
|
||||||
|
|
||||||
Originally the plan was to represent parameters using a list of
|
Originally the plan was to represent parameters using a list of
|
||||||
parameter names on the Signature object along with various dicts keyed
|
parameter names on the Signature object along with various dicts keyed
|
||||||
on parameter names to disseminate the various possible pieces of
|
on parameter names to disseminate the various pieces of information
|
||||||
information one can know about a parameter. But the decision was made
|
one can know about a parameter. But the decision was made to
|
||||||
to incorporate all information about a parameter in a single argument
|
incorporate all information about a parameter in a single object so
|
||||||
so as to make extending the information easier. This was originally
|
as to make extending the information easier. This was originally put
|
||||||
put forth by Talin and the preferred form of Guido (as discussed at
|
forth by Talin and the preferred form of Guido (as discussed at the
|
||||||
the 2006 Google Sprint).
|
2006 Google Sprint).
|
||||||
|
|
||||||
The structure of the Parameter object is:
|
The structure of the Parameter object is:
|
||||||
|
|
||||||
* name:(str | tuple(str))
|
* name : (str | tuple(str))
|
||||||
The name of the parameter as a string if it is not a tuple. If
|
The name of the parameter as a string if it is not a tuple. If
|
||||||
the argument is a tuple, use a tuple of strings where each item is
|
the argument is a tuple then a tuple of strings is used.
|
||||||
the name of the parameter contained within the tuple.
|
* position : int
|
||||||
* position:int
|
|
||||||
The position of the parameter within the signature of the
|
The position of the parameter within the signature of the
|
||||||
function (zero-indexed).
|
function (zero-indexed). For keyword-only parameters the position
|
||||||
* has_default:bool
|
value is arbitrary while not conflicting with positional
|
||||||
|
parameters. The suggestion of setting the attribute to None or -1
|
||||||
|
to represent keyword-only parameters was rejected to prevent
|
||||||
|
variable type usage and as a possible point of errors,
|
||||||
|
respectively.
|
||||||
|
* has_default : bool
|
||||||
True if the parameter has a default value, else False.
|
True if the parameter has a default value, else False.
|
||||||
* default_value:object
|
* default_value : object
|
||||||
The default value for the parameter, if present, else the
|
The default value for the parameter, if present, else the
|
||||||
attribute does not exist. This is done so that the attribute is
|
attribute does not exist. This is done so that the attribute is
|
||||||
not accidentally used if no default value is set as any default
|
not accidentally used if no default value is set as any default
|
||||||
value could be a legitimate default value itself.
|
value could be a legitimate default value itself.
|
||||||
* __str__() -> str
|
* keyword_only : bool
|
||||||
Return the string representation of the parameter as it might
|
True if the parameter is keyword-only, else False.
|
||||||
appear in source code in a function signature.
|
* has_annotation : bool
|
||||||
|
True if the parameter has an annotation, else False.
|
||||||
|
* annotation
|
||||||
|
Set to the annotation for the parameter. If ``has_annotation`` is
|
||||||
|
False then the attribute does not exist to prevent accidental use.
|
||||||
|
|
||||||
|
|
||||||
Implementation
|
Implementation
|
||||||
==============
|
==============
|
||||||
|
|
||||||
An implementation can be found in patch #1544909 [#impl]_. It
|
An implementation can be found in Python's sandbox [#impl]_.
|
||||||
modifies the 'inspect' module [#inspect-module]_to include the
|
There is a function named ``signature()`` which
|
||||||
implementation. There is a function named ``getsignature()`` which
|
returns the value stored on the ``__signature__`` attribute if it
|
||||||
returns the value stored on the ``__signature__`` attribute (for
|
exists, else it creates it bound to the Signature object for the
|
||||||
methods this is stored directly on the im_func function object since
|
function. For methods this is stored directly on the im_func function
|
||||||
that is what decorators will work with).
|
object since that is what decorators will work with.
|
||||||
|
|
||||||
For the `Open Issues`_ question of how to handle tuples, the current
|
|
||||||
implementation does the best it can to determine if the argument will
|
|
||||||
unpack properly, raising TypeError if it cannot reliably prove either
|
|
||||||
way if the argument can be unpacked.
|
|
||||||
|
|
||||||
|
|
||||||
Relation To Other PEPs
|
|
||||||
======================
|
|
||||||
|
|
||||||
Keyword-Only Arguments [#pep-3102]_
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
If keyword-only parameters come into existence, the Parameter object
|
|
||||||
will require modification. A ``keyword_only`` attribute will be added
|
|
||||||
that holds a boolean representing whether the parameter is
|
|
||||||
keyword-only or not.
|
|
||||||
|
|
||||||
Nick Coghlan suggested to set 'position' to None to signal that the
|
|
||||||
argument is keyword-only and thus remove the need for the new
|
|
||||||
attribute. But that would cause different types to be used in the
|
|
||||||
attribute that are in no way compatible. It also removes the ability
|
|
||||||
to know the position number within the signature from the Paramter
|
|
||||||
object itself. Plus Guido preferred the original approach over this
|
|
||||||
alternative. This does mean, though, that how to set the position of
|
|
||||||
an argument when ``*args`` is not at the end of the parameter list.
|
|
||||||
|
|
||||||
|
|
||||||
Function Annotations [#pep-3107]_
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
Support needs to be added for function annotations. One option is to
|
|
||||||
have two new attributes for each Parameter object: ``has_annotation``
|
|
||||||
and ``annotation``. This would remove any possible ambiguity in
|
|
||||||
terms of what an annotation could be.
|
|
||||||
|
|
||||||
But one could argue that the chances of someone setting an annotation
|
|
||||||
to ``None`` is very low and thus allows it to be used as a value
|
|
||||||
for a single ``annotation`` attribute to signify that no annotation
|
|
||||||
was set. But there is the slight issue of breaking from consistency
|
|
||||||
compared to ``has_default``/``default_value``.
|
|
||||||
|
|
||||||
Regardless of which approach is taken, Signature objects will also
|
|
||||||
need to gain support for annotations for ``*args`` and ``**kwargs``.
|
|
||||||
|
|
||||||
|
|
||||||
Open Issues
|
Open Issues
|
||||||
===========
|
===========
|
||||||
|
|
||||||
When to construct the Parameter object?
|
When to construct the Signature object?
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
The Parameter object can either be created in an eager or lazy
|
The Signature object can either be created in an eager or lazy
|
||||||
fashion. In the eager situation, the object can be created during
|
fashion. In the eager situation, the object can be created during
|
||||||
creation of the function object. In the lazy situation, one would
|
creation of the function object. In the lazy situation, one would
|
||||||
pass a function object to ``inspect.getsignature()`` and that would
|
pass a function object to a function and that would generate the
|
||||||
generate the Signature object and store it to ``__signature__`` if
|
Signature object and store it to ``__signature__`` if
|
||||||
needed, and then return the value of ``__signature__``.
|
needed, and then return the value of ``__signature__``.
|
||||||
|
|
||||||
|
|
||||||
How to handle tuples for ``Signature.bind()``?
|
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
Tuples pose an interesting problem for generating the mapping from
|
|
||||||
arguments to parameters. If one wants ``Signature.bind()`` to do the
|
|
||||||
full mapping, then the unpacking of an argument tuple's values must be
|
|
||||||
done and then have those values bound to the proper parameter. This
|
|
||||||
could be a problem since this would require using the iterator to
|
|
||||||
verify the binding and thus could possibly make the iterator worthless
|
|
||||||
for actual use in a function call later.
|
|
||||||
|
|
||||||
But if one wants parameters to be based on what is a single positional
|
|
||||||
argument, then the tuple should not be unpacked. This means that for
|
|
||||||
tuples one can do the best they can to verify that the argument will
|
|
||||||
unpack properly without running an iterator. But if an object is
|
|
||||||
passed in that does not define ``__len__()`` and ``__getitem__()`` for
|
|
||||||
verifying unpacking, then one can either just assume that if it
|
|
||||||
defines ``__iter__()`` it might be okay, or raise an exception stating
|
|
||||||
that the binding could not be calculated with full confidence.
|
|
||||||
|
|
||||||
|
|
||||||
How should ``Signature.bind`` handle ``*args`` and ``**kwargs``?
|
|
||||||
------------------------------------------------------------------
|
|
||||||
|
|
||||||
There are two possible approaches to how ``*args`` and ``**kwargs``
|
|
||||||
should be returned by ``Signature.bind``. One is to have their
|
|
||||||
names as keys in the dictionary and their values be the list and
|
|
||||||
dictionary that would be created. Another is to have ``bind``
|
|
||||||
return a three-item tuple of the parameters and their values, what
|
|
||||||
the ``*args`` value would be bound to, and a dict of what
|
|
||||||
``**kwargs`` would be set to.
|
|
||||||
|
|
||||||
|
|
||||||
Should ``Signature.bind`` return Parameter objects as keys?
|
Should ``Signature.bind`` return Parameter objects as keys?
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
||||||
Instead of returning a dict with keys consisting of the name of the
|
Instead of returning a dict with keys consisting of the name of the
|
||||||
parameters, would it be more useful to instead return Parameter
|
parameters, would it be more useful to instead use Parameter
|
||||||
objects? The name of the argument can easily be retrieved. It also
|
objects? The name of the argument can easily be retrieved from the
|
||||||
removes any need of having to map parameter name to the Parameter
|
key (and the name would be used as the hash for a Parameter object).
|
||||||
object if that is desired.
|
|
||||||
|
|
||||||
|
Provide a mapping of parameter name to Parameter object?
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
|
While providing access to the parameters in order is handy, it might
|
||||||
|
also be beneficial to provide a way to retrieve Parameter objects from
|
||||||
|
a Signature object based on the parameter's name. Which style of
|
||||||
|
access (sequential/iteration or mapping) will influence how the
|
||||||
|
parameters are stored internally and whether __getitem__ accepts
|
||||||
|
strings or integers.
|
||||||
|
|
||||||
|
One possible compromise is to have ``__getitem__`` provide mapping
|
||||||
|
support and have ``__iter__`` return Parameter objects based on their
|
||||||
|
``position`` attribute. This allows for getting the sequence of
|
||||||
|
Parameter objects easily by using the ``__iter__`` method on Signature
|
||||||
|
object along with the sequence constructor (e.g., ``list`` or
|
||||||
|
``tuple``).
|
||||||
|
|
||||||
|
|
||||||
References
|
References
|
||||||
==========
|
==========
|
||||||
|
|
||||||
.. [#inspect-module] ``inspect`` -- Inspect live objects
|
.. [#impl] pep362 directory in Python's sandbox
|
||||||
(http://docs.python.org/lib/module-inspect.html)
|
(http://svn.python.org/view/sandbox/trunk/pep362/)
|
||||||
|
|
||||||
.. [#pep-3102] Keyword-Only Arguments
|
|
||||||
(http://www.python.org/dev/peps/pep-3102/)
|
|
||||||
|
|
||||||
.. [#impl] Implementation of PEP 362
|
|
||||||
(http://www.python.org/sf/1544909)
|
|
||||||
|
|
||||||
.. [#pep-3107] Function Annotations
|
|
||||||
(http://www.python.org/dev/peps/pep-3107/)
|
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
|
|
Loading…
Reference in New Issue