[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