On Tue, 1 Jun 2021 at 13:16, Steven D'Aprano <st...@pearwood.info> wrote:

> We can distinguish the two contexts by using different signatures. The
> signature used depends entirely on the call site, not the decorator, so
> it is easy for the interpreter to deal with.
>
> If the decorator is called on a function or class statement, a single
> argument is always passed, no exceptions:
>
>     # always calls decorate with one argument
>     @decorate
>     def func():  # or class
>         pass
>
>     # --> func = decorate(func)
>
> If called on a variable, the number of arguments depends on whether it
> is a bare name, or a value and annotation are provided. There are
> exactly four cases:
>
>     # bare name
>     @decorate
>     var
>     # --> var = decorate('var')
>
>     # name with annotation
>     @decorate
>     var: annot
>     # --> var = decorate('var', annotation=annot)
>
>     # name bound to value
>     @decorate
>     var = x
>     # --> var = decorate('var', value=x)
>
>     # name with annotation and value
>     @decorate
>     var: annot = x
>     # --> var = decorate('var', annotation=annot, value=x)
>
>
> Keyword arguments are used because one or both of the value and the
> annotation may be completely missing. The decorator can either provide
> default values or collect keyword arguments with `**kwargs`.

I've yet to be convinced that variable annotations are sufficiently
useful to be worth all of this complexity (and by "this" I mean any of
the proposals being made I'm not singling out Steven's suggestion
here).

But if we do need this, I quite like the idea of making the
distinction based on signature.

> The only slightly awkward case is the bare variable case. Most of the
> time there will be no overlap between the function/class decorators and
> the bare variable decorator, but in the rare case that we need to use a
> single function in both cases, we can easily distinguish the two cases:
>
>     def mydecorator(arg, **kwargs):
>         if isinstance(arg, str):
>             # must be decorating a variable
>             ...
>         else:
>             # decorating a function or class
>             assert kwarg == {}
>
> So it is easy to handle both uses in a single function, but I emphasise
> that this would be rare. Normally a single decorator would be used in
> the function/class case, or the variable case, but not both.

You don't need to do this. Just add another keyword argument "name":

# bare name
@decorate
var
# --> var = decorate(name='var')

# name with annotation
@decorate
var: annot
# --> var = decorate(name='var', annotation=annot)

# name bound to value
@decorate
var = x
# --> var = decorate(name='var', value=x)

# name with annotation and value
@decorate
var: annot = x
# --> var = decorate(name='var', annotation=annot, value=x)

The single positional argument is reserved for function/class
annotations, and will always be None for variable annotations.

Paul
_______________________________________________
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/NJTZRP64SBWP5SJMKAGPNAYJ7MK6IKQO/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to