[Python-Dev] [Python-checkins] peps: The latest changes from Yury Selivanov. I can almost taste the acceptance! (original) (raw)
Eric Snow ericsnowcurrently at gmail.com
Thu Jun 21 17:34:10 CEST 2012
- Previous message: [Python-Dev] Fwd: Re: Add os.path.resolve to simplify the use of os.readlink
- Next message: [Python-Dev] [Python-checkins] peps: The latest changes from Yury Selivanov. I can almost taste the acceptance!
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
On Thu, Jun 21, 2012 at 2:44 AM, larry.hastings <python-checkins at python.org> wrote:
http://hg.python.org/peps/rev/1edf1cecae7d changeset: 4472:1edf1cecae7d user: Larry Hastings <larry at hastings.org> date: Thu Jun 21 01:44:15 2012 -0700 summary: The latest changes from Yury Selivanov. I can almost taste the acceptance!
files: pep-0362.txt | 159 +++++++++++++++++++++++++++++++------- 1 files changed, 128 insertions(+), 31 deletions(-)
diff --git a/pep-0362.txt b/pep-0362.txt --- a/pep-0362.txt +++ b/pep-0362.txt @@ -42,23 +42,58 @@ A Signature object has the following public attributes and methods: * returnannotation : object - The annotation for the return type of the function if specified. - If the function has no annotation for its return type, this - attribute is not set. + The "return" annotation for the function. If the function + has no "return" annotation, this attribute is not set. + * parameters : OrderedDict An ordered mapping of parameters' names to the corresponding - Parameter objects (keyword-only arguments are in the same order - as listed in
code.covarnames
). + Parameter objects. + * bind(*args, **kwargs) -> BoundArguments Creates a mapping from positional and keyword arguments to parameters. Raises aTypeError
if the passed arguments do not match the signature. + * bindpartial(*args, **kwargs) -> BoundArguments Works the same way asbind()
, but allows the omission of some required arguments (mimicsfunctools.partial
behavior.) Raises aTypeError
if the passed arguments do not match the signature. +* replace(parameters, *, returnannotation) -> Signature
Shouldn't it be something like this:
- replace_(*parameters, [return_annotation]) -> Signature
Or is parameters supposed to be a dict/OrderedDict of replacements/additions?
+ Creates a new Signature instance based on the instance +
replace
was invoked on. It is possible to pass different +parameters
and/orreturnannotation
to override the + corresponding properties of the base signature. To remove +returnannotation
from the copiedSignature
, pass in +Signature.empty
.
Can you likewise remove parameters this way?
+ +Signature objects are immutable. Use
Signature.replace()
to +make a modified copy: +:: + + >>> sig = signature(foo) + >>> newsig = sig.replace(returnannotation="new return annotation") + >>> newsig is not sig + True + >>> newsig.returnannotation == sig.returnannotation + True
Should be False here, right?
+ >>> newsig.parameters == sig.parameters + True
An example of replacing parameters would also be good here.
+ +There are two ways to instantiate a Signature class: + +* Signature(parameters, *, returnannotation)
Same here as with Signature.replace().
+ Default Signature constructor. Accepts an optional sequence + of
Parameter
objects, and an optionalreturnannotation
. + Parameters sequence is validated to check that there are no + parameters with duplicate names, and that the parameters + are in the right order, i.e. positional-only first, then + positional-or-keyword, etc. +* Signature.fromfunction(function) + Returns a Signature object reflecting the signature of the + function passed in. + It's possible to test Signatures for equality. Two signatures are equal when their parameters are equal, their positional and positional-only parameters appear in the same order, and they @@ -67,9 +102,14 @@ Changes to the Signature object, or to any of its data members, do not affect the function itself.-Signature also implements
_str_
and_copy_
methods. -The latter creates a shallow copy of Signature, with all Parameter -objects copied as well. +Signature also implements_str_
: +:: + + >>> str(Signature.fromfunction((lambda *args: None))) + '(*args)' + + >>> str(Signature()) + '()'Parameter Object @@ -80,20 +120,22 @@ propose a rich Parameter object designed to represent any possible function parameter. -The structure of the Parameter object is: +A Parameter object has the following public attributes and methods: * name : str - The name of the parameter as a string. + The name of the parameter as a string. Must be a valid + python identifier name (with the exception of
POSITIONALONLY
+ parameters, which can have it set toNone
.) * default : object - The default value for the parameter, if specified. If the - parameter has no default value, this attribute is not set. + The default value for the parameter. If the parameter has no + default value, this attribute is not set. * annotation : object - The annotation for the parameter if specified. If the - parameter has no annotation, this attribute is not set. + The annotation for the parameter. If the parameter has no + annotation, this attribute is not set. -* kind : str +* kind Describes how argument values are bound to the parameter. Possible values: @@ -101,7 +143,7 @@ as a positional argument. Python has no explicit syntax for defining positional-only - parameters, but many builtin and extension module functions + parameters, but many built-in and extension module functions (especially those that accept only one or two parameters) accept them. @@ -124,9 +166,30 @@ that aren't bound to any other parameter. This corresponds to a "**kwds" parameter in a Python function definition. +* replace(*, name, kind, default, annotation) -> Parameter + Creates a new Parameter instance based on the instance +replaced
was invoked on. To override a Parameter + attribute, pass the corresponding argument. To remove + an attribute from aParameter
, passParameter.empty
. + + Two parameters are equal when they have equal names, kinds, defaults, and annotations. +Parameter objects are immutable. Instead of modifying a Parameter object, +you can useParameter.replace()
to create a modified copy like so: +:: + + >>> param = Parameter('foo', Parameter.KEYWORDONLY, default=42) + >>> str(param) + 'foo=42' + + >>> str(param.replace()) + 'foo=42' + + >>> str(param.replace(default=Parameter.empty, annotation='spam')) + "foo:'spam'" + BoundArguments Object ===================== @@ -138,7 +201,8 @@ * arguments : OrderedDict An ordered, mutable mapping of parameters' names to arguments' values. - Does not contain arguments' default values. + Contains only explicitly bound arguments. Arguments for + whichbind()
relied on a default value are skipped. * args : tuple Tuple of positional arguments values. Dynamically computed from the 'arguments' attribute. @@ -159,6 +223,23 @@ ba = sig.bind(10, b=20) test(*ba.args, **ba.kwargs) +Arguments which could be passed as part of either*args
or**kwargs
+will be included only in theBoundArguments.args
attribute. Consider the
Why wouldn't the kwargs go into BoundArguments.kwargs?
+following example: +:: + + def test(a=1, b=2, c=3): + pass + + sig = signature(test) + ba = sig.bind(a=10, c=13) + + >>> ba.args + (10,) + + >>> ba.kwargs: + {'c': 13} +
Implementation ============== @@ -172,7 +253,7 @@ - If the object is not callable - raise a TypeError - If the object has a
_signature_
attribute and if it - is notNone
- return a shallow copy of it + is notNone
- return it - If it has a_wrapped_
attribute, returnsignature(object._wrapped_)
@@ -180,12 +261,9 @@ - If the object is a an instance ofFunctionType
construct
s/FunctionType
construct/FunctionType
, construct/
and return a new
Signature
for it- - If the object is a method or a classmethod, construct and return - a new
Signature
object, with its first parameter (usually -self
orcls
) removed - - - If the object is a staticmethod, construct and return - a newSignature
object + - If the object is a method, construct and return a newSignature
+ object, with its first parameter (usuallyself
orcls
) + removed
It may be worth explicitly clarify that it refers to bound methods (and classmethods) here.
Also, inspect.getfullargspec() doesn't strip out the self/cls. Would it be okay to store that implicit first argument (self/cls) on the Signature object somehow? Explicit is better than implicit. It's certainly a very special case: the only implicit (and unavoidable) arguments of any kind of callable. If the self were stored on the Signature, I'd also expect that Signature.replace() would leave it out (as would any other copy mechanism).
- If the object is an instance of
functools.partial
, construct a newSignature
from itspartial.func
attribute, and @@ -196,15 +274,15 @@ - If the object's type has a_call_
method defined in its MRO, return a Signature for it - - If the object has a_new_
method defined in its class, + - If the object has a_new_
method defined in its MRO, return a Signature object for it - - If the object has a_init_
method defined in its class, + - If the object has a_init_
method defined in its MRO, return a Signature object for it - Returnsignature(object._call_)
-Note, that theSignature
object is created in a lazy manner, and +Note that theSignature
object is created in a lazy manner, and is not automatically cached. If, however, the Signature object was explicitly cached by the user,signature()
returns a new shallow copy of it on each invocation. @@ -236,11 +314,21 @@ ---------------------------------------- Some functions may not be introspectable in certain implementations of -Python. For example, in CPython, builtin functions defined in C provide +Python. For example, in CPython, built-in functions defined in C provide no metadata about their arguments. Adding support for them is out of scope for this PEP.+Signature and Parameter equivalence +----------------------------------- + +We assume that parameter names have semantic significance--two +signatures are equal only when their corresponding parameters have +the exact same names. Users who want looser equivalence tests, perhaps +ignoring names of VARKEYWORD or VARPOSITIONAL parameters, will +need to implement those themselves. + + Examples ======== @@ -270,6 +358,10 @@ def call(self, a, b, *, c) -> tuple: return a, b, c + @classmethod + def spam(cls, a): + return a + def sharedvars(*sharedargs): """Decorator factory that defines shared variables that are @@ -280,10 +372,12 @@ def wrapper(*args, **kwds): fullargs = sharedargs + args return f(*fullargs, **kwds) + # Override signature - sig = wrapper.signature = signature(f) - for _ in sharedargs: - sig.parameters.popitem(last=False) + sig = signature(f) + sig = sig.replace(tuple(sig.parameters.values())[1:]) + wrapper.signature = sig + return wrapper return decorator @@ -313,6 +407,9 @@ >>> formatsignature(Foo().call) '(a, b, *, c) -> tuple' + >>> formatsignature(Foo.spam) + '(a)' + >>> formatsignature(partial(Foo().call, 1, c=3)) '(b, *, c=3) -> tuple'
I'm really impressed by the great work on this and how well the PEP process has been working here. This is a great addition to Python!
-eric
- Previous message: [Python-Dev] Fwd: Re: Add os.path.resolve to simplify the use of os.readlink
- Next message: [Python-Dev] [Python-checkins] peps: The latest changes from Yury Selivanov. I can almost taste the acceptance!
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]