Decorating functions without losing their signatures
Rotwang
sg552 at hotmail.co.uk
Wed Apr 3 21:44:40 EDT 2013
On 03/04/2013 02:05, Rotwang wrote:
> [...]
>
> 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
Oops! Earlier I found out the hard way that this fails when the
decorated function has arguments called 'args' or 'kwargs'. Here's a
modified version that fixes said bug, but presumably not the many others
I haven't noticed yet:
def sigwrapper(sig):
if not isinstance(sig, inspect.Signature):
sig = inspect.signature(sig)
n = 0
while True:
pn = 'p_%i' % n
kn = 'k_%i' % n
if pn not in sig.parameters and kn not in sig.parameters:
break
n += 1
ps = '%s = []\n\t\t' % pn
ks = '%s = {}\n\t\t' % kn
for p in sig.parameters.values():
if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD):
ps = '%s%s.append(%s)\n\t\t' % (ps, pn, p.name)
elif p.kind == p.VAR_POSITIONAL:
ps = '%s%s.extend(%s)\n\t\t' % (ps, pn, p.name)
elif p.kind == p.KEYWORD_ONLY:
ks = '%s%s[%r] = %s\n\t\t' % (ks, kn, p.name, p.name)
elif p.kind == p.VAR_KEYWORD:
ks = '%s%s.update(%s)\n\t\t' % (ks, kn, p.name)
defstring = ('def wrapouter(wrapped = wrapped):'
'\n\tdef wrapinner%s:'
'\n\t\t%s%sreturn wrapped(*%s, **%s)'
'\n\treturn wrapinner' % (sig, ps, ks, pn, kn))
def wrapper(f):
loc = {'wrapped': f}
exec(defstring, f.__globals__, loc)
return loc['wrapouter']()
return wrapper
More information about the Python-list
mailing list