I made a mistake. This sentence:

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.


Should read:

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
*SECOND* argument
(e.g., by_name), which contains the str value of the name the decorator was
applied to.

---
Ricky.

"I've never met a Kentucky man who wasn't either thinking about going home
or actually going home." - Happy Chandler


On Wed, May 26, 2021 at 12:43 PM Ricky Teachey <ri...@teachey.org> wrote:

> On Wed, May 26, 2021 at 7:54 AM Steven D'Aprano <st...@pearwood.info>
> wrote:
>
>> On Wed, May 26, 2021 at 01:33:07PM +0200, Stéfane Fermigier wrote:
>>
>> > Last point that I like in the decorator syntax: it's
>> >
>> > I can compose N different decorators and keep the intent obvious:
>> >
>> > @acl(READ, WRITE)
>> > @constraint(10 < _ < 100)
>> > @not_null
>> > @indexed
>> > @depends_on(whatever)
>> > @inject
>> > @...
>> > first_name: str
>>
>> Hmm, that's a good point.
>>
>> On the other hand, it would be terribly confusing if the same syntax:
>>
>>     @decorate
>>
>> had radically different meaning depending on whether it was followed by
>> a class/function or a bare name.
>>
>>
>> --
>> Steve
>>
>
> ....and previously Steve also said:
>
> On Tue, May 25, 2021 at 3:28 AM Steven D'Aprano <st...@pearwood.info>
> wrote:
>
>> On Mon, May 24, 2021 at 06:36:47PM -0700, micro codery wrote:
>>
>> > Basically this would add syntax to python that would transform
>> > @decorator("spam this") variable
>> > into
>> > variable = decorator("variable", "spam this")
>>
>> That is confusingly different from decorator syntax in other contexts....
>>
>>     @decorator("spam this")
>>     def func(): pass
>>
>>     # transformed to:
>>
>>     def func(): pass
>>     func = decorator("spam this")(func)
>>
>> ...the critical difference is that the argument "spam this" should be
>> passed to the decorator *factory*, which then returns the actual
>> decorator that gets applied to the variable. (Or function/ class in the
>> case of regulator decorator syntax.)
>
>
> Well, in actuality even if it were implemented the way you described, it
> is still going to be very different--- I might even say radically different.
>
> 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.
>
> 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.
>
> This got me to thinking: what if access to the variable name were provided
> by another means, and ONLY when the decorator syntax is employed?
>
> So then I started to write this big long email and it kind of got out of
> hand. Hopefully it isn't a disaster.
>
> FIRST LAYER: RICK'S (LIKELY HALF-BAKED) COUNTER PROPOSAL
>
> 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: ...
>
> Before I describe what I intend by this, first I will stipulated that what
> I am proposing here should only be implemented so that the behavior of all
> currently existing decorators (i.e., all callables) would remain exactly as
> it does today.
>
> So, for any existing callable with the name decorator, this:
>
> @decorator("spam this")
> def func(): ...
>
> ...continues to mean this, just as it does today:
>
> def func(): ...
> func = decorator("spam this")(func)
>
> 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.
>
> Let's explain using code examples.
>
> 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)
>
> My proposal is to make it such that:
>
> @decorator
> def func(): ...
>
> ...*can result* in this:
>
> def func(): ...
> func = decorator.__decoration_call__( func, "func")
>
> 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")
>
> I say "*can result*" because this has the following limitations:
>
>    1. occurs only when the callable object is employed as a decorator
>    2. occurs only when  __decoration_call__ exists (and if it doesn't,
>    just revert back to the usual __call__(func_object, obj))
>
> Here is an example to further illustrate the suggested behavior. It is not
> intended to demonstrate a useful example. You could write a callable object
> like this:
>
> class Decorator:
>     def __call__(self, obj):
>         print("spam")
>         return obj
>     def __decoration_call__(self , obj, by_name):
>         print("eggs")
>         print(by_name)
>         return obj
>
> decorator = Decorator()
>
> And the behavior would be like this:
>
> def func(): ...
> func = decorator(func)
> # Console output:
> # 'spam'
>
> @decorator
> def func(): ...
> # Console output:
> # 'eggs'
> # 'func'
>
> In order to preserve current behavior, the built-in <class 'function'>
> type would not grow its own new   __decoration_call__ method. So in the
> case of existing normal functions, and also in the case of existing custom
> Callable objects, the fallback would be to just use __call__ as normal.
>
> In this way, all of the proposal is optional. But as illustrated above,
> the new dunder can be implemented by any customized Callable class.
>
> I think __decoration_call__ by itself could provide a lot of interesting
> possibilities for existing class and function decoration. But my main
> motivation for this is as a different way of implementing the variable
> decorator idea proposed by Jeremiah.
>
> SECOND LAYER: VARIABLE DECORATORS
>
> With __decoration_call__ in place, we could do a lot of interesting things
> if we take the additional step of broadening existing decorator syntax such
> that you can decorate not just functions and classes but also variables
> (similar the variable decorator proposal recently proposed by Jeremiah).
> But first let's define the variable decorator syntax behavior.
>
> Using the same decorator object I have defined above, and borrowing
> Jeremiah's original example, the new syntax would transform this:
>
> @decorator variable
>
> ...into this:
>
> variable = decorator.__decoration_call__(None, "variable")
>
> EXPLANATION OF None: in the example above there is no assignment on the
> decorated line, and so there is no object to pass to the dunder at that
> point . Because of this, the first argument, which is usually the object
> being decorated, is supplied as None in the call to  __decoration_call__ .
> Only the name being assigned to the as-yet uncreated object exists.
>
> And the new syntax would transform this:
>
> @decorator variable = "spam"
>
> ...into this:
>
> variable = decorator.__decoration_call__(variable, "variable")
>
> With the behavior of this variable decoration defined as shown above, here
> are some of the interesting possibilities I was considering.
>
> One possibility is avoiding the pitfalls of name repetition in the RAD
> framework context previously mentioned by Stéfane Fermigier in Jeremiah's
> variable decorator thread (see his post for examples). It could see other
> frameworks, like web frameworks, making use if this too.
>
> (Here I will repeat some of the possibilities suggested by Jeremiah in his
> first post) The standard library and other library functions could, as
> needed, make use of __decoration_call__ as they see fit. I could see
> factories like namedtuple, make_dataclass, several typing factories, and
> Enum, implementing  __decoration_call__ so you can say things like:
>
> @namedtuple Point =  "x y z"
> @make_dataclass Point =  [("x", int), ("y", int), ("z", int)]
> @typing.TypeVar T = (str, bytes)
> @typing.NewType UserId = int
> @enum.Enum Colors =  "RED GREEN BLUE"
>
> Admittedly, these examples are not all that compelling on their own, but I
> think they read really well and they would allow you to avoid retyping a
> name (and potentially making a mistake in retyping it), so I think I really
> like it.
> ___
> SIDEBAR
>
> I also note that the above examples use a different idiom than suggested
> by Jeremiah. He has suggested these:
>
> @namedtuple("x y z") Point
> @make_dataclass([("x", int), ("y", int), ("z", int)]) Point
> @typing.TypeVar( (str, bytes) ) T
> @typing.NewType(int) UserId
> @enum.Enum("RED GREEN BLUE") Colors
>
> ...but using my proposal I do not see a way to make this idiom easily work
> with the existing factories. I am not sure which of these two idioms people
> would prefer. I can see pros and cons for both idioms. If people preferred
> the second idiom, new decorator objects could be added for it.
> ___
>
> One interesting possibility might be to modify inspect.getsource so that
> it can retrieve the RHS of the line on which it appears*:
>
> @inspect.getsource my_var = 1/2
> print(my_var)
> # Result
> # '@inspect.getsource my_var = 1/2'
>
> * NOTE: I am admittedly hand-waving this. Maybe there are reasons
> getsource would not be able to do this?
>
> Using the rhs string from this new version of getsource, it would be a
> pretty simple matter for a symbolic math library like sympy to replace the
> float value resulting from 1/2 in my_var with a symbolic math value:
>
> @sympy.sympify my_var = 1/2
> # Result is not 0.5, but:
> # 1/2
>
> ...or create a Fraction (using a modified Fraction type):
>
> @Fraction my_var = 1/2
> # Result is not 0.5, but:
> # Fraction(1, 2)
>
> ..or a Decimal (using a modified Decimal type):
>
> @Decimal my_var = 0.5
> # Result is not 0.5, but:
> # Decimal('0.5')
>
> MAYBE A DEFAULT DUNDER AFTER ALL...?
>
> One final possibility: I have said above we would not supply any  default
> __decoration_call__  method, and instead fallback on __call__. But it might
> be beneficial to supply a default __decoration_call__ with the following
> implementation:
>
> def  __decoration_call__(self, func, by_name):
>     if func is None:
>         return self(by_name)
>     return self(func)
>
> This would allow things like this, "out of the box":
>
> @typing.NewType Color = str
> @Color GREEN
>
> ...which becomes:
>
> Color = typing.NewType.__decoration_call__(str, "Color")
> GREEN = Color.__decoration_call__(None, "GREEN")
>
> In the above, Color is just a stand-in for str. If there is a default
> __decoration_call__ method as I wrote above, then:
>
> Color.__decoration_call__(None, "GREEN")
>
> ...is actually just:
>
> str.__decoration_call__(None, "GREEN")
>
> ..and the default implementation just returns:
>
> str("GREEN")
>
> ...and assigns it to the name, GREEN.
>
> Guess I'll leave it at that. Let's see how this is received.
>
> ---
> Ricky.
>
> "I've never met a Kentucky man who wasn't either thinking about going home
> or actually going home." - Happy Chandler
>
>
_______________________________________________
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/Z73G5T4VQ2HEHSHRELILXS3HUYYOM2SS/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to