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

Yury Selivanov yselivanov.ml at gmail.com
Wed Jun 6 22:10:22 CEST 2012


On 2012-06-06, at 3:33 PM, Daniel Urban wrote:
> On Wed, Jun 6, 2012 at 8:35 PM, Yury Selivanov <yselivanov.ml at gmail.com> wrote:
>> On 2012-06-06, at 2:22 PM, Daniel Urban wrote:
>>>> I'll try to answer you with the following code:
>>>> 
>>>>   >>> def foo(*args):
>>>>   ...    print(args)
>>>> 
>>>>   >>> bound_args = signature(foo).bind(1, 2, 3)
>>>>   >>> bound_args.arguments
>>>>   OrderedDict([('args', (1, 2, 3))])
>>>> 
>>>> You can't invoke 'foo' by:
>>>> 
>>>>   >>> foo(**bound_args.arguments)
>>>>   TypeError: foo() got an unexpected keyword argument 'args'
>>> 
>>> Of course, but you can invoke it with "1, 2, 3", the arguments you
>>> used to create the BoundArguments instance in the first place: foo(1,
>>> 2, 3) will work fine.
>> 
>> The whole point is to use BoundArguments mapping for invocation.
>> See Nick's idea to validate callbacks, and my response to him, below in
>> the thread.
>> 
>>>> That's why we have two dynamic properties 'args', and 'kwargs':
>>> 
>>> Ok, but what I'm saying is, that we don't really need them.
>> 
>> We need them.  Again, in some contexts you don't have the arguments
>> you've passed to bind().
> 
> But how could we *need* bind to return 'args' and 'kwargs' to us, when
> we wouldn't be able to call bind in the first place, if we wouldn't
> had the arguments?

You're missing the point.  BoundArguments contains properly mapped
*args and **kwargs passed to Signature.bind.  You can validate them after,
do type casts, modify them, overwrite etc. by manipulating
'BoundArguments.arguments'.

At the end you can't, however, invoke the function by doing:

   func(**bound_arguments.arguments) # <- this won't work

as varargs will be screwed.

That's why you need 'args' & 'kwargs' properties on BoundArguments.

Imagine, that "Annotation Checker" example is modified to coerce all string
arguments to int (those that had 'int' in annotation) and then to multiply
them by 42.

We'd write the following code:
 
   for arg_name, arg_value in bound_arguments.arguments.items():
      # I'm skipping is_args & is_kwargs checks, and assuming
      # we have annotations everywhere
      if sig.parameters[arg_name].annotation is int \
				and isinstance(arg_value, str):
          bound_arguments.arguments[arg_name] = int(arg_value) * 42

   return func(*bound_arguments.args, **bound_arguments.kwargs)

Thanks,

-
Yury


More information about the Python-Dev mailing list