[Python-Dev] update_wrapper should preserve staticmethod behavior
Nick Coghlan
ncoghlan at gmail.com
Thu Jun 12 11:48:52 CEST 2008
Calvin Spealman wrote:
> I'd like to make a claim about the following example, that
> update_wrapper should be improved to preserve the behavior of known,
> built-in decorators. In this case, I'm talking about staticmethod. The
> order I list here feels natural, but it obviously doesn't work. The only
> reason it doesn't seems to be that it is trying to decorate the
> descriptor, not the function itself. This is expected, but it could
> certainly be smart enough to detect a descriptor and attempt to get the
> actual callable underneath, could it not? It would not work for all
> cases, of course.
>
> >>> def d(f):
> ... def nf(*a, **kw):
> ... print "decorated function called"
> ... return f(*a, **kwargs)
> ... functools.update_wrapper(nf, f)
> ... return nf
> ...
> >>> class A(object):
> ... @d
> ... @staticmethod
> ... def a(self):
> ... print "a"
> ...
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<stdin>", line 3, in A
> File "<stdin>", line 5, in d
> File
> "/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/functools.py",
> line 33, in update_wrapper
> setattr(wrapper, attr, getattr(wrapped, attr))
> AttributeError: 'staticmethod' object has no attribute '__module__'
d expects a function object, this code gives it a nonfunction object -
that's a bug in the function definition. Decorators differ in whether or
not they are stackable with other function decorators (i.e. they return
a function object, or another callable with a compatible API), or
whether they can only be used as terminal decorators (i.e. they return
something that doesn't look like a function).
classmethod and staticmethod fit in the latter category, and I don't see
any reason to change them.
Consider what happens in your example without the update_wrapper line:
>>> def d(f):
... def new(*args, **kwds):
... print "Calling decorated function"
... return f(*args, **kwds)
... return new
...
>>> class A(object):
... @d
... @staticmethod
... def a(*args, **kwds):
... print "Method called"
... print "Args:", args
... print "Keywords:", kwds
...
>>> A().a()
Calling decorated function
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in new
TypeError: 'staticmethod' object is not callable
If anything, the AttributeError from update wrapper is a net win since
the buggy code breaks at class definition time rather than when you try
to call the broken method.
Cheers,
Nick.
P.S. Checking for __get__ won't help with detecting non-function
descriptors anyway - function objects themselves define that method in
order to provide Python's normal instance method binding behaviour.
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
---------------------------------------------------------------
http://www.boredomandlaziness.org
More information about the Python-Dev
mailing list