Michele Simionato wrote: > with the following advantages: > > 1. one-level of nesting is saved ("flat is better than nested") > 2. name, docstring and dictionary of the original function are > preserved; > 3. the signature of the original function is preserved (this one is > nontrivial). >
And at least the following bugs: >>> from decorator import * >>> @decorator def chatty(f, *args, **kw): print "Calling %r" % f.__name__ return f(*args, **kw) >>> @chatty def f((x,y)): pass Traceback (most recent call last): File "<pyshell#11>", line 1, in -toplevel- @chatty File "C:\Work\decorator\decorator.py", line 119, in __call__ return _decorate(func, self.caller) File "C:\Work\decorator\decorator.py", line 89, in _decorate dec_func = eval(lambda_src, evaldict) File "<string>", line 1 lambda ['x', 'y']: call(func, ['x', 'y']) ^ SyntaxError: invalid syntax >>> I would have thought the code would be simpler if it used inspect.formatargspec to produce an argument list which is correctly formatted for all the weird corner cases: >>> class Default: def __init__(self, n): self.n = n def __repr__(self): return 'defarg[%d]' % self.n >>> def f(x, y=1, z=2, *args, **kw): pass >>> a,v,vk,d = inspect.getargspec(f) >>> inspect.formatargspec(a,v,vk,map(Default, range(len(d)))) '(x, y=defarg[0], z=defarg[1], *args, **kw)' >>> def g(x, (y,z)=(1,2), *args, **kw): pass >>> a,v,vk,d = inspect.getargspec(g) >>> inspect.formatargspec(a,v,vk,map(Default, range(len(d)))) '(x, (y, z)=defarg[0], *args, **kw)' >>> d ((1, 2),) >>> Even if we manage to decorate the function, the decorated object has the wrong name in tracebacks: >>> @chatty def f(): pass >>> f(3) Traceback (most recent call last): File "<pyshell#15>", line 1, in -toplevel- f(3) TypeError: <lambda>() takes no arguments (1 given) >>> Using a lambda function here seems needlessly perverse. You know the name of the function you want to define and you are going to eval a string, so why not exec the string instead "def %(name)s %(fullsign)s: call(func, %(shortsign)s)" % getinfo(func) and then just pick the function out of the namespace after the exec? e.g. def _decorate(func, caller): """Takes a function and a caller and returns the function decorated with that caller. The decorated function is obtained by evaluating a lambda function with the correct signature. """ lambda_src = "def %(name)s(%(fullsign)s): call(func, %(shortsign)s)" % getinfo(func) # print lambda_src # for debugging execdict = dict(func=func, call=caller, defarg=func.func_defaults or ()) exec lambda_src in execdict dec_func = execdict.get(func.__name__) dec_func.__doc__ = func.__doc__ dec_func.__dict__ = func.__dict__ return dec_func There is still a problem with this one though. If the function has arguments named 'call', 'func', or 'defarg' (or for that matter my code above introduces a new problem if the function is called by one of those names) nasty things will happen. Fortunately you have a list of argument names readily available so it shouldn't be too hard to generate unique names to use in their place. -- http://mail.python.org/mailman/listinfo/python-list