Re: [Python-ideas] Proposal for new-style decorators

On Tue, Apr 26, 2011 at 11:20 PM, Terry Reedy <tjreedy@udel.edu> wrote:
I totally agree with that. When defining a decorating function, you don't have any syntactic element that could explain the reader of your code that this function is actually a decorator. It is only when applied on a function with the @-syntax that the mechanism becomes visible (but this is not always done in the same file). This can be considered as a strength (any function with a correct input/output is likely to later become a decorator, even if the original author did not thought about it). However, according to my own little experience, more than 9 times out of 10, you perfectly now when writting such a function that it is actually a decorator. Using the proposed @decorator idiom (hey see, I haven't written "syntax" ;-) has at least the advantage to be explicit when you want to be explicit (besides the other features it provides),
Yes, I know that using callable class can be an alternative to the nested function pattern. In the pattern you talk about, the __init__ method gets the decorator arguments, the __call__ method serves as a decorator making function, and a third method is used to generate the actual decorating function. As a result, the boilerplate is approximatively the same as with the nested functions idiom. But this is not what is done here, because the end-user only writes a single function, not a whole class. In my proposal, the two nested functions are avoided by the fact that (1) the decorator attributes are automatically injected as meta-attributes (this is the role of the middle nested function in the standard idiom), and (2) the decorating function is in charge to pass the whole set of arguments to the undecorated function (this is the role of the inner nested function in the standard idiom). As far as I know, I haven't seen the combination of these two elements before.
2. Introspection (more comments below), which your class also addresses.
OK. I'll drop the arguments on nested function, and simply focus on boilerplate and introspection. That makes sense.
Also agree. I've written "decorating function" by symmetry with the new idiom, but I knew that I would get some remark here ;-)
What I wanted to say is that wraps only does half of the job: it correctly copies the name and the docstring, but the signature presented by the help function is still test(*args, **keys), while it should actually be test(first=0, last=0) according to the undecorated function. The alternative proposed by the new idiom is to copy nothing at all: it simply says "OK, 'test' is a decorated function. If you want to know more look at 'test.func' to get the info about the undecorated one, and at 'test.deco' to see what the decorator has done". I prefer such a raw-but-explicit approach rather than an automatic half-baked, half-bogus one. Moreover, it is not easy with the 'functools' module to provide introspection of the decorating function, once you've got the decorated one.
Right again, but this overweights the boilerplate even more compared to the '@wraps' decorator, no?
I second the other recommendations to make your proposal available on the cookbook site, etc.
That sounds good... Thanks a lot, Terry!

Christophe Schlick writes:
But that's a lie; the undecorated function is *not* what *my* code calls. I would be very confused if I committed a syntax error according to the help, but the compiler let it go silently. In your approach, this doesn't get caught until the erroneous call is actually made, which might be after the code is put into production. Or it may not get caught at all, depending on whether the decorater-decorater-decorated function checks arguments or simply swallows unneeded arguments. In the standard approach, I see "test(*args, **keys)" and go "!@#$% Name your !@#$% arguments, for heaven's sake!" and go read the code (the first time), or "oh, it's decorated, gotta read the code, I guess ... mmrmfrmblgrr" (with experience). But the help's less-than- informative signature tells me I need to review carefully, whereas in your approach I would tend to leave it up to the compiler to some extent.

On Wed, Apr 27, 2011 at 4:44 AM, Stephen J. Turnbull <stephen@xemacs.org> wrote:
I ma not sure to understand the problem. In my approach, you get:
help test <function <deco>test(*args, **keys)>
then you say, "oh, it's decorated', let's see more..."
help test.func # that's the undecorated function <function test(first=0, last=0)>
Not only, you've got the clear information that the function is decorate (by the '<deco>' prefix) but also you get a standard and immediate solution to get the right signature: help(test.func) I do not understand why this is worse that the <function test(*args, **keys)> answer provided by the '@wraps' function ? CS

