[I am posting to python-3000 since this is where the parameter list ideas are being discussed, but this is probably generic enough to eventually make it into the 2.x line]
Here is a rough draft of a PEP I wrote last summer after I had Guido come for lunch at Google when I was interning there (i.e., a little old =) . Someone (I think Philip) had suggested something like this back then but it didn't go any farther. I liked the idea and I asked Guido at lunch if he was okay for it; he was. So I wrote the following PEP along with a Python implementation (attached, along with test cases). It is still a rough draft since I am on vacation on top of visiting my dad for his birthday and thus cannot put a ton of time into this at the moment, so don't consider this a final version in any way. There is already a list of things to consider and I am sure there are some things discussed during the new parameter list ideas that could stand to be worked in. I am quite happy to work on finishing this PEP so that Talin can focus on the parameter list PEP. So just comment away and I will work on incorporating them as time permits. -Brett ------------------------------------------------------------------ PEP: XXX Title: Introducing the __signature__ Attribute Version: $Revision: 1.5 $ Last-Modified: $Date: 2005/06/07 13:17:37 $ Author: Brett Cannon <[EMAIL PROTECTED]> Status: Draft Type: Standards Track Content-Type: text/x-rst Created: XX-XXX-XXXX Post-History: XXX * Break abstract into Abstract and Rationale * write signature() function - maybe not in built-ins; inspect instead? * look up object identity (issue|crisis) as reference * automatically apply signature when decorator called? * make more general by having an attribute that points to original object being wrapped that introspection checks first? - __wrapping__ - __identity__ - __introspect__ - __metadata__ Abstract ======== Decorators were introduced to Python in version 2.4 . Their introduction provided a syntactically easy way to "decorate" functions and methods. The ease of use has allowed the use of functions that wrap other functions and methods to become more prevalent in Python code. Unfortunately, one side-effect of the increased use of decorators is the loss of introspection on the function being wrapped. Most decorators are not meant to be directly noticed by someone using the code; decorators are meant to be heard, not seen. But when a decorator is used on a function or method that returns a new function that wraps the original, introspection will occur on the wrapping function, not the wrapped one. :: def yell(func): def wrapper(*args, **kwargs): print "Hi!" return func(*args, **kwargs) return wrapper @yell def wrapped_one_arg(x): pass def unwrapped_one_arg(x): pass if __name__ == '__main__': from inspect import getargspec print getargspec(wrapped_one_arg) == getargspec(unwrapped_one_arg) To help deal with this phenomenon, the __signature__ attribute is being proposed. It is to be an optional attribute on objects where introspection of function parameters may be performed. It contains an instance of the Signature class that represents all relevant data that one might want to know about the call signature of a function or method. This allows one to assign to a wrapping function's __signature__ attribute an instance of the Signature class that represents the wrapped function or method, thus allowing introspection on the wrapping function to reflect the call signature of the wrapped function. The Signature also works for classes (representing the call signature for instantiation) and instances (for the ``__call__`` method of an instance). A built-in, aptly named ``signature``, is also being proposed. When passed an object that meets the proper requirements, it will either return a new instance of the Signature class or the pre-existing object for the object passed in. Types of Argument Parameters ============================ Python has a fairly rich set of function parameter possibilities. To start, there are required arguments; ``def positional(x): pass``. These types of parameters require an argument be passed in for them. Next, there are default arguments; ``def defaults(x=42): pass``. They have a default value assigned to them if no argument is passed in for that parameter. They are optional, though, unlike required arguments. Lastly, there are excess arguments; ``def excess(*args, **kwargs): pass``. There are two types of excess arguments. One type is an excess positional argument (``*args``). When this type of parameter is present all positional arguments provided during the call are collected into a tuple and assigned to the excess positional argument. The other type of parameter is an excess keyword argument. All keyword arguments that are not directly assigned to an existing keyword parameter are collected into a dict keyed on the keyword name and containing a value of the argument passed in. This dict then gets assigned to the excess keyword argument parameter. For both types, direct assignment to the parameter is not permitted. Signature Object ================ All types of function parameters mentioned in the section `Types of Argument Parameters`_ must be handled by the Signature object in order for it to be useful. One should be able to view the Signature object as providing enough information to be able to know what is possible in terms of calling a function or method. That said, there must be a way to handle required arguments, default arguments, and both types of excess argument parameters. For required arguments, the attribute ``required_args`` provides a tuple of strings that correspond to the names of the required arguments in the order they are defined in the function. By providing the name and order one can easily find out the calling convention required for the minimal call of the function or method. Default arguments are represented by the ``default_args`` attribute. A tuple is contained within the attribute which itself contains two item tuples of ``(name, value)`` pairs representing the name of the default argument parameter and the value that is given. For instance, for the function ``def defaults(x=42, y=True): pass``, the value held by ``default_args`` would be ``(('x', 42), ('y', True))``. Excess argument parameters each have their own attribute to represent their existence. For the existence of an excess positional argument, ``excess_pos_args`` exists. ``excess_kw_args`` represents an excess keyword argument parameter. Both attributes contain a boolean value representing whether they parameter exists. While changing the attributes to contain the name of the respective excess parameter is technically feasible, it has been deemed unnecessary since that knowledge is not useful when making an actual call to the function or method that is represented by Signature object. One key decision that was made during the development of attribute names is taking into consideration the possibility of optional type checking for function parameters. This meant the names should be general enough to represent what they do without type information. Finally, the Signature object implements a ``__str__`` method. Calling this will return a string representing the parameter as might be seen in the actual code. This is provided for human consumption to easily deduce what is required for calling the object. No parentheses are put around the returned string. The final interface, following the syntax outlined in [#interface-syntax]_, is:: interface Signature: """Specifies the call signature of a class, instance, method, or class""" def __str__(self) -> str: """String representation of the parameters""" required_args: tuple(str) default_args: tuple(tuple(str, object)) excess_pos_args: bool excess_kw_args: bool Implementation ============== XXX Signature object (using 'inspect'), changing pydoc (and thus help()), builtin Example Usage ============= For all examples, assume the variable ``sig`` contains an instance of the Signature object. Making a decorator have its __signature__ set to the function it is wrapping ---------------------------------------------------------------------------- :: def wrapper(func): def inner(*args, **kwargs): # Magic happens here ... return func(*args, **kwargs) inner.__signature__ = signature(func) return inner All positional arguments ------------------------ :: sig.required_args + (name for name, value in sig.default_args) Dictionary of default arguments ------------------------------- :: dict(sig.default_args) References ========== .. [#interface-syntax] Guido van Rossum's blog ("Interfaces or Abstract Base Classes?") http://www.artima.com/weblogs/viewpost.jsp?thread=92662 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 End:
signature.py
Description: Binary data
test_signature.py
Description: Binary data
_______________________________________________ Python-3000 mailing list Python-3000@python.org http://mail.python.org/mailman/listinfo/python-3000 Unsubscribe: http://mail.python.org/mailman/options/python-3000/archive%40mail-archive.com