Re: [Python-ideas] Python Decorator Improvement Idea

2018-06-16 Thread Eric V. Smith

On 6/16/2018 8:22 PM, Michael Selik wrote:
The idea of having a dunder to introspect the bound variable name has 
been discussed before. You can find the past discussions in the mailing 
list archive. If I recall correctly, there were very few use cases 
beyond namedtuple. With dataclasses available in 3.7, there may be even 
less interest than before.


One such thread is here:
https://mail.python.org/pipermail/python-ideas/2011-March/009250.html

Eric




On Sat, Jun 16, 2018, 9:04 AM Brian Allen Vanderburg II via Python-ideas 
mailto:python-ideas@python.org>> wrote:



On 06/16/2018 01:22 AM, Steven D'Aprano wrote:
 > Some of the information would be available in all
 >> contexts, while other information may only be available in certain
 >> contexts.The parameter's value cannot be explicitly specified,
defaults
 >> to Null except when called as a decorator, and can only be specified
 >> once in the function's parameter list.
 > Do you mean None?

Yes, I meant None instead of Null.

 > [...]
 >> Rules:
 >>
 >> 1. It is not possible for the parameter's value to be directly
 >> specified. You can't call fn(info=...)
 > That sounds like a recipe for confusion to me. How would you explain
 > this to a beginner?
 >
 > Aside from the confusion that something that looks like a parameter
 > isn't an actual parameter, but something magical, it is also very
 > limiting. It makes it more difficult to use the decorator, since
now it
 > only works using @ syntax.

That was just an initial idea.  However there would be no reason
that the
parameter could not be passed directly.  Actually if creating one
decorator
that wraps another decorator, being able to pass the parameter on could
be needed.

Also, the decorator would still work in normal syntax, only with that
parameter
set to None

 >> Information that could be contained in the parameters for all
contexts:
 >>
 >> Variable name
 >> Module object declared in
 >> Module globals (useful for @export/@public style decorators)
 >> Etc
 > The variable name is just the name of the function or class, the
first
 > parameter received by the decorator. You can get it with
func.__name__.

This works with functions and classes but not other values that may
not have __name__.
 >> Using the decorator in a class context, pass the class object.
 > The decorator already receives the class object as the first
parameter.
 > Why pass it again?
 >
 >
 >> While the class object hasn't been fully created yet,
 > What makes you say that?

What I mean is used inside the body of a class to decorate a class
member:

     class MyClass(object):
         @decorator
         def method(self):
             pass

Using the explicit is better than implicit:

     class MyClass(object):
         @decorator(MyClass, ...)
         def method(self):
             pass

However right now that does not work as MyClass does not exist when the
decorator is called.  I'm not sure how Python works on this under
the hood
as it's been a long time since I've looked through the source code.  If
Python
gather's everything under MyClass first before it even begins to
create the
MyClass object, then it may not be possible, but if Python has already
created
a class object, and just not yet assigned it to the MyClass name in the
module,
then perhaps there could be some way to pass that class object to the
decorator.

I have seen some examples that decorates the class and members to
achieve
something similar

     @outerdecorator
     class MyClass:
         @decorator
         def method(self):
             pass

 >
 >>     # This will call the decorator passing in 200 as the object, as
 >>     # well as info.name  as the variable being
assigned.
 >>     @expose
 >>     SCRIPT_CONSTANT = 200
 > That would require a change to syntax, and would have to be a
separate
 > discussion.
 >
 > If there were a way to get the left hand side of assignments as a
 > parameter, that feature would be *far* to useful to waste on just
 > decorators. For instance, we could finally do something about:
 >
 > name = namedtuple("name", fields)

Agreed it would be a change in syntax.  Using the decorator syntax i've
mentioned
the name being assigned would be passed to that extra info parameter.
Python
would treat anything in the form of:

     @decorator
     NAME = (expression)

as a decorator as well:

     _tmp = (expression)
     NAME = decorator(_tmp)

Right now, there's litlte use as it is just as easy to say directly

     NAME = decorator(expression)

With this idea, it could 

Re: [Python-ideas] Python Decorator Improvement Idea