Attacking (some aspects of) the same problem from a different angle, note that part of the motivation of PEP 362 (function signature objects) is to allow functools.wraps to be more effective at preserving signature information, and to allow similar gains for functools.partial. It also allows signature checks to be performed independently of actually calling functions (via the Signature.bind() method). As of 3.2, functools.wraps has already been updated to automatically include a __wrapped__ attribute on the resulting callable, which links back to the underlying function. The "func" attribute on functools.partial serves the same purpose. Actually *writing* decorators is still somewhat cumbersome by default, but if anything were to happen on that front, the most likely would be to ask Michele about making the decorators module part of the standard library. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Wed, Apr 27, 2011 at 6:26 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Exactly, there are really two complementary solutions to tackle the problem: 1 - copy *all* information from the undecorated function to the undecorated one. As you say, this requires to extend the '@wraps' tool to include signature copying (and with function signature objects of PEP 362 this would be trivial) 2 - let the decorated function as is without copying anything, but provide a simple, systematic solution to get the name/signature/docstring of the undecorated function. That's the idea of my proposal
As I said in my initial post, there was quite a long discussion in python-dev exactly about that idea. I finally get the reference to the topic. Here is the first post of that thread: http://mail.python.org/pipermail/python-dev/2009-April/088387.html and here is an extract of what Guido thinks about it: --- On Wed, Apr 8, 2009 at 7:51 PM, Guido van Rossum <guido at python.org> wrote:
Reading that thread was actually the starting point, several months ago, when I came out with the idea of NSD. The initial question was: "so, if copying signature is not a step in the right direction, is there any alternative that offers similar functionality?". In fact, reducing the boilerplate of decorator pattern only came as a side-effect CS

Another snippet of the BDFL going in the same direction: On Fri, Apr 10, 2009 at 7:55 PM, Guido van Rossum <guido at python.org> wrote:
That's one of the reason for which I've added the recursive 'self.deco' reference in the decorated function, to get the complete info about all chained decorators applied on a given function. Here again, I am not aware of any implementation that offers a similar feature. CS

Christophe Schlick writes:
But that's a lie; the undecorated function is *not* what *my* code calls. I would be very confused if I committed a syntax error according to the help, but the compiler let it go silently. In your approach, this doesn't get caught until the erroneous call is actually made, which might be after the code is put into production. Or it may not get caught at all, depending on whether the decorater-decorater-decorated function checks arguments or simply swallows unneeded arguments. In the standard approach, I see "test(*args, **keys)" and go "!@#$% Name your !@#$% arguments, for heaven's sake!" and go read the code (the first time), or "oh, it's decorated, gotta read the code, I guess ... mmrmfrmblgrr" (with experience). But the help's less-than- informative signature tells me I need to review carefully, whereas in your approach I would tend to leave it up to the compiler to some extent.

On Wed, Apr 27, 2011 at 4:44 AM, Stephen J. Turnbull <stephen@xemacs.org> wrote:
I ma not sure to understand the problem. In my approach, you get:
help test <function <deco>test(*args, **keys)>
then you say, "oh, it's decorated', let's see more..."
help test.func # that's the undecorated function <function test(first=0, last=0)>
Not only, you've got the clear information that the function is decorate (by the '<deco>' prefix) but also you get a standard and immediate solution to get the right signature: help(test.func) I do not understand why this is worse that the <function test(*args, **keys)> answer provided by the '@wraps' function ? CS

Attacking (some aspects of) the same problem from a different angle, note that part of the motivation of PEP 362 (function signature objects) is to allow functools.wraps to be more effective at preserving signature information, and to allow similar gains for functools.partial. It also allows signature checks to be performed independently of actually calling functions (via the Signature.bind() method). As of 3.2, functools.wraps has already been updated to automatically include a __wrapped__ attribute on the resulting callable, which links back to the underlying function. The "func" attribute on functools.partial serves the same purpose. Actually *writing* decorators is still somewhat cumbersome by default, but if anything were to happen on that front, the most likely would be to ask Michele about making the decorators module part of the standard library. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Wed, Apr 27, 2011 at 6:26 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Exactly, there are really two complementary solutions to tackle the problem: 1 - copy *all* information from the undecorated function to the undecorated one. As you say, this requires to extend the '@wraps' tool to include signature copying (and with function signature objects of PEP 362 this would be trivial) 2 - let the decorated function as is without copying anything, but provide a simple, systematic solution to get the name/signature/docstring of the undecorated function. That's the idea of my proposal
As I said in my initial post, there was quite a long discussion in python-dev exactly about that idea. I finally get the reference to the topic. Here is the first post of that thread: http://mail.python.org/pipermail/python-dev/2009-April/088387.html and here is an extract of what Guido thinks about it: --- On Wed, Apr 8, 2009 at 7:51 PM, Guido van Rossum <guido at python.org> wrote:
Reading that thread was actually the starting point, several months ago, when I came out with the idea of NSD. The initial question was: "so, if copying signature is not a step in the right direction, is there any alternative that offers similar functionality?". In fact, reducing the boilerplate of decorator pattern only came as a side-effect CS

Another snippet of the BDFL going in the same direction: On Fri, Apr 10, 2009 at 7:55 PM, Guido van Rossum <guido at python.org> wrote:
That's one of the reason for which I've added the recursive 'self.deco' reference in the decorated function, to get the complete info about all chained decorators applied on a given function. Here again, I am not aware of any implementation that offers a similar feature. CS
participants (3)
-
Christophe Schlick
-
Nick Coghlan
-
Stephen J. Turnbull