Update the PEP to be in line with the working implementation.

This commit is contained in:
Brett Cannon 2007-02-26 15:30:51 +00:00
parent 89710fca07
commit 9bb90a8fea
1 changed files with 89 additions and 146 deletions

View File

@ -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