2018-06-16 Thread Michael Selik
The idea of having a dunder to introspect the bound variable name has been
discussed before. You can find the past discussions in the mailing list
archive. If I recall correctly, there were very few use cases beyond
namedtuple. With dataclasses available in 3.7, there may be even less
interest than before.


On Sat, Jun 16, 2018, 9:04 AM Brian Allen Vanderburg II via Python-ideas <
python-ideas@python.org> wrote:

>
> On 06/16/2018 01:22 AM, Steven D'Aprano wrote:
> > Some of the information would be available in all
> >> contexts, while other information may only be available in certain
> >> contexts.The parameter's value cannot be explicitly specified, defaults
> >> to Null except when called as a decorator, and can only be specified
> >> once in the function's parameter list.
> > Do you mean None?
>
> Yes, I meant None instead of Null.
>
> > [...]
> >> Rules:
> >>
> >> 1. It is not possible for the parameter's value to be directly
> >> specified. You can't call fn(info=...)
> > That sounds like a recipe for confusion to me. How would you explain
> > this to a beginner?
> >
> > Aside from the confusion that something that looks like a parameter
> > isn't an actual parameter, but something magical, it is also very
> > limiting. It makes it more difficult to use the decorator, since now it
> > only works using @ syntax.
>
> That was just an initial idea.  However there would be no reason that the
> parameter could not be passed directly.  Actually if creating one decorator
> that wraps another decorator, being able to pass the parameter on could
> be needed.
>
> Also, the decorator would still work in normal syntax, only with that
> parameter
> set to None
>
> >> Information that could be contained in the parameters for all contexts:
> >>
> >> Variable name
> >> Module object declared in
> >> Module globals (useful for @export/@public style decorators)
> >> Etc
> > The variable name is just the name of the function or class, the first
> > parameter received by the decorator. You can get it with func.__name__.
>
> This works with functions and classes but not other values that may
> not have __name__.
> >> Using the decorator in a class context, pass the class object.
> > The decorator already receives the class object as the first parameter.
> > Why pass it again?
> >
> >
> >> While the class object hasn't been fully created yet,
> > What makes you say that?
>
> What I mean is used inside the body of a class to decorate a class member:
>
> class MyClass(object):
> @decorator
> def method(self):
> pass
>
> Using the explicit is better than implicit:
>
> class MyClass(object):
> @decorator(MyClass, ...)
> def method(self):
> pass
>
> However right now that does not work as MyClass does not exist when the
> decorator is called.  I'm not sure how Python works on this under the hood
> as it's been a long time since I've looked through the source code.  If
> Python
> gather's everything under MyClass first before it even begins to create the
> MyClass object, then it may not be possible, but if Python has already
> created
> a class object, and just not yet assigned it to the MyClass name in the
> module,
> then perhaps there could be some way to pass that class object to the
> decorator.
>
> I have seen some examples that decorates the class and members to achieve
> something similar
>
> @outerdecorator
> class MyClass:
> @decorator
> def method(self):
> pass
>
> >
> >> # This will call the decorator passing in 200 as the object, as
> >> # well as info.name as the variable being assigned.
> >> @expose
> >> SCRIPT_CONSTANT = 200
> > That would require a change to syntax, and would have to be a separate
> > discussion.
> >
> > If there were a way to get the left hand side of assignments as a
> > parameter, that feature would be *far* to useful to waste on just
> > decorators. For instance, we could finally do something about:
> >
> > name = namedtuple("name", fields)
>
> Agreed it would be a change in syntax.  Using the decorator syntax i've
> mentioned
> the name being assigned would be passed to that extra info parameter.
> Python
> would treat anything in the form of:
>
> @decorator
> NAME = (expression)
>
> as a decorator as well:
>
> _tmp = (expression)
> NAME = decorator(_tmp)
>
> Right now, there's litlte use as it is just as easy to say directly
>
> NAME = decorator(expression)
>
> With this idea, it could be possible to do something like this:
>
> def NamedTuple(obj @info):
> return namedtuple(info.name, obj)
>
> @NamedTuple
> Point3 = ["x", "y", "z"]
> >> The two potential benefits I see from this are:
> >>
> >> 1. The runtime can pass certain information to the decorator, some
> >> information in all contexts, and some information in specific contexts
> >> such as when decorating a class member, decorating a function defined
> >> within another 

