I just thought it looked better, but would you and others here like it better if the
NEWLINE requirement was kept for all decorators? There is nothing in the original
proposal that requires a single line.
I also don't know what should happen for complicated assignments, and I think this
has been the death of such variable decorator discussions in the past, so I would
still push for only bare identifiers, with or without a type hint (but maybe it will be
better received by more if the type hint is required?). I still think such a proposal is
strong enough on its own to be of value to the language.

So now the syntax would be:

@decorator
variable: Type

@decorator("spam", eggs)
variable: Type

become

variable = decorator("variable")

variable = decorator("spam", eggs)("variable")

I'm not sure that the type hint shouldn't be passed as an additional parameter,
especially if one is always required. Yes it is still different from a function
decorator in that you are not getting the variable object (which may or may not
exist) but instead its name as a string. However, decorators that expect to be
applied to variables should already be drastically different. Even if they did get
the variable object, there would be not necessarily be a __code__ or
__name__ or __dict__ attribute. They won't be callable (generally) where most
current decorators attempt to call func, wrapped in an internal closure.

I think there's a fundamental difference between your original proposal and the OPs proposal in this thread, which is that you seem to envision variable decorators as granting access to just a name (and potentially a type-hint?), whereas the OP wants the decorators extended to assignment in general.

Basically, your proposal is a subset of the OPs proposal. Any implementation of the OPs decorators would also allow us to do all the things you want to be able to do with variable decorators (as a happy side-effect), but would also offer a LOT more functionality.

My view is that variable decorators should be legal for:

1) assignment statements (so that the decorator can modify or replace the object that is getting assigned)
2) bare type-hints, since these are arguably a sort of assignment operation that still carries useful info a decorator might want to capture (you're not assigning the name and its value to globals(), but rather the name and its type to globals()['__annotations__'])

If there was huge pushback against point 2 I'd be happy to see it deferred and revisit it at a later point, but as far as I'm concerned point 1 is what actually interests me about this proposal.

Your point about complicated assignment being a problem seems solvable using the following rules:

1) Variable decorators only capture the name (or names) on the LHS of a true assignment statement (or bare type-hint). Expressions that happen to bind names (such as the walrus operator, for loop, etc.) don't have their names available for use in the decorator.
2) The proposed __decoration_call__ is given the value, name, and type-hint from the decorated statement (and potentially the code object from the RHS, as OP suggested). We could provide the names as a series of useful pre-parsed parsed AST-like objects, like:

@decorate
some_obj.foo = first, _, third, *some_obj.rest = some_dict[(lost_name := ('ba' + 'r'))] = baz = (another_lost_name := [0, 1, 2, 3, 4])

such that __decoration_call__ receives:

def __decoration_call__(self, obj, names, annotation):
    print(obj)
    # [0, 1, 2, 3, 4]
   
    print(names)
    # ParsedNames(names=[
    #     ObjectAssignment(obj={reference_to_some_obj}, name='foo'),
    #     DestructuringAssignment(names=[
    #         BasicAssignment(name='first'),
    #         BasicAssignment(name='_'),
    #         BasicAssignment(name='third'),
    #         ObjectAssignment(obj={reference_to_some_obj}, name='*rest')
    #     ]),
    #     ItemAssignment(obj={ref_to_some_dict}, name='bar'),
    #     BasicAssignment(name='baz')
    # ])
   
    print(annotation)
    # NOTSET

Notice that as per point 1 above, `lost_name` and `another_lost_name` do not get captured, because they are not part of the assignment statement. They're only sub-expressions.
Also, notice that expressions like 'ba' + 'r' are evaluated before being passed to the decorator, so that it actually receives 'bar' for the __setitem__ assignment name.

3) Augmented assignment gets expanded out to its true meaning before being passed to the decorator, so for example:

foo = 3
@decorate
foo += 2

behaves exactly the same as:

foo = 3
@decorate
foo = foo + 2


If there are still any issues given these rules that I can't think of I'd be happy for people to raise them. There are so many edge-cases in python assignment that I could very easily be forgetting about something. But I think this covers 99% of cases.


class Colors(Enum):
    @str
    RED: str
    @str
    GREEN: str
    @str
    BLUE: str

@namedtuple("x y z")
Point: NamedTuple

@os.getenv
PATH: Optional[str]


With decorated assignments these examples could instead look something like:

class Color(Enum):
    @string_values
    RED, GREEN, BLUE = auto()

Here the decorator would return a tuple like: ('RED', 'GREEN', 'BLUE'), which would be assigned instead of whatever is actually on the RHS

And for the namedtuple:
@namedtuple
Point = 'x', 'y', 'z'

The third one would look just like you suggested.



On Sat, May 29, 2021 at 3:28 AM micro codery <ucodery@gmail.com> wrote:
Ah, I think you might be missing the context of the original proposal? I do mean bare unbound identifiers - at lease as they occur in this new syntax. 

# currently works
spam = “eggs”
spam: eggs

# currently a NameError
spam

# proposed to work, currently illegal

@spam
eggs

@spam(“eggs”)
cheese

@spam
eggs: str

But none of this would change the first three examples. 
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@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/JETD7GGN2HKKL3E6A2XNRTCASAMIQQM5/
Code of Conduct: http://python.org/psf/codeofconduct/