[Python-Dev] Re: __init_subclass__ and metaclasses

2021-01-16 Thread Nick Coghlan
On Tue, 12 Jan 2021, 11:55 am Ethan Furman,  wrote:

> On 1/7/21 4:56 AM, Nick Coghlan wrote:
>
>  > Both EnumMeta and ABCMeta should probably be relying on `__set_name__`
>  > for their per-member set up work these days, rather than deferring
>  > that work until after __new__ returns.
>
> And here I was thinking that `__set_name__` was for, well, setting the
> name.  ;-)
>


Yeah, __set_owner__ was also discussed as a possible name, since the method
notifies the descriptor of both the defining class and the attribute name
within that class.

We decided we preferred __set_name__ out of the two short options.



> But it does work.
>

Good to hear :)

Cheers,
Nick.



>
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/RY7FBQFJ4T64CYILI5B3BOL2KYX65X62/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Let's Fix Class Annotations -- And Maybe Annotations Generally

2021-01-16 Thread Nick Coghlan
On Wed, 13 Jan 2021, 12:35 pm Larry Hastings,  wrote:

>
> On 1/12/21 5:28 PM, Brett Cannon wrote:
>
> The other thing to keep in mind is we are talking about every module,
> class, and function getting 64 bytes ... which I bet isn't that much.
>
> Actually it's only every module and class.  Functions don't have this
> problem because they've always stored __annotations__ internally--meaning,
> peeking in their __dict__ doesn't work, and they don't support inheritance
> anyway.  So the number is even smaller than that.
>
> If we can just make __annotations__ default to an empty dict on classes
> and modules, and not worry about the memory consumption, that goes a long
> way to cleaning up the semantics.
>

Could you get the best of both worlds by making __annotations__ an
auto-populating descriptor on "type", the way it is on functions?

Continue to add a non-empty annotations dict to the class dict eagerly, but
only add the empty dict when "cls.__annotations__" is accessed.

Then your co_annotations PEP would only be changing the way the non-empty
case was handled, rather than introducing the descriptor in the first place.

Cheers,
Nick.

>
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/BMBFLLQZY4WZP5RQ32OFQYR2N3UMHTFF/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Let's Fix Class Annotations -- And Maybe Annotations Generally

2021-01-16 Thread Larry Hastings


On 1/16/21 8:41 AM, Nick Coghlan wrote:
Could you get the best of both worlds by making __annotations__ an 
auto-populating descriptor on "type", the way it is on functions?


Continue to add a non-empty annotations dict to the class dict 
eagerly, but only add the empty dict when "cls.__annotations__" is 
accessed.



I think that'll work though it's a little imprecise.  Consider the best 
practice for getting class annotations, example here from 
Lib/dataclasses.py:


   cls_annotations = cls.__dict__.get('__annotations__', {})

What happens when that current best practice code meets your proposed 
"lazy-populate the empty dict" approach?


 * If a class has annotations set, cls.__dict__['__annotations__'] will
   be set, so the code works fine.

 * If a class doesn't have annotations set, then
   cls.__dict__['__annotations__'] won't be set yet.  So people peering
   in cls.__dict__['__annotations__'] will get the right /answer/, that
   no annotations are set.  But they'll see the wrong /specifics/:
   they'll think annotations are unset, when in fact it has an empty
   dict as its value.

So the code will continue to work, even though it's arguably a little 
misguided.  If anybody distinguished between "annotations are unset" and 
"annotations are set to an empty dict", that code would fail, but I 
expect nobody ever does that.


Two notes about this idea.  First, I think most people who use this 
best-practices code above use it for modules as well as classes.  (They 
have two code paths: one for functions, the other for not-functions.)  
But everything I said above is true for both classes and modules.


Second, I think this is only sensible if, at the same time, we make it 
illegal to delete cls.__annotations__.  If we lazy-populate the empty 
dict, and a user deletes cls.__annotations__, and we don't remember some 
extra state, we'd just re-"lazy" create the empty dict the next time 
they asked for it.  Which is actually what functions do, just 
lazy-repopulate the empty annotations dict every time, and I'm not keen 
to bring those semantics to classes and modules.



Cheers,


//arry/

___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/XNJWBEYSAPMBMNCUUK7MWH2L6HNBST3Z/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: PEP: Deferred Evaluation Of Annotations Using Descriptors

2021-01-16 Thread Larry Hastings


Given your comments below, I'd summarize the semantics you want as:

   Looking up names for annotations should work exactly as it does
   today with "stock" semantics, except annotations should also see
   names that haven't been declared yet.

Thus an annotation should be able to see names set in the following 
scopes, in order of most-preferred to least-preferred:


 * names in the current scope (whether the current scope is a class
   body, function body, or global),
 * names in enclosing /function/ scopes, up to but not including the
   first enclosing /class/ scope, and
 * global scope,

whether they are declared before or after the annotation.

If the same name is defined multiple times, annotations will prefer the 
definition from the "nearest" scope, even if that definition hasn't been 
evaluated yet.  For example:


   x = int
   def foo():
    def bar(a:x): pass
    x = str

Here a would be annotated with "str".

Ambiguous conditions (referring to names that change value, referring to 
names that may be deleted) will result in undefined behavior.



Does that sound right?


Thanks for the kind words,


//arry/

On 1/15/21 12:38 PM, Guido van Rossum wrote:
On Fri, Jan 15, 2021 at 10:53 AM Larry Hastings > wrote:



Sorry it took me 3+ days to reply--I had a lot to think about
here.  But I have good things to report!


