PEP: 362 Title: Function Signature Object Version: $Revision$ Last-Modified: $Date$ Author: Brett Cannon Jiwon Seo Status: Draft Type: Standards Track Python-Version: 2.6 Content-Type: text/x-rst Created: 21-Aug-2006 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 signature using ``func_defaults``, ``func_code.co_argcount``, ``func_code.co_flags``, and ``func_code.co_varnames``. Unfortunately this is a little unruly having to look at four 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. It also helps for introspection for decorators that wrap the function they are applied to by allowing the wrapped function's signature object be set for the wrapping function. Signature Object ================ The overall signature of an object is represented by the Signature object. This object is to store Parameter objects (discussed later) along with any information about the function itself and any parameters that are highly unique (.e.g, ``*args``). 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. indistinguishable from * var_args:str Name of the ``*args`` parameter, if present, else the empty string. * var_kw_args:str Name of the ``**kwargs`` parameter, if present, else the empty string. * parameters:list(Parameter) List of the parameters of the function are represented by Parameter objects (see `Parameter Object`_ for their description). * __str__() -> str Return the string representation of the signature as it might appear in source code. * bind(\*args, \*\*kwargs) -> dict Create a mapping from parameter to argument for the signature (see `Open Issues`_ for question of how to handle tuples). The Signature object is stored in the ``__signature__`` attribute of the function. When it is to be created is an `Open Issues`_. Parameter Object ================ A function's signature is partially made up of several parameters. Python's different kinds of parameters is quite large and rich and continues to grow. This means that Parameter objects require they 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 possible pieces of information one can know about a parameter. But the decision was made to incorporate all information about a parameter in a single argument 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, use a tuple of strings where each item is the name of the parameter contained within the tuple. * position:int The position of the parameter within the signature of the function (zero-indexed). * 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. * __str__() -> str Return the string representation of the parameter as it might appear in source code in a function signature. Implementation ============== An implementation can be found in patch #1544909 [#impl]_. It modifies the 'inspect' module [#inspect-module]_to include the implementation. There is a function named ``getsignature()`` which returns the value stored on the ``__signature__`` attribute (for methods this is stored directly on the im_func function 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 With 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. Open Issues =========== When to construct the Parameter object? --------------------------------------- The Parameter 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 ``inspect.getsignature()`` and that would generate the Signature object and store it to ``__signature__`` if 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. References ========== .. [#inspect-module] ``inspect`` -- Inspect live objects (http://docs.python.org/lib/module-inspect.html) .. [#pep-3102] Keyword-Only Arguments (http://www.python.org/dev/peps/pep-3102/) .. [#impl] Implementation of PEP 362 (http://www.python.org/sf/1544909) 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: