[Python-Dev] signature object issues (to discuss while I am out of contact)
Nick Coghlan
ncoghlan at gmail.com
Tue May 2 13:04:06 CEST 2006
Brett Cannon wrote:
> One is whether a signature object should be automatically created for
> every function. As of right now the PEP I am drafting has it on a
> per-need basis and have it assigned to __signature__ through a
> built-in function or putting it 'inspect'. Now automatically creating
> the object would possibly make it more useful, but it could also be
> considered overkill. Also not doing it automatically allows signature
> objects to possibly make more sense for classes (to represent
> __init__) and instances (to represent __call__). But having that same
> support automatically feels off for some reason to me.
My current impulse is to put the signature object in the inspect module to
start with, and don't give it a special attribute at all.
All of the use cases I can think of (introspection for documentation purposes
or argument checking purposes) don't really suffer either way regardless of
whether the signature retrieval is spelt "obj.__signature__" or
"inspect.getsignature(obj)".
Since it doesn't make much difference from a usability point of view, let's
start with the object in the library module first.
> The second question is whether it is worth providing a function that
> will either figure out if a tuple and dict representing arguments
> would work in calling the function. Some have even suggested a
> function that returns the actual bindings if the call were to occur.
> Personally I don't see a huge use for either, but even less for the
> latter version. If people have a legit use case for either please
> speak up, otherwise I am tempted to keep the object simple.
A "bind" method on the signature objects is pretty much essential for any kind
of argument checking usage.
In addition to Aahz's precondition checking example, Talin gave a good example
on the Py3k list of a function decorator for logging all calls to a function,
and including the argument bindings in the log message.
And just in case you think the operation would be easy to implement if you
need it, I've included below the bind method from the signature object I wrote
to play around with the ideas posted to the Py3k list. It took a fair bit of
work to get it to spit out the right answers :)
Cheers,
Nick.
def bind(*args, **kwds):
"""Return a dict mapping parameter names to bound arguments"""
self = args[0]
args = args[1:]
bound_params = {}
num_args = len(args)
missing_args = set(self.required_args)
arg_names = self.required_args + self.optional_args
num_names = len(arg_names)
# Handle excess positional arguments
if self.extra_args:
bound_params[self.extra_args] = tuple(args[num_names:])
elif num_args > num_names:
self._raise_args_error(num_args)
# Bind positional arguments
for name, value in zip(arg_names, args):
bound_params[name] = value
missing_args -= set((name,))
# Bind keyword arguments (and handle excess)
if self.extra_kwds:
extra_kwds = dict()
bound_params[self.extra_kwds] = extra_kwds
else:
extra_kwds = None
for name, value in kwds.items():
if name in bound_params:
raise TypeError(
"Got multiple values for argument '%s'" % name)
elif name in arg_names:
missing_args -= set((name,))
bound_params[name] = value
elif extra_kwds is not None:
extra_kwds[name] = value
else:
raise TypeError(
"Got unexpected keyword argument '%s'" % name)
if missing_args:
self._raise_args_error(num_args)
# All done
return bound_params
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
---------------------------------------------------------------
http://www.boredomandlaziness.org
More information about the Python-Dev
mailing list