Hi all,
Here's a Python problem I've come up against and my crappy solution.
Hopefully someone here can suggest something better. I want to decorate
a bunch of functions with different signatures; for example, I might
want to add some keyword-only arguments to all functions that return
instances of a particular class so that the caller can create instances
with additional attributes. So I do something like this:
import functools
def mydecorator(f):
@functools.wraps(f)
def wrapped(*args, attribute = None, **kwargs):
result = f(*args, **kwargs)
result.attribute = attribute
return result
return wrapped
@mydecorator
def f(x, y = 1, *a, z = 2, **k):
return something
The problem with this is, when I subsequently type 'f(' in IDLE, the
signature prompt that appears is not very useful; it looks like this:
(*args, attribute=None, **kwargs)
whereas I'd like it to look like this:
(x, y=1, *a, z=2, attribute=None, **k)
After thinking about it for a while I've come up with the following
abomination:
import inspect
def sigwrapper(sig):
if not isinstance(sig, inspect.Signature):
sig = inspect.signature(sig)
def wrapper(f):
ps = 'args = []\n\t\t'
ks = 'kwargs = {}\n\t\t'
for p in sig.parameters.values():
if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD):
ps = '%sargs.append(%s)\n\t\t' % (ps, p.name)
elif p.kind == p.VAR_POSITIONAL:
ps = '%sargs.extend(%s)\n\t\t' % (ps, p.name)
elif p.kind == p.KEYWORD_ONLY:
ks = '%skwargs[%r] = %s\n\t\t' % (ks, p.name, p.name)
elif p.kind == p.VAR_KEYWORD:
ks = '%skwargs.update(%s)\n\t\t' % (ks, p.name)
loc = {'wrapped': f}
defstring = ('def wrapouter(wrapped = wrapped):'
'\n\tdef wrapinner%s:'
'\n\t\t%s%sreturn wrapped(*args, **kwargs)'
'\n\treturn wrapinner' % (sig, ps, ks))
exec(defstring, f.__globals__, loc)
return loc['wrapouter']()
return wrapper
The function sigwrapper() may be passed an inspect.Signature object sig
(or function, if that function has the right signature) and returns a
decorator that gives any function the signature sig. I can then replace
my original decorator with something like
def mydecorator(f):
sig = inspect.signature(f)
sig = do_something(sig) # add an additional kw-only argument to sig
@functools.wraps(f)
@sigwrapper(sig)
def wrapped(*args, attribute = None, **kwargs):
result = f(*args, **kwargs)
result.attribute = attribute
return result
return wrapped
It seems to work, but I don't like it. Does anyone know of a better way
of doing the same thing?
--
http://mail.python.org/mailman/listinfo/python-list