Based on a pair of tracker issues (#3445 and #9396) I'm considering a couple of adjustments to functools.wraps for 3.2. The first (#3445) is a request from ages ago to make update_wrapper more forgiving when it encounters a missing attribute. Instead of throwing AttributeError (as it does now), it would just skip the missing attribute. This would allow wraps to be used with other callables that don't fully mimic the function API. I was initially opposed to the idea, but over time I've come to think this is a case where practicality beats purity (since that really sums up functools.wraps in general - it is already the case that the copied info isn't quite right for the decorated function, but it's still better than using the wrapper function's own metadata). The second (#9396) came up in the context of the new cache decorators added to functools, and allowing applications to choose their own caching strategies. I suggested exposing the original (uncached) function, and Raymond suggested that the easiest way to enable that would be for functools.update_wrapper to add a new attribute that provides a reference to the original function. Some time back, we considered doing this automatically as an integral part of decoration, but decided that wasn't appropriate. However, building it into the explicit wrapping functions makes sense to me. To avoid namespace conflicts, I plan to use "__wraps__" as the name for the reference to the original function. Thoughts? Concerns? Better ideas? Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
2010/8/10 Nick Coghlan
Based on a pair of tracker issues (#3445 and #9396) I'm considering a couple of adjustments to functools.wraps for 3.2.
The first (#3445) is a request from ages ago to make update_wrapper more forgiving when it encounters a missing attribute. Instead of throwing AttributeError (as it does now), it would just skip the missing attribute. This would allow wraps to be used with other callables that don't fully mimic the function API. I was initially opposed to the idea, but over time I've come to think this is a case where practicality beats purity (since that really sums up functools.wraps in general - it is already the case that the copied info isn't quite right for the decorated function, but it's still better than using the wrapper function's own metadata).
That seems fine. With class decorators, I suppose it might be possible to have something like: def class_deco(cls): @functools.wraps(cls) class Spam: pass @class_deco class Eggs: pass which would require ignoring the absence of __annotations__.
The second (#9396) came up in the context of the new cache decorators added to functools, and allowing applications to choose their own caching strategies. I suggested exposing the original (uncached) function, and Raymond suggested that the easiest way to enable that would be for functools.update_wrapper to add a new attribute that provides a reference to the original function. Some time back, we considered doing this automatically as an integral part of decoration, but decided that wasn't appropriate. However, building it into the explicit wrapping functions makes sense to me. To avoid namespace conflicts, I plan to use "__wraps__" as the name for the reference to the original function.
Namespace conflict with what? I would prefer "wraps" unless it's standardized as a behavior for all decorators. -- Regards, Benjamin
On Wed, Aug 11, 2010 at 12:39 PM, Benjamin Peterson
The second (#9396) came up in the context of the new cache decorators added to functools, and allowing applications to choose their own caching strategies. I suggested exposing the original (uncached) function, and Raymond suggested that the easiest way to enable that would be for functools.update_wrapper to add a new attribute that provides a reference to the original function. Some time back, we considered doing this automatically as an integral part of decoration, but decided that wasn't appropriate. However, building it into the explicit wrapping functions makes sense to me. To avoid namespace conflicts, I plan to use "__wraps__" as the name for the reference to the original function.
Namespace conflict with what? I would prefer "wraps" unless it's standardized as a behavior for all decorators.
With any existing attributes on the function - there's a reason update_wrapper includes a call to __dict__.update(). Using a normal attribute means having to consider the implications of what to do if the object being wrapped already has an attribute with that name. By using a system-reserved name, we can duck that question entirely. My recollection of previous discussions is that the reason we didn't make it an implicit part of the decoration process is because not all decoration is about creating wrapper functions, so it gets messy trying to decide whether or not it should be added. The explicit use of the @wraps decorator when creating the wrapper function resolves that particular concern nicely. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Wed, Aug 11, 2010 at 12:39 PM, Benjamin Peterson
which would require ignoring the absence of __annotations__.
It turns out the patch that added __annotations__ support also made a change to make all of the copied attributes optional. So I'll be tidying up the implementation of that, extending it to the updated attributes and adding unit tests to make sure they're all optional. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Wed, 2010-08-11 at 13:10 +1000, Nick Coghlan wrote:
On Wed, Aug 11, 2010 at 12:39 PM, Benjamin Peterson
wrote: which would require ignoring the absence of __annotations__.
It turns out the patch that added __annotations__ support also made a change to make all of the copied attributes optional.
The discussion happened on issue 8814. I initially made only __annotations__ optional, however, after finding issue 1576241 on the tracker and thinking about it a bit, making all of the annotations optional seemed like the only sane solution. http://bugs.python.org/issue8814 http://bugs.python.org/issue1576241
So I'll be tidying up the implementation of that, extending it to the updated attributes and adding unit tests to make sure they're all optional.
Cheers, Nick.
-- Terrence
On Wed, Aug 11, 2010 at 4:39 AM, Benjamin Peterson
Namespace conflict with what? I would prefer "wraps" unless it's standardized as a behavior for all decorators.
Having the original function available as __wrapped__ would be really cool, although I'm not quite sure what the behaviour should be in corner cases like: * The decorator returns the original function (I suppose a reference to itself is okay?) * The decorator returns the a function that is already decorating something else. Schiavo Simon
On Wed, Aug 11, 2010 at 9:26 PM, Simon Cross
On Wed, Aug 11, 2010 at 4:39 AM, Benjamin Peterson
wrote: Namespace conflict with what? I would prefer "wraps" unless it's standardized as a behavior for all decorators.
Having the original function available as __wrapped__ would be really cool, although I'm not quite sure what the behaviour should be in corner cases like:
* The decorator returns the original function (I suppose a reference to itself is okay?) * The decorator returns the a function that is already decorating something else.
Those are the corner cases that make it more appropriate to have this as a behaviour of functools.update_wrapper() (and hence the functools.wraps() decorator) rather than built in to the decorator machinery. The change will just add the following line to update_wrapper(): wrapper.__wrapped__ = wrapped Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Wed, 11 Aug 2010 09:26:56 pm Simon Cross wrote:
On Wed, Aug 11, 2010 at 4:39 AM, Benjamin Peterson
wrote: Namespace conflict with what? I would prefer "wraps" unless it's standardized as a behavior for all decorators.
Having the original function available as __wrapped__ would be really cool, although I'm not quite sure what the behaviour should be in corner cases like:
* The decorator returns the original function (I suppose a reference to itself is okay?)
There's no reason why a function can't have an attribute that refers to the function itself. It works fine:
def f(): ... return f.__wrapped__ ... f.__wrapped__ = f
f() is f True
But in the case of a decorator returning the original function, the point is moot. If the original function is returned, then it hasn't been decorated at all, so there shouldn't be a __wrapped__ attribute added: def decorator(func): if not __debug__: # Return undecorated original. return func @wraps(func) def inner(*args): print args return func(*args) return inner
* The decorator returns the a function that is already decorating something else.
That shouldn't make any difference. Given: @wraps(f) def func(*args): do_something() return f(*args) then func.__wrapped__ gives f. If f itself wraps (say) g, and g wraps h, then you have: func.__wrapped__ => f func.__wrapped__.__wrapped__ => g func.__wrapped__.__wrapped__.__wrapped__ => h and so on, until you reach a function that doesn't wrap anything and doesn't have a __wrapped__ attribute. I'm +1 on the proposal. -- Steven D'Aprano
On Wed, Aug 11, 2010 at 3:00 PM, Steven D'Aprano
* The decorator returns the original function (I suppose a reference to itself is okay?)
There's no reason why a function can't have an attribute that refers to the function itself. It works fine:
Yes. But it's more common for the original function to have be modified in some way. e.g.: def autodoc(f): f.__doc__ += document_args(f) return f @autodoc def f(x, y): """Add two numbers""" return x + y And then f.__wrapped__ is not particularly useful because the original function no longer exists and odd things will happen. For example, in the code above autodoc(f.__wrapped__).__doc__ will not equal f.__doc__.
* The decorator returns the a function that is already decorating something else.
That shouldn't make any difference. Given:
@wraps(f) def func(*args): do_something() return f(*args)
then func.__wrapped__ gives f. If f itself wraps (say) g, and g wraps h, then you have:
I guess my description of the problem wasn't clear. I meant: def _debug(*args, **kwargs) print args, kwargs def mock(f): return _debug @mock def plus(a, b): return a + b @mock def prod(a, b): return a * b Schiavo Simon
I think this is a good idea, because sometimes getting the innermost wrapped function from a wrapper function is very useful. For example, when I use inspect.getsource(), in most case, I want to get the source code of the wrapped function, not the wrapper, because the wrapped function usually contains the most important code. Even further, we can add optional keyword argument to let the functions in inspect module to get the wrapped function instead of the wrapper function by following the __wrapped__ chain automatically. -- Ray Allen Best wishes!
On Thu, Aug 12, 2010 at 12:12 AM, Simon Cross
Yes. But it's more common for the original function to have be modified in some way. e.g.:
def autodoc(f): f.__doc__ += document_args(f) return f
@autodoc def f(x, y): """Add two numbers""" return x + y
And then f.__wrapped__ is not particularly useful because the original function no longer exists and odd things will happen. For example, in the code above autodoc(f.__wrapped__).__doc__ will not equal f.__doc__.
There's no call to wraps or update_wrapper here, so f.__wrapped__ won't exist.
I guess my description of the problem wasn't clear. I meant:
def _debug(*args, **kwargs) print args, kwargs
def mock(f): return _debug
@mock def plus(a, b): return a + b
@mock def prod(a, b): return a * b
Again, without any calls to wraps or update_wrapper, plus.__wrapped__ and prod.__wrapped__ won't exist. However, as I noted before, these kinds of scenario are the reason we decided that building this feature directly into the decorator machinery wasn't a good idea. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Wed, Aug 11, 2010 at 11:21 PM, Nick Coghlan
However, as I noted before, these kinds of scenario are the reason we decided that building this feature directly into the decorator machinery wasn't a good idea.
I agree. I was just replying to Steven's response to my post. :) Schiavo Simon
On Aug 11, 2010, at 6:00 AM, Steven D'Aprano wrote:
@wraps(f) def func(*args): do_something() return f(*args)
then func.__wrapped__ gives f. If f itself wraps (say) g, and g wraps h, then you have:
func.__wrapped__ => f func.__wrapped__.__wrapped__ => g func.__wrapped__.__wrapped__.__wrapped__ => h
and so on, until you reach a function that doesn't wrap anything and doesn't have a __wrapped__ attribute.
I'm +1 on the proposal.
+1 from me also. The ability to introspect is basic to Python's design. Objects know their class, functions know their code objects, bound methods know both their underlying function, classes know their own class dictionary, etc. Raymond
On 8/11/2010 3:16 PM, Raymond Hettinger wrote:
The ability to introspect is basic to Python's design. Objects know their class, functions know their code objects, bound methods know both their underlying function, classes know their own class dictionary, etc.
Should iterators know their iterable when there is one? There is or was a request for this on python-list, I believe, a few days ago. I suggested bad idea because a) iterator requirement is intentially minimal b) not all iterators have underlying object c) OP wanted to mutate underlying object (list) while iterating I did give a my_iter class that would do what OP wanted. -- Terry Jan Reedy
On Aug 11, 2010, at 2:37 PM, Terry Reedy wrote:
On 8/11/2010 3:16 PM, Raymond Hettinger wrote:
The ability to introspect is basic to Python's design. Objects know their class, functions know their code objects, bound methods know both their underlying function, classes know their own class dictionary, etc.
Should iterators know their iterable when there is one?
There is or was a request for this on python-list, I believe, a few days ago. I suggested bad idea because a) iterator requirement is intentially minimal b) not all iterators have underlying object c) OP wanted to mutate underlying object (list) while iterating I did give a my_iter class that would do what OP wanted.
I agree with your assessment. Also an iterator is a protocol, not a single class. Raymond
2010/8/11 Raymond Hettinger
On Aug 11, 2010, at 2:37 PM, Terry Reedy wrote:
On 8/11/2010 3:16 PM, Raymond Hettinger wrote:
The ability to introspect is basic to Python's design. Objects know their class, functions know their code objects, bound methods know both their underlying function, classes know their own class dictionary, etc.
Should iterators know their iterable when there is one?
There is or was a request for this on python-list, I believe, a few days ago. I suggested bad idea because a) iterator requirement is intentially minimal b) not all iterators have underlying object c) OP wanted to mutate underlying object (list) while iterating I did give a my_iter class that would do what OP wanted.
I agree with your assessment. Also an iterator is a protocol, not a single class.
As is decoration... -- Regards, Benjamin
On Aug 11, 2010, at 3:28 PM, Benjamin Peterson wrote:
2010/8/11 Raymond Hettinger
: On Aug 11, 2010, at 2:37 PM, Terry Reedy wrote:
On 8/11/2010 3:16 PM, Raymond Hettinger wrote:
The ability to introspect is basic to Python's design. Objects know their class, functions know their code objects, bound methods know both their underlying function, classes know their own class dictionary, etc.
Should iterators know their iterable when there is one?
There is or was a request for this on python-list, I believe, a few days ago. I suggested bad idea because a) iterator requirement is intentially minimal b) not all iterators have underlying object c) OP wanted to mutate underlying object (list) while iterating I did give a my_iter class that would do what OP wanted.
I agree with your assessment. Also an iterator is a protocol, not a single class.
As is decoration...
This isn't proposed for decoration in general, just for wraps(). In that respect, it is very much like bound methods or other things that introspect. The length of the this thread is surprising. It is a rather basic piece of functionality with no real downside. Raymond
On 8/11/2010 5:37 PM, Terry Reedy wrote:
On 8/11/2010 3:16 PM, Raymond Hettinger wrote:
The ability to introspect is basic to Python's design. Objects know their class, functions know their code objects, bound methods know both their underlying function, classes know their own class dictionary, etc.
Should iterators know their iterable when there is one?
There is or was a request for this on python-list, I believe, a few days ago. I suggested bad idea because a) iterator requirement is intentially minimal b) not all iterators have underlying object c) OP wanted to mutate underlying object (list) while iterating I did give a my_iter class that would do what OP wanted.
Mutation of the underlying iterable is a terrible idea. Imagine the confusion when multiple iterators are active on the same iterable. But I suspect I am preaching to the choir here. regards Steve -- Steve Holden +1 571 484 6266 +1 800 494 3119 DjangoCon US September 7-9, 2010 http://djangocon.us/ See Python Video! http://python.mirocommunity.org/ Holden Web LLC http://www.holdenweb.com/
The second (#9396) came up in the context of the new cache decorators added to functools, and allowing applications to choose their own caching strategies. I suggested exposing the original (uncached) function, and Raymond suggested that the easiest way to enable that would be for functools.update_wrapper to add a new attribute that provides a reference to the original function.
I moved this feature request to its own bug after brief IRC discussion with RDM: http://bugs.python.org/issue9567 The idea was to separate concerns and eventually get feedback from people reading new-bugs-announce, but your email actually does that :) I say “add attribute to partial objects” in the bug title since I don’t know if it’s feasible in wraps only; while update_wrapper is simple Python code, wraps merely delegates to _functools.partial, so please change the title (and maybe add easy keyword) if appropriate. Regards
On Wed, Aug 11, 2010 at 12:48 PM, Éric Araujo
The second (#9396) came up in the context of the new cache decorators added to functools, and allowing applications to choose their own caching strategies. I suggested exposing the original (uncached) function, and Raymond suggested that the easiest way to enable that would be for functools.update_wrapper to add a new attribute that provides a reference to the original function.
I moved this feature request to its own bug after brief IRC discussion with RDM: http://bugs.python.org/issue9567
The idea was to separate concerns and eventually get feedback from people reading new-bugs-announce, but your email actually does that :)
I say “add attribute to partial objects” in the bug title since I don’t know if it’s feasible in wraps only; while update_wrapper is simple Python code, wraps merely delegates to _functools.partial, so please change the title (and maybe add easy keyword) if appropriate.
Ah, that's the trick though - the partial object is the *decorator*, so when you write @wraps(f) def wrapper: # .... it is equivalent to: def wrapper: # .... wrapper = partial(update_wrapper, wrapped=f)(wrapper) The partial object is a transient thing during the decoration process - the wrapper function itself is the object that persists. So it's only update_wrapper that needs changing to add the new attribute. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On 8/10/2010 10:58 PM, Nick Coghlan wrote:
On Wed, Aug 11, 2010 at 12:48 PM, Éric Araujo
wrote: The second (#9396) came up in the context of the new cache decorators added to functools, and allowing applications to choose their own caching strategies. I suggested exposing the original (uncached) function, and Raymond suggested that the easiest way to enable that would be for functools.update_wrapper to add a new attribute that provides a reference to the original function.
I moved this feature request to its own bug after brief IRC discussion with RDM: http://bugs.python.org/issue9567
The idea was to separate concerns and eventually get feedback from people reading new-bugs-announce, but your email actually does that :)
I say “add attribute to partial objects” in the bug title since I don’t know if it’s feasible in wraps only; while update_wrapper is simple Python code, wraps merely delegates to _functools.partial, so please change the title (and maybe add easy keyword) if appropriate.
Ah, that's the trick though - the partial object is the *decorator*, so when you write
@wraps(f) def wrapper: # ....
it is equivalent to:
def wrapper: # ....
wrapper = partial(update_wrapper, wrapped=f)(wrapper)
The partial object is a transient thing during the decoration process - the wrapper function itself is the object that persists.
So it's only update_wrapper that needs changing to add the new attribute.
One of the things that's slightly irking about the decorator syntax is that a decorator is always called with exactly one argument, and that if you want to write a parameterized decorator you therefore end up writing a function that returns a function that returns a function. I've scratched my head about how partials (or indeed anything else) could be used to make the extra level of indirection necessary, but haven' come up with anything that even I could regard as acceptable. But I can't escape this feeling that there must be a way. regards Steve -- Steve Holden +1 571 484 6266 +1 800 494 3119 DjangoCon US September 7-9, 2010 http://djangocon.us/ See Python Video! http://python.mirocommunity.org/ Holden Web LLC http://www.holdenweb.com/
On Tue, Aug 10, 2010 at 8:22 PM, Steve Holden
One of the things that's slightly irking about the decorator syntax is that a decorator is always called with exactly one argument, and that if you want to write a parameterized decorator you therefore end up writing a function that returns a function that returns a function.
I've scratched my head about how partials (or indeed anything else) could be used to make the extra level of indirection necessary, but haven' come up with anything that even I could regard as acceptable. But I can't escape this feeling that there must be a way.
Someone at EuroPython brought up this very criticism. But my argument against it is: you should be able to write either @bar(args) def func(): ... or foo = bar(args) @foo def func(): ... and you should be able to predict how these two relate using standard knowledge about what it means to say foo = bar(args) and then use foo in an expression. That pretty much rules out solutions using partial IMO. (Not that I mind -- I find examples using partial as hard to read as code using reduce()... :-) -- --Guido van Rossum (python.org/~guido)
On Wed, Aug 11, 2010 at 1:22 PM, Steve Holden
One of the things that's slightly irking about the decorator syntax is that a decorator is always called with exactly one argument, and that if you want to write a parameterized decorator you therefore end up writing a function that returns a function that returns a function.
I've scratched my head about how partials (or indeed anything else) could be used to make the extra level of indirection necessary, but haven' come up with anything that even I could regard as acceptable. But I can't escape this feeling that there must be a way.
Making the parameterised decorator a callable rather than a function can sometimes help, provided there are only a few parameters actually needed in the wrapper function. Making parameterised decorators easier to parse mentally was pointed out [1] as a possible benefit of pursuing PEP 3150 (statement local namespaces). That PEP as a whole is still sitting in "not enough practical benefit to justify the pain" territory though. Cheers, Nick. [1] http://mail.python.org/pipermail/python-ideas/2010-July/007691.html -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Wed, 11 Aug 2010 12:30:40 +1000
Nick Coghlan
The second (#9396) came up in the context of the new cache decorators added to functools, and allowing applications to choose their own caching strategies. I suggested exposing the original (uncached) function, and Raymond suggested that the easiest way to enable that would be for functools.update_wrapper to add a new attribute that provides a reference to the original function. Some time back, we considered doing this automatically as an integral part of decoration, but decided that wasn't appropriate. However, building it into the explicit wrapping functions makes sense to me. To avoid namespace conflicts, I plan to use "__wraps__" as the name for the reference to the original function.
I think it should be "__wrapped__". Regards Antoine.
On Wed, Aug 11, 2010 at 8:45 PM, Antoine Pitrou
On Wed, 11 Aug 2010 12:30:40 +1000 Nick Coghlan
wrote: The second (#9396) came up in the context of the new cache decorators added to functools, and allowing applications to choose their own caching strategies. I suggested exposing the original (uncached) function, and Raymond suggested that the easiest way to enable that would be for functools.update_wrapper to add a new attribute that provides a reference to the original function. Some time back, we considered doing this automatically as an integral part of decoration, but decided that wasn't appropriate. However, building it into the explicit wrapping functions makes sense to me. To avoid namespace conflicts, I plan to use "__wraps__" as the name for the reference to the original function.
I think it should be "__wrapped__".
Agreed, particularly since the relevant argument to update_wrapper() is already called "wrapped". Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Actually, what is the problem with having all decorators add a __decorated__ to the function that ultimately gets returned, pointing to the function they decorated? I guess I never saw that discussion. Perhaps set it to None when the decorator is the same as the decorated (no wrapping involved). The alternative is sifting through closures, trying to figure out which is the decorated function. Finding the original decorated function has been a real pain, particularly when a function has more than one closure cell. -eric -----Original Message----- From: python-dev-bounces+esnow=verio.net@python.org [mailto:python-dev-bounces+esnow=verio.net@python.org] On Behalf Of Nick Coghlan Sent: Tuesday, August 10, 2010 8:31 PM To: python-dev@python.org Subject: [Python-Dev] Proposed tweaks to functools.wraps Based on a pair of tracker issues (#3445 and #9396) I'm considering a couple of adjustments to functools.wraps for 3.2. The first (#3445) is a request from ages ago to make update_wrapper more forgiving when it encounters a missing attribute. Instead of throwing AttributeError (as it does now), it would just skip the missing attribute. This would allow wraps to be used with other callables that don't fully mimic the function API. I was initially opposed to the idea, but over time I've come to think this is a case where practicality beats purity (since that really sums up functools.wraps in general - it is already the case that the copied info isn't quite right for the decorated function, but it's still better than using the wrapper function's own metadata). The second (#9396) came up in the context of the new cache decorators added to functools, and allowing applications to choose their own caching strategies. I suggested exposing the original (uncached) function, and Raymond suggested that the easiest way to enable that would be for functools.update_wrapper to add a new attribute that provides a reference to the original function. Some time back, we considered doing this automatically as an integral part of decoration, but decided that wasn't appropriate. However, building it into the explicit wrapping functions makes sense to me. To avoid namespace conflicts, I plan to use "__wraps__" as the name for the reference to the original function. Thoughts? Concerns? Better ideas? Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia _______________________________________________ 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/esnow%40verio.net This email message is intended for the use of the person to whom it has been sent, and may contain information that is confidential or legally protected. If you are not the intended recipient or have received this message in error, you are not authorized to copy, distribute, or otherwise use this message or its attachments. Please notify the sender immediately by return e-mail and permanently delete this message and any attachments. Verio, Inc. makes no warranty that this email is error or virus free. Thank you.
On Sat, Aug 14, 2010 at 1:01 AM, Eric Snow
Actually, what is the problem with having all decorators add a __decorated__ to the function that ultimately gets returned, pointing to the function they decorated? I guess I never saw that discussion. Perhaps set it to None when the decorator is the same as the decorated (no wrapping involved). The alternative is sifting through closures, trying to figure out which is the decorated function. Finding the original decorated function has been a real pain, particularly when a function has more than one closure cell.
Because decorators don't always return simple wrapper functions - sometimes they return the original function and sometimes they completely replace the original function with something else entirely (which may not be callable, or even mutable). We refused the temptation to try to guess when it was appropriate to add the referring attribute. functools.update_wrapper and functools.wraps are explicit though, so it's easy to add the attribute there, we just hadn't thought of doing it before now. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
True. It is tricky. However, not as tricky as finding the decorated function after the fact (unless I am missing something). But maybe that is a fringe need (finding the original function).
-eric
-----Original Message-----
From: Nick Coghlan [mailto:ncoghlan@gmail.com]
Sent: Friday, August 13, 2010 10:07 AM
To: Eric Snow
Cc: python-dev@python.org
Subject: Re: [Python-Dev] Proposed tweaks to functools.wraps
On Sat, Aug 14, 2010 at 1:01 AM, Eric Snow
Actually, what is the problem with having all decorators add a __decorated__ to the function that ultimately gets returned, pointing to the function they decorated? I guess I never saw that discussion. Perhaps set it to None when the decorator is the same as the decorated (no wrapping involved). The alternative is sifting through closures, trying to figure out which is the decorated function. Finding the original decorated function has been a real pain, particularly when a function has more than one closure cell.
Because decorators don't always return simple wrapper functions - sometimes they return the original function and sometimes they completely replace the original function with something else entirely (which may not be callable, or even mutable). We refused the temptation to try to guess when it was appropriate to add the referring attribute. functools.update_wrapper and functools.wraps are explicit though, so it's easy to add the attribute there, we just hadn't thought of doing it before now. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia This email message is intended for the use of the person to whom it has been sent, and may contain information that is confidential or legally protected. If you are not the intended recipient or have received this message in error, you are not authorized to copy, distribute, or otherwise use this message or its attachments. Please notify the sender immediately by return e-mail and permanently delete this message and any attachments. Verio, Inc. makes no warranty that this email is error or virus free. Thank you.
participants (13)
-
Antoine Pitrou
-
Benjamin Peterson
-
Eric Snow
-
Guido van Rossum
-
Nick Coghlan
-
Ray Allen
-
Raymond Hettinger
-
Simon Cross
-
Steve Holden
-
Steven D'Aprano
-
Terrence Cole
-
Terry Reedy
-
Éric Araujo