PEP: 362 Title: Function Signature Object Version: $Revision$ Last-Modified: $Date$ Author: Brett Cannon Jiwon Seo Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 21-Aug-2006 Python-Version: 2.6 Post-History: Abstract ======== Python has always supported powerful introspection capabilities, including that for functions and methods (for the rest of this PEP the word "function" refers to both functions and methods). Taking a function object, you can fully reconstruct the function's signature. Unfortunately it is a little unruly having to look at all the different attributes to pull together complete information for a function's signature. This PEP proposes an object representation for function signatures. This should help facilitate introspection on functions for various usese (e.g., decorators). The introspection information contains all possible information about the parameters in a signature (including Python 3.0 features). Signature Object ================ The overall signature of an object is represented by the Signature object. This object is to store a `Parameter object`_ for each parameter in the signature. It is also to store any information about the function itself that is pertinent to the signature. A Signature object has the following structure attributes: * name : str Name of the function. This is not fully qualified because function objects for methods do not know the class they are contained within. This makes functions and methods indistinguishable from one another when passed to decorators, preventing proper creation of a fully qualified name. * var_args : str Name of the variable positional parameter (i.e., ``*args``), if present, or the empty string. * var_kw_args : str Name of the variable keyword parameter (i.e., ``**kwargs``), if present, or the empty string. * 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 Parameter objects in the order of its definition (keyword-only arguments are in the order listed by ``code.co_varnames``). * bind(\*args, \*\*kwargs) -> dict(str, Parameter) Create a mapping from arguments to parameters. The keys are the names of the parameter that an argument maps to with the value 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 function. When it is to be created is discussed in `Open Issues`_. Parameter Object ================ A function's signature is made up of several parameters. Python's different kinds of parameters is quite large and rich and continues to grow. Parameter objects represent any possible parameter. Originally the plan was to represent parameters using a list of parameter names on the Signature object along with various dicts keyed on parameter names to disseminate the various pieces of information one can know about a parameter. But the decision was made to incorporate all information about a parameter in a single object so as to make extending the information easier. This was originally put forth by Talin and the preferred form of Guido (as discussed at the 2006 Google Sprint). The structure of the Parameter object is: * name : (str | tuple(str)) The name of the parameter as a string if it is not a tuple. If the argument is a tuple then a tuple of strings is used. * position : int The position of the parameter within the signature of the function (zero-indexed). For keyword-only parameters the position 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. * default_value : object The default value for the parameter, if present, else the attribute does not exist. This is done so that the attribute is not accidentally used if no default value is set as any default value could be a legitimate default value itself. * keyword_only : bool True if the parameter is keyword-only, else False. * 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 ============== An implementation can be found in Python's sandbox [#impl]_. There is a function named ``signature()`` which returns the value stored on the ``__signature__`` attribute if it exists, else it creates it bound to the Signature object for the function. For methods this is stored directly on the im_func function object since that is what decorators will work with. Open Issues =========== When to construct the Signature object? --------------------------------------- The Signature object can either be created in an eager or lazy fashion. In the eager situation, the object can be created during creation of the function object. In the lazy situation, one would pass a function object to a function and that would generate the Signature object and store it to ``__signature__`` if needed, and then return the value of ``__signature__``. Should ``Signature.bind`` return Parameter objects as keys? ----------------------------------------------------------- Instead of returning a dict with keys consisting of the name of the parameters, would it be more useful to instead use Parameter objects? The name of the argument can easily be retrieved from the key (and the name would be used as the hash for a Parameter object). 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 ========== .. [#impl] pep362 directory in Python's sandbox (http://svn.python.org/view/sandbox/trunk/pep362/) 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: