On Wed, May 26, 2021 at 12:13 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Wed, May 26, 2021 at 08:58:51AM +0200, Stéfane Fermigier wrote:

> For the use cases I have in mind, some kind of context information would
> need to be passed too, like:
>
> class Customer:
>
>     @permissions({"read": USER, "write": MANAGER})
>     first_name: str
>
> the function primary_key should be called with the class being constructed
> for it to make sense.

What function primary_key? Never mind, it probably doesn't matter.

Sorry, I re-wrote my example after a first draft. In this example "primary_key" should be "permissions".

I think that's a minor win in that you don't have to repeat the variable
name.

Repetition is not the main issue, in my own experience. The main issue is the risk of making a typo in the variable's name when passing it as a string (it happens to me all the time, specially when copy-pasting code and forgetting to change the variable's name in every place).
 
> 1) Set metadata directly on some magical attribute:
>
> class Customer:
>     __permissions__ = {
>         'first_name': { READ: ..., WRITE: ... }
>     }
>
>     first_name: str
>
> cons: risk of mispelling or forgetting some attributes; lack of locality;
> how do we deal with inheritance ?

The inheritance question applies equally to the decorator version.

What I meant is that if you set up the metadata registry by yourself, that won't be enough to deal with inheritance.

If the decorator takes care of it, it can probably be made smart enough to massage the provided metadata into the information needed at runtime.

But I agree that's mostly irrelevant for the discussion at hand.
 
> class Customer:
>     first_name: str
>
>     @acl({"read": USER, "write": MANAGER})
>     def meta_first_name(self): pass
>
> I.e. use a method decorator on a dummy function named similarly to the
> variable (e.g. by prepending some magical prefix).

Good lord. All that trouble to avoid typing the name of the variable as
a function argument, and then you still end up typing it in the dummy
function name :-(

     first_name = acl('first_name', {"read": USER, "write": MANAGER})

is *much* clearer and simpler than any of the hacks you have shown,
especially the one with the dummy method. Seriously, you end up typing
an extra 21 characters plus newlines and indents to avoid typing
'first_name' as a function argument. That's just sad.

Well that was just a straw man just to convey what I'd like to achieve, i.e. a variable decorator should be, mostly equivalent to this semantically. I've never implemented this idea.

Thank you for your examples. They suggest to me that:
[...]
 
- Decorators are a hammer, and some people think that every problem
  is a nail.

Actually there is a bit more to it. If we go back to the name, "decorators" are supposed to "decorate" things (i.e. add metadata). That's what they do, and nothing more, in Java, for instance. (I know that in Python they do more.) Now I want to add metadata to some variables, that's quite logical that I would want to use some kind of "decorator" for that.

Another thing is that there are decorators for classes, methods, functions. My humble opinion is that they would be also useful for class variables (and probably for other variables too).

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

(Yes, it can become a bit heavy after some point, but imagine that all these metadata are mandated by the business domain, what would be the alternative syntaxes or approaches ?). 

- There is good case for having a feature that gives the right-hand
  side of assignment statements access to the assignment targets as
  strings.

Yep.
 
- But I still don't think that decorator syntax is the right solution.

Not usually fond of adding new stuff to the language, but as I wrote, this is something that I was ready to argue about 8 years ago, I never did because I was in a hurry delivering the features needed for my customers, but I'm still fond of the idea of moving in this direction.

  S.

--
Stefane Fermigier - http://fermigier.com/ - http://twitter.com/sfermigier - http://linkedin.com/in/sfermigier
Founder & CEO, Abilian - Enterprise Social Software - http://www.abilian.com/
Chairman, National Council for Free & Open Source Software (CNLL) - http://cnll.fr/
Founder & Organiser, PyParis & PyData Paris - http://pyparis.org/http://pydata.fr/