[Python-Dev] pep 362 - 5th edition (original) (raw)
Yury Selivanov yselivanov.ml at gmail.com
Wed Jun 20 03:22:58 CEST 2012
- Previous message: [Python-Dev] pep 362 - 5th edition
- Next message: [Python-Dev] pep 362 - 5th edition
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
On 2012-06-19, at 8:39 PM, Nick Coghlan wrote:
On Wed, Jun 20, 2012 at 3:53 AM, Yury Selivanov <yselivanov.ml at gmail.com> wrote:
On 2012-06-19, at 1:03 PM, Ethan Furman wrote:
At some point it was suggested that Signature be put in provisionally so we could modify the API if needed -- are we doing that?
Well, it doesn't have much of an API right now (just few methods) Right, provisional API status is a fairly blunt instrument that we should only use if we can't find any other way to allow the standard library to progress. In this particular case, we have a superior alternative: distill the API down to the bare minimum that is needed to provide a more robust, cross-implementation format for describing callable signatures. We can then implement that bare minimum API for 3.3 as the foundation for higher level layered APIs that offer more flexibility, and/or capture more information about the callables. Further comments on the PEP and implementation: 1. The PEP should specify the constructor signatures for Signature and Parameter objects (I'd also prefer it if "kind" was permitted as a positional argument)
+1
2. The constructor for Parameter objects should require that names for positional-only parameters start with "<" and end with ">" to ensure they can always be distinguished from standard parameters in signature string representations and in BoundArguments.parameters
+1
3. The standard Signature constructor should accept an iterable of Parameter objects directly (with the return annotation as an optional keyword-only "annotation" argument) and enforce the following constraints: - permitted parameter binding order is strictly (POSITIONALONLY, POSITIONALORKEYWORD, VARPOSITIONAL, KEYWORDONLY, VARKEYWORD) - all parameter names must be distinct (otherwise bind() won't work properly) - if a positional only parameter is not named (param.name is None), it is given a name based on its position in the parameter list ("<0>", "<1>", etc)
+1
4. The current Signature constructor should become a "fromfunction" class method
+1
With these changes, the following would become straightforward:
def f1(a): pass str(signature(f1)) (a) def f2(*args): a, = args f.signature = Signature([Parameter("", Parameter.POSITIONALONLY]) str(signature(f2)) () def f3(*args): a, = args f.signature = Signature([Parameter(None, Parameter.POSITIONALONLY]) str(signature(f3)) (<0>) 5. Given the updated constructor signature, we can revisit the question of immutable signature objects (even just using read-only properties for public attributes and exposing a dict proxy for the parameter list). Instead of mutating the parameter list, you would instead write code like: newsig = Signature(oldsig.parameters[1:])
I think that mocking immutability here is a better decision than to implement a truly immutable object in C. Read-only properties and a dict-proxy for Signature.parameters will work.
In my opinion, that's a much nicer approach than copying an existing signature object and mutating it.
+1
6. I think "returnannotation" can safely be abbreviated to just "annotation". The fact it is on the Signature object rather than an individual parameter is enough to identify it as the return annotation.
I'm not sure about this one. 'return_annotation' is a very self-descriptive and clear name.
7. The idea of immutable Signature objects does highlight an annoyance with the "attribute may be missing" style APIs. To actually duplicate a signature correctly, including its return annotation (and assuming the attribute is renamed), you would have to do something like:
try: note = {"annotation": oldsig.annotation} except AttributeError: note = {} newsig = Signature(oldsig.parameters[1:], **note) There's an alternative approach to optional attributes that's often easier to work with: expose them as containers. In this case, since we want to make them easy to pass as keyword-only arguments, one way to achieve that would be expose an "optional" attribute on both Signature and Parameter objects. Then the above would be written as: newsig = Signature(oldsig.parameters[1:], **oldsig.optional) And copying a Parameter would be: newparam = Parameter("new name", oldparam.kind, **oldparam.optional) If we even keep that at all for the initial version of the API, the direct "default" and "annotation" attributes would just be read-only properties that accessed the "optional" container (reporting AttributeError if the corresponding attribute was missing)
+0. I think that 'optional' is a bit unusual attribute for the stdlib, but it will work if we make Signature immutable.
8. Not essential, but I suggest moving most of the parameter formatting details to a Parameter.str method
+1
9. The PEP should explicitly note that we're taking a deliberately strict approach to the notion of signature and parameter equivalence by assuming that all parameter names have semantic significance. Looser checks that ignore the names of positional and variable keyword parameters can be handled with Signature.bind() or by implementing custom key or comparison functions.
+1
10. Similar to the discussion of convenience properties on Signature objects themselves, I now think we should drop the "args" and "kwargs"
Big -1 on this one. Look at the current implementation of those properties - it's quite complex. One of the major points of new API is to allow easy modifications of arguments. Without .args & .kwargs it will be a PITA to call a function.
Imagine, that the "check types" example from the PEP is modified to coerce arguments to specified types. It won't be possible to do without .args & .kwargs. I, for instance, use this API to bind, validate, and coerce arguments for RPC calls. The whole point for me to work on this PEP was to make these types of functionality easy to implement.
properties from the initial version of BoundArguments. Instead, I propose the following attributes: - arguments (mapping of parameter names to values supplied as arguments) - defaults (mapping of unbound parameter names with defaults to their default values)
Why would you need 'defaults'? It's very easy to compile that list manually (and I believe the use cases will be limited)
- unbound (set of unbound names, always empty for bind(), may have entries for bindpartial())
This may be practical. But again - those are easy to deduce from 'BoundArguments.arguments' and 'Signature.parameters'.
In summary - I like everything you've proposed, except comment #10.
- Yury
- Previous message: [Python-Dev] pep 362 - 5th edition
- Next message: [Python-Dev] pep 362 - 5th edition
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]