On 1/11/21 8:42 PM, Guido van Rossum wrote:

On Mon, Jan 11, 2021 at 1:20 PM Larry Hastings
mailto:la...@hastings.org>> wrote:

PEP 563 states:

For code that uses type hints, the
typing.get_type_hints(obj, globalns=None, localns=None)
function correctly evaluates expressions back from its
string form.

So, if you are passing in a localns argument that isn't None,
okay, but you're not using them "correctly" according to the
language.  Also, this usage won't be compatible with static
type checkers.

I think you're misreading PEP 563 here. The mention of
globalns=None, localns=None refers to the fact that these
parameters have defaults, not that you must pass None. Note that
the next paragraph in that PEP mentions eval(ann, globals,
locals) -- it doesn't say eval(ann, {}, {}).


I think that's misleading, then.  The passage is telling you how
to "correctly evaluate[s] expressions", and how I read it was,
it's telling me I have to supply globalns=None and localns=None
for it to work correctly--which, I had to discover on my own, were
the default values.  I don't understand why PEP 563 feels
compelled to define a function that it's not introducing, and in
fact had already shipped with Python two versions ago.


I suppose PEP 563 is ambiguous because on the one hand global symbols 
are the only things that work out of the box, on the other hand you 
can make other things work by passing the right scope (and there's 
lots of code now that does so), and on the third hand, it claims that 
get_type_hints() adds the class scope, which nobody noticed or 
implemented until this week (there's a PR, can't recall the number).


But I think all this is irrelevant given what comes below.



Later in that same section, PEP 563 points out a problem with
annotations that reference class-scoped variables, and claims
that the implementation would run into problems because methods
can't "see" the class scope. This is indeed a problem for PEP
563, but *you* can easily generate correct code, assuming the
containing class exists in the global scope (and your solution
requires that anyway). So in this case
```
class Outer:
    class Inner:
   ...
    def method(self, a: Inner, b: Outer) -> None:
    ...
```
The generated code for the `__annotations__` property could just
have a reference to `Outer.Inner` for such cases:
```
def __annotations__():
    return {"a": Outer.Inner, "b": Outer, "return": None}
```


This suggestion was a revelation for me.  Previously, a
combination of bad experiences early on when hacking on compile
and symtable, and my misunderstanding of exactly what was being
asserted in the November 2017 thread, led me to believe that all I
could support was globals.  But I've been turning this over in my
head for several days now, and I suspect I can support... just
about anything.


I can name five name resolution scenarios I might encounter.  I'll
discuss them below, in increasing order of difficulty.


*First* is references to globals / builtins. That's already
working, it's obvious how it works, and I need not elaborate further.


Yup.


*Second* is local variables in an enclosing function scope:

def outer_fn():
    class C: pass
    def inner_fn(a:C=None): pass
    return inner_fn

As you pointed out elsewhere in un-quoted text, 

[Python-Dev] Re: Let's Fix Class Annotations -- And Maybe Annotations Generally

2021-01-16 Thread Greg Ewing

On 17/01/21 12:31 pm, Larry Hastings wrote:


Consider the best 
practice for getting class annotations, example here from 
Lib/dataclasses.py:


cls_annotations = cls.__dict__.get('__annotations__', {})


Isn't that going to get broken anyway? It won't trigger the
calling of __co_annotations__.

--
Greg
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/PPDBI3J4SUJ6NCWRORWYAOKYW4IM3HMQ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Let's Fix Class Annotations -- And Maybe Annotations Generally

2021-01-16 Thread Larry Hastings


On 1/16/21 4:09 PM, Greg Ewing wrote:

On 17/01/21 12:31 pm, Larry Hastings wrote:


Consider the best practice for getting class annotations, example 
here from Lib/dataclasses.py:


    cls_annotations = cls.__dict__.get('__annotations__', {})


Isn't that going to get broken anyway? It won't trigger the
calling of __co_annotations__.



I proposed these as two separate conversations, because I wanted to 
clean up the semantics of annotations whether or not PEP 649 was 
accepted.  But, yes, if PEP 649 is accepted (in some form), this 
current-best-practice would no longer work, and the new best practice 
would likely become much more complicated.



Cheers,


//arry/

___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/3OREHZBKW2ANZG2H5WEVQC4Y2GNYHQV7/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: PEP: Deferred Evaluation Of Annotations Using Descriptors

2021-01-16 Thread Inada Naoki
This PEP doesn't cover about what happened when __co_annotation__()
failed (e.g. NameError).

Forward reference is a major reason, but not a only reason for using
string annotation. There are two other reasons:

* Avoid importing heavy module.
* Avoid circular imports.

In these cases, this pattern is used:

```
from __future__ import annotations
import typing
from dataclasses import dataclass

if typing.TYPE_CHECKING:
import other_mod  # do not want to import actually

@dataclass
class Foo:
a: other_mod.spam
b: other_mod.ham

def fun(a: other_mod.spam, b: other_mod.ham) -> None: ...
```

Of course, mypy works well with string annotation because it is static checker.
IPython shows signature well too:

```
In [3]: sample.Foo?
Init signature: sample.Foo(a: 'other_mod.spam', b: 'other_mod.ham') -> None
Docstring:  Foo(a: 'other_mod.spam', b: 'other_mod.ham')
```

PEP 563 works fine in this scenario. How PEP 649 works?

Regards,
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/YMMYKST2B4IJJOHAQIFIBAT57MKBBG56/
Code of Conduct: http://python.org/psf/codeofconduct/