[Python-Dev] Updated PEP 362 (Function Signature Object)

Mark Shannon mark at hotpy.org
Wed Jun 6 17:50:15 CEST 2012


Steven D'Aprano wrote:
> Brett Cannon wrote:
> 
>> PEP: 362
>> Title: Function Signature Object
>> Version: $Revision$
>> Last-Modified: $Date$
>> Author: Brett Cannon <brett at python.org>, Jiwon Seo <seojiwon at gmail.com>,
>>         Yury Selivanov <yselivanov at sprymix.com>, Larry Hastings <
>> larry at hastings.org>
>> Status: Draft
>> Type: Standards Track
>> Content-Type: text/x-rst
>> Created: 21-Aug-2006
>> Python-Version: 3.3
>> Post-History: 04-Jun-2012
>>
>>
>> Abstract
>> ========
>>
>> Python has always supported powerful introspection capabilities,
>> including introspecting functions and methods.  (For the rest of
>> this PEP, "function" refers to both functions and methods).  By
>> examining a function object you can fully reconstruct the function's
>> signature.  Unfortunately this information is stored in an inconvenient
>> manner, and is spread across a half-dozen deeply nested attributes.
>>
>> This PEP proposes a new representation for function signatures.
>> The new representation contains all necessary information about a 
>> function
>> and its parameters, and makes introspection easy and straightforward.
> 
> It's already easy and straightforward, thanks to the existing 
> inspect.getfullargspec function. If this existing inspect function is 
> lacking something, the PEP should explain what, and why the inspect 
> function can't be fixed.
> 
> 
>> However, this object does not replace the existing function
>> metadata, which is used by Python itself to execute those
>> functions.  The new metadata object is intended solely to make
>> function introspection easier for Python programmers.
> 
> What happens when the existing function metadata and the __signature__ 
> object disagree?
> 
> Are there use-cases where we want them to disagree, or is disagreement 
> always a sign that something is broken?
> 
> 
> 
>> Signature Object
>> ================
>>
>> A Signature object represents the overall signature of a function.
>> It stores a `Parameter object`_ for each parameter accepted by the
>> function, as well as information specific to the function itself.
> 
> There's a considerable amount of data recorded, including a number of 
> mappings (dicts?). This potentially increase the size of functions, and 
> the overhead of creating them. Since most functions are never 
> introspected, or only rarely introspected, it seems rather wasteful to 
> record all this data "just in case", particularly since it's already 
> recorded once in the function metadata and/or code object.
> 

I agree with Stephen. Don't forget that each list comprehension 
evaluation involves creating a temporary function object.

> 
> 
>> A Signature object has the following public attributes and methods:
>>
>> * name : str
>>     Name of the function.
> 
> Functions already record their name (twice!), and it is simple enough to 
> query func.__name__. What reason is there for recording it a third time, 
> in the Signature object?
> 
> Besides, I don't consider the name of the function part of the 
> function's signature. Functions can have multiple names, or no name at 
> all, and the calling signature remains the same.
> 
> Even if we limit the discussion to distinct functions (rather than a 
> single function with multiple names), I consider spam(x, y, z) ham(x, y, 
> z) and eggs(x, y, z) to have the same signature. Otherwise, it makes it 
> difficult to talk about one function having the same signature as 
> another function, unless they also have the same name. Which would be 
> unfortunate.
> 
> 
>> * qualname : str
>>     Fully qualified name of the function.
> 
> What's the fully qualified name of the function, and why is it needed?
> 
> 
> 
> [...]
>> The structure of the Parameter object is:
> 
>> * is_args : bool
>>     True if the parameter accepts variable number of arguments
>>     (``\*args``-like), else False.
> 
> 
> "args" is just a common name for the parameter, not for the kind of 
> parameter. *args (or *data, *whatever) is a varargs parameter, and so 
> the attribute should be called "is_varargs".
> 
> 
>> * is_kwargs : bool
>>     True if the parameter accepts variable number of keyword
>>     arguments (``\*\*kwargs``-like), else False.
> 
> Likewise for **kwargs (or **kw, etc.) I'm not sure if there is a common 
> convention for keyword varargs, so I see two options:
> 
> is_varkwargs
> is_kwvarargs
> 
> 
>> * is_implemented : bool
>>     True if the parameter is implemented for use.  Some platforms
>>     implement functions but can't support specific parameters
>>     (e.g. "mode" for os.mkdir).  Passing in an unimplemented
>>     parameter may result in the parameter being ignored,
>>     or in NotImplementedError being raised.  It is intended that
>>     all conditions where ``is_implemented`` may be False be
>>     thoroughly documented.
> 
> What to do about parameters which are partly implemented? E.g. 
> mode='spam' is implemented but mode='ham' is not.
> 
> Is there a use-case for is_implemented?
> 
> [...]
>> Annotation Checker
> 
>>         def check_type(sig, arg_name, arg_type, arg_value):
>>             # Internal function that incapsulates arguments type checking
> 
> /s/incapsulates/encapsulates
> 
> 
> 
>> Open Issues
>> ===========
> 
> inspect.getfullargspec is currently unable to introspect builtin 
> functions and methods. Should builtins gain a __signature__ so they can 
> be introspected?

I'm +0 on this, but care is needed as print and [].append are the same 
type in CPython.

> 
> 
> 
>> 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__``.
>>
>> In the current implementation, signatures are created only on demand
>> ("lazy").
> 
> +1
+1 also. See comment above about list comprehensions
> 
> 
> 
>> Deprecate ``inspect.getfullargspec()`` and ``inspect.getcallargs()``?
>> ---------------------------------------------------------------------
> 
> 
> -1
> 
>> Since the Signature object replicates the use of ``getfullargspec()``
>> and ``getcallargs()`` from the ``inspect`` module it might make sense
>> to begin deprecating them in 3.3.
> 
> I think it is way to soon to deprecate anything. I don't think we should 
> even consider PendingDeprecation until at least 3.4.
> 
> Actually, I would go further: leave getfullargspec to extract the 
> *actual* argument spec from the code object, and __signature__ to be the 
> claimed argument spec. Earlier, you state:
> 
> "Changes to the Signature object, or to any of its data members,
> do not affect the function itself."
> 
> which leaves the possibility that __signature__ may no longer match the 
> actual argument spec, for some reason. If you remove getfullargspec, 
> people will have to reinvent it to deal with such cases.
> 
> 
> 
> 



More information about the Python-Dev mailing list