[Python-Dev] Review of PEP 362 (signature object)

Yury Selivanov yselivanov.ml at gmail.com
Tue Mar 13 15:35:10 CET 2012


Guido, Brett,

I've tried to use the proposed signature object, however, I found that 
the 'bind' method is incorrect, and came up with my own implementation 
of the PEP: https://gist.github.com/2029032  (If needed, I can change 
the licence to PSFL)  I used my version to implement typechecking,
arguments validation in various RPC dispatch mechanisms, and it is 
proven to work.

First of all, in the current version of the PEP, "bind" doesn't work 
correctly with "varargs", as it returns a dictionary object:

    def foo(bar, *args):
        print(bar, args)

    s = signature(foo)
    bound = s.bind(1, 2 ,3 ,4)
    print('Bound args:', bound)
    foo(**bound)

This code outputs the following:

    Bound args: {'args': (2, 3, 4), 'bar': 1}
    Traceback (most recent call last):
      File "test.py", line 286, in <module>
        foo(**bound)
    TypeError: foo() got an unexpected keyword argument 'args'

The conclusion is that ** form of unpacking is not always enough, so
'bind' should at least return a pair of (args, kwargs).

Secondly, I don't think that even (args, kwargs) pair is enough, as
some information about how arguments were mapped is lost.  In my
implementation, 'bind' method returns an instance of 'BoundArguments'
class, which preserves the exact mapping, and has two convenience
properties '.args' and '.kwargs', so the example above transforms into:

    bound = s.bind(1, 2, 3, 4)
    foo(*bound.args, **bound.kwargs)

And that works as it should.  When some advanced processing is required,
you can work with its private fields:

    >>> bound._args
    (1,)
    >>> bound._varargs
    (2, 3, 4)

I also think that keyword-only arguments deserve to land in a separate
collection in the signature object, in my implementation it is
'signature.kwonlyargs' slot.  It is easier to do some advanced processing
of arguments this way, and even the 'signature.__iter__' is simpler.

Finally, I also added 'render_args' method to the signature.  It just
renders function's arguments as in its definition:
 
    >>> signature(foo).render_args()
    bar, *args

This is useful for printing detailed error messages and hints.

-
Yury


More information about the Python-Dev mailing list