Re: [Python-ideas] Python Decorator Improvement Idea

2018-06-16 Thread Brian Allen Vanderburg II via Python-ideas

On 06/16/2018 01:22 AM, Steven D'Aprano wrote:
> Some of the information would be available in all
>> contexts, while other information may only be available in certain
>> contexts.The parameter's value cannot be explicitly specified, defaults
>> to Null except when called as a decorator, and can only be specified
>> once in the function's parameter list.
> Do you mean None?

Yes, I meant None instead of Null.

> [...]
>> Rules:
>>
>> 1. It is not possible for the parameter's value to be directly
>> specified. You can't call fn(info=...)
> That sounds like a recipe for confusion to me. How would you explain 
> this to a beginner?
>
> Aside from the confusion that something that looks like a parameter 
> isn't an actual parameter, but something magical, it is also very 
> limiting. It makes it more difficult to use the decorator, since now it 
> only works using @ syntax.

That was just an initial idea.  However there would be no reason that the
parameter could not be passed directly.  Actually if creating one decorator
that wraps another decorator, being able to pass the parameter on could
be needed.

Also, the decorator would still work in normal syntax, only with that
parameter
set to None

>> Information that could be contained in the parameters for all contexts:
>>
>> Variable name
>> Module object declared in
>> Module globals (useful for @export/@public style decorators)
>> Etc
> The variable name is just the name of the function or class, the first 
> parameter received by the decorator. You can get it with func.__name__.

This works with functions and classes but not other values that may
not have __name__.
>> Using the decorator in a class context, pass the class object.
> The decorator already receives the class object as the first parameter. 
> Why pass it again?
>
>
>> While the class object hasn't been fully created yet,
> What makes you say that?

What I mean is used inside the body of a class to decorate a class member:

    class MyClass(object):
        @decorator
        def method(self):
            pass

Using the explicit is better than implicit:

    class MyClass(object):
        @decorator(MyClass, ...)
        def method(self):
            pass

However right now that does not work as MyClass does not exist when the
decorator is called.  I'm not sure how Python works on this under the hood
as it's been a long time since I've looked through the source code.  If
Python
gather's everything under MyClass first before it even begins to create the
MyClass object, then it may not be possible, but if Python has already
created
a class object, and just not yet assigned it to the MyClass name in the
module,
then perhaps there could be some way to pass that class object to the
decorator.

I have seen some examples that decorates the class and members to achieve
something similar

    @outerdecorator
    class MyClass:
        @decorator
        def method(self):
            pass

>
>>     # This will call the decorator passing in 200 as the object, as 
>> # well as info.name as the variable being assigned.
>> @expose
>> SCRIPT_CONSTANT = 200
> That would require a change to syntax, and would have to be a separate 
> discussion.
>
> If there were a way to get the left hand side of assignments as a 
> parameter, that feature would be *far* to useful to waste on just 
> decorators. For instance, we could finally do something about:
>
> name = namedtuple("name", fields)

Agreed it would be a change in syntax.  Using the decorator syntax i've
mentioned
the name being assigned would be passed to that extra info parameter. 
Python
would treat anything in the form of:

    @decorator
    NAME = (expression)

as a decorator as well:

    _tmp = (expression)
    NAME = decorator(_tmp)

Right now, there's litlte use as it is just as easy to say directly

    NAME = decorator(expression)

With this idea, it could be possible to do something like this:

    def NamedTuple(obj @info):
        return namedtuple(info.name, obj)

    @NamedTuple
    Point3 = ["x", "y", "z"]
>> The two potential benefits I see from this are:
>>
>> 1. The runtime can pass certain information to the decorator, some
>> information in all contexts, and some information in specific contexts
>> such as when decorating a class member, decorating a function defined
>> within another function, etc
>>
>> 2. It would be possible to decorate values directly, as the runtime can
>> pass relevant information such as the variables name
> No, that would require a second, independent change.
>
> We could, if desired, allow decorator syntax like this:
>
> @decorate
> value = 1
>
> but it seems pretty pointless since that's the same as:
>
> value = decorator(1)
>
> The reason we have @decorator syntax is not to be a second way to call 
> functions, using two lines instead of a single expression, but to avoid 
> having to repeat the name of the function three times:
>
> # Repeat the function name three times:
> def function():
>

Re: [Python-ideas] Python Decorator Improvement Idea

2018-06-15 Thread Steven D'Aprano
On Fri, Jun 15, 2018 at 11:54:42PM -0400, Brian Allen Vanderburg II via 
Python-ideas wrote:

> An idea I had is that it could be possible for a decorator function to
> declare a parameter which, when the function is called as a decorator,
> the runtime can fill in various information in the parameters for the
> decorator to use.

We can already do this, by writing a decorator factory:

@decorator(any parameters you care to pass)
def spam():
...

"Explicit is better than implicit" -- it is better to explicitly pass 
the parameters you want, than to hope that "the runtime" (do you mean 
the interpreter?) will guess which parameters you need.


> Some of the information would be available in all
> contexts, while other information may only be available in certain
> contexts.The parameter's value cannot be explicitly specified, defaults
> to Null except when called as a decorator, and can only be specified
> once in the function's parameter list.

Do you mean None?

Why do you think it is a good idea to have the same function, the 
decorator, behave differently when called using decorator syntax and 
standard function call syntax? To me, that sounds like a terrible idea. 
What advantage do you see?



[...]
> Rules:
> 
> 1. It is not possible for the parameter's value to be directly
> specified. You can't call fn(info=...)

That sounds like a recipe for confusion to me. How would you explain 
this to a beginner?

Aside from the confusion that something that looks like a parameter 
isn't an actual parameter, but something magical, it is also very 
limiting. It makes it more difficult to use the decorator, since now it 
only works using @ syntax.


> 2. The parameters value is Null except in the cases where it is invoked
> (the callable called a a decorator).  If used in a partial, the
> decorator parameter would be Null. etc.

You keep saying Null. What's Null?


> Information that could be contained in the parameters for all contexts:
> 
> Variable name
> Module object declared in
> Module globals (useful for @export/@public style decorators)
> Etc

The variable name is just the name of the function or class, the first 
parameter received by the decorator. You can get it with func.__name__.

The module globals is already available in globals(). You can either 
pass it directly as an argument to the decorator, or the decorator can 
call it itself. (Assuming the decorator is used in the same module it is 
defined in.)

If the decorator is in the same module as the globals you want to 
access, the decorator can just call globals(). Or use the global 
keyword.

If the decorator is contained in another module, the caller can pass the 
global namespace as an argument to the decorator:

@decorate(globals())
def func(): ...

Not the neatest solution in the world, but it works now.


> Using the decorator in a class context, pass the class object.

The decorator already receives the class object as the first parameter. 
Why pass it again?


> While the class object hasn't been fully created yet,

What makes you say that?



> this could allow
> accessing attributes of the class (like a registry or such)
> 
>   def decorator(fn, @info):
>       if hasattr(info, "class_obj"):
>           registry = info.class_obj.__dict__.setdefault("_registry", [])
> 
>       registry.append(fn)
>   return fn

Writing "hasattr(info, whatever)" is an anti-pattern.

By the way, the public interface for accessing objects' __dict__ is to 
call the vars() function:

vars(info.class_obj).set_default(...)


> This could also make it possible to use decorators on assignments.

We already can:

result = decorator(obj)

is equivalent to:

@decorator
def obj(): ...

or 

@decorator
class obj: ...


except that we can use the decorator on anything we like, not just a 
function or class.


[...]
>     # This will call the decorator passing in 200 as the object, as 
> # well as info.name as the variable being assigned.
> @expose
> SCRIPT_CONSTANT = 200

That would require a change to syntax, and would have to be a separate 
discussion.

If there were a way to get the left hand side of assignments as a 
parameter, that feature would be *far* to useful to waste on just 
decorators. For instance, we could finally do something about:

name = namedtuple("name", fields)


> The two potential benefits I see from this are:
> 
> 1. The runtime can pass certain information to the decorator, some
> information in all contexts, and some information in specific contexts
> such as when decorating a class member, decorating a function defined
> within another function, etc
> 
> 2. It would be possible to decorate values directly, as the runtime can
> pass relevant information such as the variables name

No, that would require a second, independent change.

We could, if desired, allow decorator syntax like this:

@decorate
value = 1

but it seems pretty pointless since that's the same as:

value = decorator(1)

The