This issue tracker has been migrated to GitHub ,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2008年07月25日 15:26 by Antoine d'Otreppe, last changed 2022年04月11日 14:56 by admin. This issue is now closed.
Files | ||||
---|---|---|---|---|
File name | Uploaded | Description | Edit | |
fix.patch | eklitzke, 2010年01月14日 00:50 | trivial change to ignore missing "asssigned" attributes | ||
fix.patch | eklitzke, 2010年01月16日 04:11 | updated patch | ||
update_wrapper-ignore-missing-attributes.patch | july, 2010年05月02日 14:06 | patch with test updated (apply to trunk) |
Messages (20) | |||
---|---|---|---|
msg70256 - (view) | Author: Antoine d'Otreppe (Antoine d'Otreppe) | Date: 2008年07月25日 15:26 | |
When trying to do something like "functools.update_wrapper(myWrapper, str.split)" I got this error message: Traceback (most recent call last): File "<stdin>", line 1, in <module> File "Aspyct.py", line 175, in beforeCall _stickAdvice(function, theAdvice) File "Aspyct.py", line 90, in _stickAdvice functools.update_wrapper(theAdvice, function) File "/usr/lib/python2.5/functools.py", line 33, in update_wrapper setattr(wrapper, attr, getattr(wrapped, attr)) AttributeError: 'method_descriptor' object has no attribute '__module__' |
|||
msg70320 - (view) | Author: Benjamin Peterson (benjamin.peterson) * (Python committer) | Date: 2008年07月27日 13:05 | |
I think what you want is functools.update_wrapper(myWrapper, str.split, ('__name__', '__doc__')) |
|||
msg70328 - (view) | Author: Alyssa Coghlan (ncoghlan) * (Python committer) | Date: 2008年07月27日 18:12 | |
The problem actually has to do with trying to use update_wrapper on a method instead of a function - bound and unbound methods don't have all the same attributes that actual functions do. >>> import functools >>> functools.WRAPPER_ASSIGNMENTS ('__module__', '__name__', '__doc__') >>> functools.WRAPPER_UPDATES ('__dict__',) >>> def f(): pass ... >>> class C: ... def m(): pass ... >>> set(dir(f)) - set(dir(C.m)) set(['func_closure', 'func_dict', '__module__', 'func_name', 'func_defaults', '__dict__', '__name__', 'func_code', 'func_doc', 'func_globals']) Is an exception the right response when encountering a missing attribute? Given that the error can be explicitly silenced by writing "functools.update_wrapper(g, str.split, set(functools.WRAPPER_ASSIGNMENTS) & set(dir(str.split)))", I'm inclined to think the current behaviour is the correct option. Since this is an issue that doesn't come up with the main intended use case for update_wrapper (writing decorators), and since it can be handled easily by limiting the set of attributes copied to those the object actually has, I'm converting this tracker item to an enhancement request asking for a shorter way of spelling "ignore missing attributes" (e.g. a keyword-only flag). |
|||
msg70331 - (view) | Author: Antoine d'Otreppe (Antoine d'Otreppe) | Date: 2008年07月28日 00:05 | |
Thank you for considering this report :) |
|||
msg70352 - (view) | Author: Antoine Pitrou (pitrou) * (Python committer) | Date: 2008年07月28日 15:29 | |
Another possibility would be for methods to also get the __name__ and __module__ attributes. I don't see any counter-indication to it. |
|||
msg71677 - (view) | Author: Piotr Findeisen (findepi) | Date: 2008年08月21日 20:33 | |
IMO, I'd be better to always ignore missing attributes. Consider another use case (following code I've posted also on comp.lang.python): from functools import wraps, partial def never_throw(f): @wraps(f) def wrapper(*args, **kwargs): try: return f(*args, **kwargs) except: pass return wrapper Looks reasonable. But if I write never_throw(partial(int))('45a') I got the exception saying partial objects also don't have __module__ attribute. I was to use some additional parameters to @wraps, I would have to rewrite all my decorator definitions. And stil -- the main intent of wraps and update_wrapper is to write decorators, so IMO they should not throw on missing attributes. |
|||
msg71695 - (view) | Author: Alyssa Coghlan (ncoghlan) * (Python committer) | Date: 2008年08月21日 22:30 | |
If the object being wrapped isn't a plain vanilla function, then the odds are *very* good that __doc__ cannot be copied to the new function and still be correct. This is definitely the case for both modules and partial objects. So for both the use cases mentioned thus far, the AttributeError appears to me to be flagging a real problem with the way update_wrapper is being used. |
|||
msg97745 - (view) | Author: Evan Klitzke (eklitzke) | Date: 2010年01月14日 00:50 | |
I'm also interested in seeing this fixed. In the current behavior, the following code doesn't work: <<< start code from functools import wraps def magic(func): @wraps(func) def even_more_magic(*args): return func(*args) return even_more_magic class Frob(object): @magic @classmethod def hello(cls): print '%r says hello' % (cls,) >>> end code It fails because classmethods don't have a __module__ attribute, as commented upon elsewhere in this issue. To fix this, you'd either have to either pass in an `assigned` parameter to the `wraps` function, or swap the order of decorator application (i.e. `classmethod(magic(hello))`). This seems arbitrary and unnecessarily complex; skipping over a missing __module__ should be just fine. Mixing `functools.wraps` and `classmethod` is a relatively common use case that should be as simple as possible. I've attached a trivial patch which just ignores missing "assigned" attributes. |
|||
msg97829 - (view) | Author: Antoine Pitrou (pitrou) * (Python committer) | Date: 2010年01月15日 18:11 | |
The patch should come with an unit test (in Lib/test/test_functools.py). |
|||
msg97862 - (view) | Author: Evan Klitzke (eklitzke) | Date: 2010年01月16日 04:11 | |
New patch included, with a test case. I had wanted to check the classmethod __module__ thing directly, but that proved to be elusive, since the classmethod gets the __module__ attribute if the module is '__main__', and you can't delete that attribute. My test just tries to assign another attribute which doesn't exist. I just tried to copy the style of the rest of the module, lmk if there are any problems. |
|||
msg97908 - (view) | Author: Brian Curtin (brian.curtin) * (Python committer) | Date: 2010年01月16日 21:39 | |
In your test, the more common convention is to use assertFalse(foo) instead of assert_(not foo). |
|||
msg97909 - (view) | Author: Antoine Pitrou (pitrou) * (Python committer) | Date: 2010年01月16日 21:42 | |
I think it would be better to test with some of the real world examples given in this issue: str.split, and a functools.partial object. |
|||
msg100933 - (view) | Author: Russell Keith-Magee (freakboy3742) * | Date: 2010年03月12日 13:00 | |
As an extra data point: we just hit this problem in Django ticket #13093 (http://code.djangoproject.com/ticket/13093). In our case, a decorator was using wraps(); however, that decorator was breaking when it was used on a class with a __call__ method, because the instance of the class doesn't have a __name__ attribute. We've implemented the proposed workaround (i.e., check the attributes that are available and provide that tuple as the assigned argument), but I don't agree that this should be expected behavior. wraps() is used to make a decorated callable look like the callable that is being decorated; if there are different types of callable objects, I would personally expect wraps() to adapt to the differences, not raise an error if it sees anything other than a function. True, some attributes (like __doc__) won't always be correct as a result of wrapping on non-vanilla functions -- but then, that's true of plain vanilla functions, too. A decorator wrapping a function can fundamentally change what the wrapped function does, and there's no guarantee that the docstring for the wrapped function will still be correct after decoration. |
|||
msg104787 - (view) | Author: July Tikhonov (july) * | Date: 2010年05月02日 14:06 | |
Patch updated: bound and unbound methods, user-defined callable, partial object included in test. By the way, >>> [id(abs.__doc__) for i in range(5)] [140714383081744, 140714383081744, 140714383081744, 140714383081744, 140714383081744] >>> [id(s) for s in [abs.__doc__ for i in range(5)]] [140714383084040, 140714383082976, 140714383083144, 140714383075904, 140714383081744] How it can be explained? Built-in functions (and methods) _sometimes_ return a new instance of its '__doc__' (and '__name__'), and sometimes does not. (I found this trying to include built-in method into the test.) |
|||
msg104832 - (view) | Author: July Tikhonov (july) * | Date: 2010年05月03日 12:43 | |
To Evan Klitzke (eklitzke): >I'm also interested in seeing this fixed. In the current behavior, >the following code doesn't work: > ><<< start code >from functools import wraps > >def magic(func): > @wraps(func) > def even_more_magic(*args): > return func(*args) > return even_more_magic > >class Frob(object): > @magic > @classmethod > def hello(cls): > print '%r says hello' % (cls,) >>>> end code This code _should not_ work. [Unbound] classmethod object is not a method or a function, it is even not a callable; it is a descriptor (returning callable). So, it cannot be wrapped or decorated in such way. If you want something like this to work, you probably should place @classmethod on the upper level (in other words, apply classmethod after all other decorators): @classmethod @magic def hello(cls): print '%r says hello' % (cls,) |
|||
msg110679 - (view) | Author: Mark Lawrence (BreamoreBoy) * | Date: 2010年07月18日 19:47 | |
Is there anyone who can provide this issue with a bit of TLC as it's almost the 2nd birthday? |
|||
msg110691 - (view) | Author: Alyssa Coghlan (ncoghlan) * (Python committer) | Date: 2010年07月18日 21:39 | |
That would be me :) In line with the 'consenting adults' philosophy and with the current behaviour causing real world problems, I'll accept this RFE and check it in soon. |
|||
msg114101 - (view) | Author: Alyssa Coghlan (ncoghlan) * (Python committer) | Date: 2010年08月17日 06:20 | |
Implemented in r84132 (not based on this patch though). |
|||
msg154271 - (view) | Author: Yaniv Aknin (Yaniv.Aknin) | Date: 2012年02月25日 16:46 | |
Shouldn't this be fixed in 2.7 as well? It's a bug, it's in the wild, and it's causing people to do ugly (and maybe not 100% reliable) things like https://code.djangoproject.com/browser/django/trunk/django/utils/decorators.py#L68 |
|||
msg154301 - (view) | Author: Éric Araujo (eric.araujo) * (Python committer) | Date: 2012年02月26日 02:58 | |
Well, Nick judged that this was not a bug per se, but rather a request for enhancement, thus it was only committed to 3.3. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022年04月11日 14:56:36 | admin | set | github: 47695 |
2021年10月23日 21:13:15 | yaubi | set | nosy:
- yaubi |
2012年02月26日 02:58:53 | eric.araujo | set | messages: + msg154301 |
2012年02月25日 16:50:07 | eric.araujo | set | nosy:
+ eric.araujo |
2012年02月25日 16:46:58 | Yaniv.Aknin | set | nosy:
+ Yaniv.Aknin messages: + msg154271 |
2010年08月17日 06:20:13 | ncoghlan | set | status: open -> closed messages: + msg114101 stage: patch review -> resolved |
2010年07月18日 21:39:10 | ncoghlan | set | assignee: ncoghlan resolution: accepted messages: + msg110691 |
2010年07月18日 19:47:03 | BreamoreBoy | set | nosy:
+ BreamoreBoy messages: + msg110679 versions: + Python 3.2, - Python 2.6, Python 2.5, Python 3.1, Python 2.7 |
2010年05月03日 12:43:39 | july | set | messages: + msg104832 |
2010年05月02日 14:06:34 | july | set | files:
+ update_wrapper-ignore-missing-attributes.patch nosy: + july messages: + msg104787 |
2010年03月31日 09:21:49 | yaubi | set | nosy:
+ yaubi |
2010年03月12日 13:00:32 | freakboy3742 | set | nosy:
+ freakboy3742 messages: + msg100933 versions: + Python 2.6, Python 2.5 |
2010年01月16日 21:42:55 | pitrou | set | messages: + msg97909 |
2010年01月16日 21:39:38 | brian.curtin | set | nosy:
+ brian.curtin messages: + msg97908 keywords: + needs review stage: patch review |
2010年01月16日 04:11:30 | eklitzke | set | files:
+ fix.patch messages: + msg97862 |
2010年01月15日 18:11:52 | pitrou | set | messages: + msg97829 |
2010年01月14日 00:50:40 | eklitzke | set | files:
+ fix.patch nosy: + eklitzke messages: + msg97745 keywords: + patch |
2008年08月21日 22:30:44 | ncoghlan | set | messages: + msg71695 |
2008年08月21日 20:33:40 | findepi | set | nosy:
+ findepi messages: + msg71677 |
2008年07月28日 15:29:58 | pitrou | set | nosy:
+ pitrou messages: + msg70352 |
2008年07月28日 00:05:32 | Antoine d'Otreppe | set | messages: + msg70331 |
2008年07月27日 18:13:32 | ncoghlan | set | title: functools.update_wrapper bug -> Ignore missing attributes in functools.update_wrapper |
2008年07月27日 18:12:08 | ncoghlan | set | priority: normal assignee: ncoghlan -> (no value) type: behavior -> enhancement messages: + msg70328 versions: + Python 3.1, Python 2.7, - Python 2.5 |
2008年07月27日 13:05:11 | benjamin.peterson | set | nosy:
+ benjamin.peterson messages: + msg70320 |
2008年07月27日 07:47:31 | georg.brandl | set | assignee: ncoghlan nosy: + ncoghlan |
2008年07月25日 15:26:30 | Antoine d'Otreppe | create |