
Bikesheddable, but I don't know why having these two be equivalent:
@decorator var @decorator var = None
..would be a problem. Having an implied default of None for var above makes sense to my brain. Do you have an example in mind where you think it would create a problem?
I don't have anything immediately in mind, no, but I think that given the semantics of those two statements are very different, it is at least worthwhile to allow the decorator to know which of them is actually happening (is `None` being assigned to the name or is it maybe just a type hint for that name?). Keep in mind that if no assignment is happening then there is no need to return anything from the decorator, since it will just be lost anyway. And I'm pretty much 100% positive that even if I can't think of a use-case off the top of my head, there will eventually be a library author (if this proposal is accepted) who will have some cool idea that will require distinguishing these two scenarios. Like for example if this were used for a CLI-building library (think something like Typer) how the assignment to `None` could signify that it is an option with a default value of `None`, whereas the bare name would signify that it is a mandatory argument. Oh, and I think I've just discovered another thing that I'm not 100% sure I like. Even putting aside that I'm not a fan of decorators on the same line as the statement they are decorating (as I mentioned in an earlier response), you've got examples of variable decorators where no assignment is happening such as: @decorator var To me this breaks the symmetry between function decorators, which always decorate a function definition (an implicit form of assignment), and the proposed variable decorators. They are also confusing in the sense that the decorator is de-facto turning an otherwise invalid python statement legal. If you remove the decorator from the above example you will presumably get a `NameError`. I imagine this would then have to be special-cased somehow in the language spec so that an undefined name is not evaluated, but only when preceded by a decorator? I don't know, it seems messy to me. Also, I just can't quite see the value in them if I'm honest, whereas the version that is applied to an assignment statement: @decorator var: bool = True And even a bare type-hint version: @decorator var: bool seem to me to be far more self-evidently useful. On Thu, May 27, 2021 at 5:45 PM Ricky Teachey <ricky@teachey.org> wrote:
On Thu, May 27, 2021 at 11:09 AM Matt del Valle <matthewgdv@gmail.com> wrote:
I'm not the OP, but the way I understand the proposal __decoration_call__ is only invoked when you actually *use an object to decorate something*. That means that a decorator factory will just invoke __call__ as normal, because it's nothing but a convenient way to generate a decorator. It is not itself a decorator, nor is it used to actually decorate anything. To illustrate this point we can separate it out across several lines:
@factory("foo") def bar(): pass
Can be rewritten as:
decorator = factory("foo")
@decorator def bar(): pass
So __decorator_call__ will only be invoked on the object that gets returned from `factory("foo")`, not on `factory`.
Correct.
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.
The OP mentioned a default implementation for __decoration_call__ of:
def __decoration_call__(self, func, by_name): if func is None: return self(by_name) return self(func)
Such that you can assume that the decorator will *always* receive the name, but may choose to discard it and not make use of it if it doesn't implement the __decoration_call__ interface and instead opts to use default implementation which falls back on __call__.
Yes but I am on the fence as to whether this default implementation (I suppose it would live on the object class?) should be considered or not. It would certainly provide a lot of functionality "out-of-the-box".
For decorated functions the name can always be pulled out of the function
object as normal even when using __call__, but to make use of the name in a decorated assignment statement the decorator would have to override __decoration_call__.
At this point I will say that I may be putting words into OPs mouth, and would be happy to be corrected if I've misunderstood.
Nah you got it.
One final point I've just thought of is that Ricky suggested that when no value is assigned to a name that the object reference be `None`. But I don't think that works, because it becomes indistinguishable from when `None` is explicitly assigned. We would need some sentinel value instead of `None` to remove ambiguity in this situation:
from somewhere import NOTSET
@decorate foo: int
def __decoration_call__(self, obj, names, annotation): print(obj is None) # False print(obj is NOTSET) # True
@decorate foo: int = None
def __decoration_call__(self, obj, names, annotation): print(obj is None) # True print(obj is NOTSET) # False
Bikesheddable, but I don't know why having these two be equivalent:
@decorator var @decorator var = None
..would be a problem. Having an implied default of None for var above makes sense to my brain. Do you have an example in mind where you think it would create a problem?
--- Ricky.
"I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler