Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #110646 > unrolled thread

Meta decorator with parameters, defined in explicit functions

Started byBen Finney <ben+python@benfinney.id.au>
First post2016-06-28 15:02 +1000
Last post2016-07-01 23:53 -0600
Articles 13 on this page of 33 — 10 participants

Back to article view | Back to comp.lang.python

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Meta decorator with parameters, defined in explicit functions Ben Finney <ben+python@benfinney.id.au> - 2016-06-28 15:02 +1000
    Re: Meta decorator with parameters, defined in explicit functions Paul Rubin <no.email@nospam.invalid> - 2016-06-27 23:08 -0700
    Re: Meta decorator with parameters, defined in explicit functions Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2016-06-28 18:39 +1000
    Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-06-29 19:43 -0700
      Re: Meta decorator with parameters, defined in explicit functions Steven D'Aprano <steve@pearwood.info> - 2016-06-30 13:49 +1000
      Fear and suspicion of lambdas, was Re: Meta decorator with parameters, defined in explicit functions Peter Otten <__peter__@web.de> - 2016-06-30 09:25 +0200
        Re: Fear and suspicion of lambdas, was Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-06-30 00:43 -0700
          Re: Fear and suspicion of lambdas, was Re: Meta decorator with parameters, defined in explicit functions Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2016-06-30 18:00 +1000
          Re: Fear and suspicion of lambdas, was Re: Meta decorator with parameters, defined in explicit functions Peter Otten <__peter__@web.de> - 2016-06-30 10:41 +0200
      Re: Fear and suspicion of lambdas, was Re: Meta decorator with parameters, defined in explicit functions dieter <dieter@handshake.de> - 2016-07-01 09:04 +0200
        Re: Fear and suspicion of lambdas, was Re: Meta decorator with parameters, defined in explicit functions Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-07-01 21:25 +0100
          Re: Fear and suspicion of lambdas, was Re: Meta decorator with parameters, defined in explicit functions Ben Finney <ben+python@benfinney.id.au> - 2016-07-02 11:52 +1000
          Re: Fear and suspicion of lambdas, was Re: Meta decorator with parameters, defined in explicit functions dieter <dieter@handshake.de> - 2016-07-02 08:57 +0200
          Re: Fear and suspicion of lambdas, was Re: Meta decorator with parameters, defined in explicit functions Ben Finney <ben+python@benfinney.id.au> - 2016-07-02 18:00 +1000
    Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-06-29 19:50 -0700
    Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-07-01 15:08 -0700
      Re: Meta decorator with parameters, defined in explicit functions Ian Kelly <ian.g.kelly@gmail.com> - 2016-07-01 23:09 -0600
        Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-07-01 23:40 -0700
          Re: Meta decorator with parameters, defined in explicit functions Ian Kelly <ian.g.kelly@gmail.com> - 2016-07-02 22:48 -0600
            Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-07-02 22:37 -0700
              Re: Meta decorator with parameters, defined in explicit functions Ian Kelly <ian.g.kelly@gmail.com> - 2016-07-03 05:52 -0600
                Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-07-03 14:17 -0700
                  Re: Meta decorator with parameters, defined in explicit functions Ian Kelly <ian.g.kelly@gmail.com> - 2016-07-03 16:38 -0600
                    Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-07-03 16:25 -0700
                      Re: Meta decorator with parameters, defined in explicit functions Chris Angelico <rosuav@gmail.com> - 2016-07-04 09:39 +1000
                        Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-07-03 16:59 -0700
                          Re: Meta decorator with parameters, defined in explicit functions Chris Angelico <rosuav@gmail.com> - 2016-07-04 10:06 +1000
                            Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-07-03 17:23 -0700
                      Re: Meta decorator with parameters, defined in explicit functions Ian Kelly <ian.g.kelly@gmail.com> - 2016-07-03 18:01 -0600
                        Re: Meta decorator with parameters, defined in explicit functions Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-07-03 17:06 -0700
                          Re: Meta decorator with parameters, defined in explicit functions Ian Kelly <ian.g.kelly@gmail.com> - 2016-07-03 18:20 -0600
      Re: Meta decorator with parameters, defined in explicit functions Ben Finney <ben+python@benfinney.id.au> - 2016-07-02 15:32 +1000
      Re: Meta decorator with parameters, defined in explicit functions Ian Kelly <ian.g.kelly@gmail.com> - 2016-07-01 23:53 -0600

Page 2 of 2 — ← Prev page 1 [2]


#110992

FromIan Kelly <ian.g.kelly@gmail.com>
Date2016-07-03 05:52 -0600
Message-ID<mailman.38.1467546815.2295.python-list@python.org>
In reply to#110976
On Sat, Jul 2, 2016 at 11:37 PM, Lawrence D’Oliveiro
<lawrencedo99@gmail.com> wrote:
> On Sunday, July 3, 2016 at 4:49:15 PM UTC+12, Ian wrote:
>>
>> On Sat, Jul 2, 2016 at 12:40 AM, Lawrence D’Oliveiro wrote:
>>>
>>> On Saturday, July 2, 2016 at 5:10:06 PM UTC+12, Ian wrote:
>>>>
>>>> You should use functools.wraps instead of clobbering the decorated
>>>> function's name and docstring:
>>>
>>> Where am I doing that?
>>
>> Using the implementation you posted:
>>
>>>>> @decorator_with_args
>> ... def my_decorator(func, *args, **kwargs):
>> ...     """Returns func unmodified."""
>> ...     return func
>> ...
>>>>> my_decorator.__doc__
>> 'generates a decorator which applies my_decorator to the given arguments'
>
> That is a function that I am generating, so naturally I want to give it a useful docstring. Am I “clobbering” any objects passed in by the caller, or returned by the caller? No, I am not.

It isn't useful, though. If somebody writes a docstring for a
function, it's because they want that to be the docstring for the
function, not some arbitrary and vague docstring assigned by a
decorator. If I type "help(my_decorator)" in the interactive
interpreter, I should see what my_decorator actually does, not
"generates a decorator which applies my_decorator", which just
confuses with its recursiveness. The only way to find out what
my_decorator actually does from the interactive interpreter would be
to realize that it's been opaquely decorated and type out something
like this:

>>> my_decorator.__closure__[0].cell_contents.__doc__
'Returns func unmodified.'

That's far too obscure. Alternatively, one could go read the source.
But if you have to go read the source to understand what a function
does, then you've lost the purpose of having a docstring in the first
place.

[toc] | [prev] | [next] | [standalone]


#111010

FromLawrence D’Oliveiro <lawrencedo99@gmail.com>
Date2016-07-03 14:17 -0700
Message-ID<d2f71d72-d2da-4f68-9d6a-ca059327afdd@googlegroups.com>
In reply to#110992
On Sunday, July 3, 2016 at 11:53:46 PM UTC+12, Ian wrote:
> On Sat, Jul 2, 2016 at 11:37 PM, Lawrence D’Oliveiro wrote:
>> That is a function that I am generating, so naturally I want to give it a
>> useful docstring. Am I “clobbering” any objects passed in by the caller,
>> or returned by the caller? No, I am not.
> 
> It isn't useful, though. If somebody writes a docstring for a
> function, it's because they want that to be the docstring for the
> function ...

Which I am not “clobbering” at all. Do you get that yet? My docstrings apply to *my* functions, not those supplied or generated by the caller.

[toc] | [prev] | [next] | [standalone]


#111016

FromIan Kelly <ian.g.kelly@gmail.com>
Date2016-07-03 16:38 -0600
Message-ID<mailman.47.1467585556.2295.python-list@python.org>
In reply to#111010
On Sun, Jul 3, 2016 at 3:17 PM, Lawrence D’Oliveiro
<lawrencedo99@gmail.com> wrote:
> On Sunday, July 3, 2016 at 11:53:46 PM UTC+12, Ian wrote:
>> On Sat, Jul 2, 2016 at 11:37 PM, Lawrence D’Oliveiro wrote:
>>> That is a function that I am generating, so naturally I want to give it a
>>> useful docstring. Am I “clobbering” any objects passed in by the caller,
>>> or returned by the caller? No, I am not.
>>
>> It isn't useful, though. If somebody writes a docstring for a
>> function, it's because they want that to be the docstring for the
>> function ...
>
> Which I am not “clobbering” at all. Do you get that yet? My docstrings apply to *my* functions, not those supplied or generated by the caller.

Sorry, but you're the one who doesn't seem to get it. Because it's a
decorator, "your" function is replacing the caller's function in the
caller's namespace. If the docstring written in the code for function
m.f is x, but the value of m.f.__doc__ is actually y, then the
docstring x is, for all practical purposes, clobbered. That you're not actually
modifying the original function is irrelevant.

For the purpose of the built-in help, it's the value of m.f.__doc__
that counts. For the purpose of documentation built with pydoc, it's
the value of m.f.__doc__ that counts. If the documentation for every
decorator in a module just reads "generates a decorator which applies
<function name> to the given arguments", that's useless documentation.

Which brings me to the other half of the point I was trying to make:
your decorator_with_args doesn't even *need* a docstring. It's not
part of the public interface. As a nested function that doesn't have
its own name within the module, help() or pydoc won't even include it
in their output. If you want to put something in the code to explain
what it does, that's fine, but use a comment for that. The docstring
doesn't add any value.

[toc] | [prev] | [next] | [standalone]


#111019

FromLawrence D’Oliveiro <lawrencedo99@gmail.com>
Date2016-07-03 16:25 -0700
Message-ID<62b43f29-e6ea-4d19-9e9f-1becb3988bd3@googlegroups.com>
In reply to#111016
On Monday, July 4, 2016 at 10:39:30 AM UTC+12, Ian wrote:
> Sorry, but you're the one who doesn't seem to get it. Because it's a
> decorator, "your" function is replacing the caller's function in the
> caller's namespace.

No it is not. The replacement happens here:

        def generated_decorator(func) :
            return \
                decorator(func, *args, **kwargs)
        #end generated_decorator

As you can see, the function being called, “decorator”, is supplied by the caller. The function being decorated, “func”, is supplied by the caller (along with the additional *args and **kwargs). And the returned function is returned *as-is*, so whatever docstring was assigned by the caller is *NOT* “clobbered”.

Once again, the only docstrings I am setting are the ones for functions that *I* generate.

Do you understand that yet?

[toc] | [prev] | [next] | [standalone]


#111020

FromChris Angelico <rosuav@gmail.com>
Date2016-07-04 09:39 +1000
Message-ID<mailman.49.1467589185.2295.python-list@python.org>
In reply to#111019
On Mon, Jul 4, 2016 at 9:25 AM, Lawrence D’Oliveiro
<lawrencedo99@gmail.com> wrote:
> On Monday, July 4, 2016 at 10:39:30 AM UTC+12, Ian wrote:
>> Sorry, but you're the one who doesn't seem to get it. Because it's a
>> decorator, "your" function is replacing the caller's function in the
>> caller's namespace.
>
> No it is not. The replacement happens here:
>
>         def generated_decorator(func) :
>             return \
>                 decorator(func, *args, **kwargs)
>         #end generated_decorator
>
> As you can see, the function being called, “decorator”, is supplied by the caller. The function being decorated, “func”, is supplied by the caller (along with the additional *args and **kwargs). And the returned function is returned *as-is*, so whatever docstring was assigned by the caller is *NOT* “clobbered”.
>
> Once again, the only docstrings I am setting are the ones for functions that *I* generate.
>
> Do you understand that yet?

Yes, in a technical sense, you are correct. It's also technically
correct to say that str.upper() doesn't convert its argument to
uppercase - it constructs a completely new string of all uppercase
letters. But in normal usage, people expect "x = x.upper()" to return
*the same string, uppercased*. In the same way, people expect "f =
deco(f)" to return *the same function, decorated*. Technically it's
generally going to be a new function, because functions are immutable
(more-or-less), but it should broadly be the same function.

def require_login(f):
    def modified(*a, **kw):
        if not logged_in:
            return "You must be logged in to do this."
        return f(*a, **kw)
    return modified

@require_login
def fire_ze_missiles():
    raise Exhausted

@require_login
def reboot_server(server):
    """Reboot the specified server"""
    server.send_command("sudo reboot")

Tell me, should the docstring for reboot_server be "Reboot the
specified server", or not? If not, why not?

ChrisA

[toc] | [prev] | [next] | [standalone]


#111022

FromLawrence D’Oliveiro <lawrencedo99@gmail.com>
Date2016-07-03 16:59 -0700
Message-ID<0b85cc58-0552-41b3-8729-0f037e33c183@googlegroups.com>
In reply to#111020
On Monday, July 4, 2016 at 11:39:56 AM UTC+12, Chris Angelico wrote:
> In the same way, people expect "f = deco(f)" to return *the same function,
> decorated*.

I am not changing that in anyway. The decorated function generated by the caller is returned *unchanged*.

Once again, the only docstrings I am setting are for the intermediate functions *I* generate.

Suppose we have the following:

    def try_func(func, arg) :
        "sample function to be turned into a decorator."

        def result() :
            "returns func unchanged."
            return \
                func()
        #end result

    #begin try_func
        result.__name__ = func.__name__
        result.__doc__ = func.__doc__
        return \
            result
    #end try_func

then:

    >>> f = decorator_with_args(try_func)
    >>> help(f)
    Help on function decorate_with_try_func in module decorator_try:

    decorate_with_try_func(*args, **kwargs)
        generates a decorator which applies try_func to the given arguments

    >>> @f(None)
    ... def g() :
    ...     "my docstring--should be unchanged."
    ...     return \
    ...         1
    ... #end g
    ... 
    >>> help(g)
    Help on function g in module decorator_try:

    g()
        my docstring--should be unchanged.

Do you understand now?

[toc] | [prev] | [next] | [standalone]


#111026

FromChris Angelico <rosuav@gmail.com>
Date2016-07-04 10:06 +1000
Message-ID<mailman.53.1467590768.2295.python-list@python.org>
In reply to#111022
On Mon, Jul 4, 2016 at 9:59 AM, Lawrence D’Oliveiro
<lawrencedo99@gmail.com> wrote:
>     #begin try_func
>         result.__name__ = func.__name__
>         result.__doc__ = func.__doc__
>         return \
>             result
>     #end try_func

This is the bit we're talking about. You're doing manually what
functools.wraps does for you (and it does other stuff too, to make
help() more useful), and you weren't doing this before.

Your original docstring on result() is completely ignored.

ChrisA

[toc] | [prev] | [next] | [standalone]


#111031

FromLawrence D’Oliveiro <lawrencedo99@gmail.com>
Date2016-07-03 17:23 -0700
Message-ID<d7bbef93-ccc8-4ebc-b025-98a70fe64197@googlegroups.com>
In reply to#111026
On Monday, July 4, 2016 at 12:06:21 PM UTC+12, Chris Angelico wrote:
> On Mon, Jul 4, 2016 at 9:59 AM, Lawrence D’Oliveiro wrote:
>>     #begin try_func
>>         result.__name__ = func.__name__
>>         result.__doc__ = func.__doc__
>>         return \
>>             result
>>     #end try_func
> 
> This is the bit we're talking about.

This isn’t part of my decoration mechanism, this is just an example of it in use.

[toc] | [prev] | [next] | [standalone]


#111025

FromIan Kelly <ian.g.kelly@gmail.com>
Date2016-07-03 18:01 -0600
Message-ID<mailman.52.1467590530.2295.python-list@python.org>
In reply to#111019
On Sun, Jul 3, 2016 at 5:25 PM, Lawrence D’Oliveiro
<lawrencedo99@gmail.com> wrote:
> On Monday, July 4, 2016 at 10:39:30 AM UTC+12, Ian wrote:
>> Sorry, but you're the one who doesn't seem to get it. Because it's a
>> decorator, "your" function is replacing the caller's function in the
>> caller's namespace.
>
> No it is not. The replacement happens here:
>
>         def generated_decorator(func) :
>             return \
>                 decorator(func, *args, **kwargs)
>         #end generated_decorator
>
> As you can see, the function being called, “decorator”, is supplied by the caller. The function being decorated, “func”, is supplied by the caller (along with the additional *args and **kwargs). And the returned function is returned *as-is*, so whatever docstring was assigned by the caller is *NOT* “clobbered”.

I'm talking about the docstring of the *decorator*, not func. I agree
that whatever docstring func has is entirely up to func and the
decorator, not your code. Here again is the example I posted earlier:

>>> @decorator_with_args
... def my_decorator(func, *args, **kwargs):
...     """Returns func unmodified."""
...     return func
...
>>> my_decorator.__doc__
'generates a decorator which applies my_decorator to the given arguments'

I never made any claim about the docstring of the decorated func.

[toc] | [prev] | [next] | [standalone]


#111027

FromLawrence D’Oliveiro <lawrencedo99@gmail.com>
Date2016-07-03 17:06 -0700
Message-ID<19025b2c-ba2e-4683-af50-ff208d1e9a49@googlegroups.com>
In reply to#111025
On Monday, July 4, 2016 at 12:02:23 PM UTC+12, Ian wrote:
> I'm talking about the docstring of the *decorator*, not func.

*Sigh*. Originally somebody was complaining that the lambda version didn’t allow for good (or any) docstrings. So I posted my version, and I went to all the effort of ensuring that every generated function had a good docstring. And now you are trying to knock me down for that?

What the hell is wrong with you people?!?

[toc] | [prev] | [next] | [standalone]


#111030

FromIan Kelly <ian.g.kelly@gmail.com>
Date2016-07-03 18:20 -0600
Message-ID<mailman.54.1467591655.2295.python-list@python.org>
In reply to#111027
On Sun, Jul 3, 2016 at 6:06 PM, Lawrence D’Oliveiro
<lawrencedo99@gmail.com> wrote:
> On Monday, July 4, 2016 at 12:02:23 PM UTC+12, Ian wrote:
>> I'm talking about the docstring of the *decorator*, not func.
>
> *Sigh*. Originally somebody was complaining that the lambda version didn’t allow for good (or any) docstrings. So I posted my version, and I went to all the effort of ensuring that every generated function had a good docstring. And now you are trying to knock me down for that?
>
> What the hell is wrong with you people?!?

Sorry. I don't know who that was or what they wrote, because your post
is the first mention of docstrings that I see in this thread. Maybe
you're thinking of some other thread. I don't read more than a
fraction of what gets posted to this list.

But I read your post and I saw a way to improve the code in it, so I
suggested it. Next time I won't bother.

[toc] | [prev] | [next] | [standalone]


#110943

FromBen Finney <ben+python@benfinney.id.au>
Date2016-07-02 15:32 +1000
Message-ID<mailman.16.1467437546.2295.python-list@python.org>
In reply to#110921
Ian Kelly <ian.g.kelly@gmail.com> writes:

> You should use functools.wraps instead of clobbering the decorated
> function's name and docstring:
>
>         @functools.wraps(decorator)
>         def decorate(*args, **kwargs):
>             ...

Thanks. Can you write the full implementation with that, so I can be
sure of where you expect that to go?

-- 
 \      “I knew things were changing when my Fraternity Brothers threw |
  `\           a guy out of the house for mocking me because I'm gay.” |
_o__)                                      —postsecret.com, 2010-01-19 |
Ben Finney

[toc] | [prev] | [next] | [standalone]


#110944

FromIan Kelly <ian.g.kelly@gmail.com>
Date2016-07-01 23:53 -0600
Message-ID<mailman.17.1467438837.2295.python-list@python.org>
In reply to#110921
On Fri, Jul 1, 2016 at 11:32 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
> Ian Kelly <ian.g.kelly@gmail.com> writes:
>
>> You should use functools.wraps instead of clobbering the decorated
>> function's name and docstring:
>>
>>         @functools.wraps(decorator)
>>         def decorate(*args, **kwargs):
>>             ...
>
> Thanks. Can you write the full implementation with that, so I can be
> sure of where you expect that to go?

    def decorator_with_args(decorator):
        """Given function decorator(func, *args, **kwargs), returns a
        wrapper that accepts args and kwargs separately from func.
        """
        @functools.wraps(decorator)
        def apply(*args, **kwargs):
            # inner_decorator is transient, so its name and docstring
            # are unimportant.
            def inner_decorator(func):
                return decorator(func, *args, **kwargs)
            return inner_decorator
        return apply

[toc] | [prev] | [standalone]


Page 2 of 2 — ← Prev page 1 [2]

Back to top | Article view | comp.lang.python


csiph-web