On Wed, May 26, 2021 at 12:43:48PM -0400, Ricky Teachey wrote:

[...]
> These two ideas of a decorator syntax result are not the same:
> 
> RESULT A: function decorator
> # func = decorator("spam")(func)
> 
> RESULT B: variable decorator
> # name = decorator("spam")("name")
> 
> ...because func is passed as an object, but "name" a string representing
> the name of the object. Two very different things.

Ricky, it's not clear to me whether you are proposing the above RESULT A 
and RESULT B as an *alternative* to the "variable decorator" proposal, 
or if you have just misunderstood it. The current variable decorator 
proposal on the table is for this:

    @decorator(spam) name
    # --> name = decorator("name", spam)

rather than what you wrote:

    # name = decorator("spam")("name")

So I can't tell whether the difference between your version and the OPs 
is a bug or a feature :-)


> For this reason I think I would agree even more so that the differences in
> the decorator behavior would be an extremely significant point of confusion.
[...]
> Maybe employment of decorator syntax could OPTIONALLY trigger a new dunder
> method-- here I'll just call it __decoration_call__-- with the signature:
> 
> def  __decoration_call__(self, obj: Any, by_name: str) -> Any: ...

To be clear here, I think that your proposal is that this method is to 
be looked up on the *decorator*, not the thing being decorated. Is that 
correct?

In other words:

    @decorator
    class X: ...  # or a function, or something else

it is *decorator*, not X, that is checked for a `__decoration_call__` 
method.

Correct?


> My idea is to optionally allow any callable object to write a
> __decoration_call__ method that gets called in lieu of the __call__ method
> when the callable object is employed using decorator syntax. When this
> happens, the decorated named is supplied- not counting self- as the first
> argument (e.g., by_name), which contains the str value of the name the
> decorator was applied to.

In current Python, the only objects which can be decorated with the @ 
syntax are callable functions and classes. So it is ambiguous to talk 
about "any callable object" without stating whether it is the decorator 
or the thing being decorated.


> In actuality, unless I'm wrong (I might be; not an expert) current
> decorator syntax is really sugar for:
> 
> def func(): ...
> func = decorator.__call__("spam this").__call__(func)

Roughly speaking, that would correspond to

    @decorator("spam this")
    def func():
        ...


If we have a bare decorator, we have this:

    @decorator
    def func():
        ...

    # --> func = decorator.__call__(func)


> My proposal is to make it such that:
> 
> @decorator
> def func(): ...
> 
> ...*can result* in this:
> 
> def func(): ...
> func = decorator.__decoration_call__( func, "func")


Okay. Without reading the source code, does this code snippet use the 
old `__call__` protocol or the new `__decoration_call__` protocol?


    @flambé
    class Banana_Surprise:
        pass


It seems to me that this proposal means that we can't even tell which of 
the two protocols (classic decoration, or new `__decoration_call__` 
style decoration) without digging into the implementation of the 
decorator.

To be precise, the problem here as reader isn't so much the fact that I 
don't know whether the object is called using the `__call__` protocol or 
the new-style `__decorator_call__` protocol, but the fact that I can't 
tell whether the calls will involve the name being passed or not.

This is because the name is being *implicitly* passed, in a way that is 
unclear whether or not it will be passed.

I just don't know whether or not the decorator `flambé` receives the 
name or not.


> And also so that this:
> 
> @decorator("spam this")
> def func(): ...
> 
> ...*can result* in this:
> 
> def func(): ...
> func = decorator.__call__("spam this").__decoration_call__(func, "func")


What happens if the decorator factory has `__decoration_call__` and the 
object it returns only has `__call__`? I presume you get this:

    func = decorator.__decoration_call__("spam this", "func").__call__(func)

And let's not forget the other two combinations:

    func = decorator.__decoration_call__("spam this", 
"func").__decoration_call__(func, "func")
    func = decorator.__call__("spam this").__call__(func)

The last one is, of course, the current behaviour for a decorator 
factory.

The bottom line here is that is you have plain, unadorned decorator:

    @decorator

there are two possible behaviours and no obvious way to tell which one 
is used, short of digging into the implementation. But if you have a 
decorarator factory:

    @factory(*args, **kwargs)

there are now four possible behaviours. And anyone brave enough to use a 
double-barrelled factory-factory

    @factory(*args, **kwargs)(*more_args)

will be faced with eight possible combinations.


And at this point, I'm afraid I have run out of steam to respond further 
to this proposal. Sorry.


-- 
Steve
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/GZQ2RTWVNW6KC2REISB34TLRH45ZJIMK/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to