update_wrapper should preserve staticmethod behavior
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/
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. python2.5/functools.py", line 33, in update_wrapper setattr(wrapper, attr, getattr(wrapped, attr)) AttributeError: 'staticmethod' object has no attribute '__module__'
Please submit a fix to the issue tracker at bugs.python.org if you
care about this.
On Wed, Jun 11, 2008 at 7:06 AM, Calvin Spealman
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__'
Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/guido%40python.org
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
Calvin Spealman
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__'
Well, if staticmethod doesn't mirror the original function's __module__ attribute, I'd say staticmethod is the culprit. Since Python grew the update_wrapper function, it seems reasonable to ask that all decorators (or decorator-alikes) provided with Python call update_wrapper. Of course staticmethod is written in C, so is there a C function somewhere providing the same functionality as update_wrapper does?
staticmethod doesn't wrap anything, it just creates a descriptor on the class with a __get__ that returns the original, untouched callable. Doesn't even care _what_ the thing you use it on is (function, other callable, or something else entirely.) This actually shouldn't be attempted on non-staticmethod descriptors, after thinking about it. Can't be sure that desc.__get__(cls) is usable to wrap when, at the end, you will be doing some_instance.a and now had the wrong __get__() signature used. Oh, no! class A(object): @d @some_decorator_returns_a_descriptor def a(): pass What should probably happen here is that d needs to see its decorating a descriptor and itself return a descriptor to pass along the right behavior. So, when you do A().a() you should have d.__get__ (cls, inst) calling some_decorator_returns_a_descriptor.__get__(cls, inst) and acting as if that was the thing it decorated. Of course, this would have the probably unexpected behavior of decorating such things at binding time (ie, when a classmethod is bound) rather than after definition. Not good. They could be cached and this used to implement new functionality that the decorator can be applied to the class method once for each class its bound to (interesting? useful?), but I can't think of a justification myself. Unless any of this other behavior could be justified, I'll provide an update_wrapper() patch to at least become staticmethod smart. On Jun 11, 2008, at 1:48 PM, Antoine Pitrou wrote:
Calvin Spealman
writes: 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__'
Well, if staticmethod doesn't mirror the original function's __module__ attribute, I'd say staticmethod is the culprit.
Since Python grew the update_wrapper function, it seems reasonable to ask that all decorators (or decorator-alikes) provided with Python call update_wrapper. Of course staticmethod is written in C, so is there a C function somewhere providing the same functionality as update_wrapper does?
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/ ironfroggy%40socialserve.com
Calvin Spealman
staticmethod doesn't wrap anything, it just creates a descriptor on the class with a __get__ that returns the original, untouched callable. Doesn't even care _what_ the thing you use it on is (function, other callable, or something else entirely.)
FWIW, I still disagree. Technically, it might not "wrap" anything (in the sense that it isn't defined as a function returning another function - which is a narrow definition of a wrapper by the way), but semantically it does. To the non-expert programmer, it is a decorator like any other one. The fact that it is implemented differently from other decorators is not an excuse for it to follow different rules... Unless, of course, there is a good *semantic* reason for staticmethod not to mirror the __module__ attribute. (by the way, does the same apply to classmethod as well?) Regards Antoine.
On Wed, Jun 11, 2008 at 12:22 PM, Antoine Pitrou
Calvin Spealman
writes: staticmethod doesn't wrap anything, it just creates a descriptor on the class with a __get__ that returns the original, untouched callable. Doesn't even care _what_ the thing you use it on is (function, other callable, or something else entirely.)
FWIW, I still disagree. Technically, it might not "wrap" anything (in the sense that it isn't defined as a function returning another function - which is a narrow definition of a wrapper by the way), but semantically it does. To the non-expert programmer, it is a decorator like any other one. The fact that it is implemented differently from other decorators is not an excuse for it to follow different rules...
Unless, of course, there is a good *semantic* reason for staticmethod not to mirror the __module__ attribute.
(by the way, does the same apply to classmethod as well?)
Remember (a) these are implemented in C, and (b) they were created in Python 2.2, before we even had decorators (first introduced in 2.4). I'm fine with making them more transparent and conformant to emerging protocols, but they will always be a little different, due to being implemented as objects rather than functional wrappers. The latter approach *can* be used for decorator implementations to, it just is done rarely. I see this as something that can be done post beta 1. It has to be done while carefully remaining backwards compatible though. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
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@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
participants (4)
-
Antoine Pitrou
-
Calvin Spealman
-
Guido van Rossum
-
Nick Coghlan