[Python-Dev] Re: Heap types (PyType_FromSpec) must fully implement the GC protocol

2021-01-11 Thread Simon Cross
Hi Victor,

Thank you for looking into these issues. They are very important to HPy too!

HPy currently only supports head types for similar reasons to why they
are important to sub-interpreters -- their lifecycle can be managed by
the Python interpreter and they are not tied to the memory and life
cycle of the dynamic library containing the C extension. E.g. with
heap types the interpreter can control when a type is created and
destroyed and when it can be accessed.

We've run into some minor issues with the limitations in PyType_Slot
(https://docs.python.org/3/c-api/type.html#c.PyType_Slot.PyType_Slot.slot)
but we are working around them for the moment.

It would be useful to have some sense of where PyType_FromSpec is
headed -- e.g. is it a goal to have it support all of the features of
static types in the future -- so that we can perhaps help suggest /
implement small changes that head in the right direction and also
ensure that HPy is aligned with the immediate future of the C API.

Yours sincerely,
Simon Cross
___
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/GXZI7T2KGAU3BNKNW6E4CKDTECLZAUGX/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: PEP 642 v3: Explicit patterns for structured pattern matching

2021-01-11 Thread Nick Coghlan
On Sun, 10 Jan 2021, 7:37 pm Paul Sokolovsky,  wrote:

> And I patiently continue this thread, hoping that people whose argument
> would be along the lines of "I teach Python, and I don't want to teach
> my students 2 ways of doing the same thing, and which way use when. Why,
> if PEP634 offers just one way?"
>

They don't do the same thing, though. One does traditional duck typing
(checking for the presence of a specified set of attributes on an object),
while the other matches a sequence of attributes specified by the class.

PEP 634 just conflates the two tasks into a single call-like syntax that
may or may not bare any resemblance to the type's constructor signature.

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/2XYLWGHUSDADOUWDDHPLL2NNMJZM4VO2/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: PEP 642 v3: Explicit patterns for structured pattern matching

2021-01-11 Thread Paul Sokolovsky
Hello,

On Tue, 12 Jan 2021 00:11:33 +1000
Nick Coghlan  wrote:

> On Sun, 10 Jan 2021, 7:37 pm Paul Sokolovsky, 
> wrote:
> 
> > And I patiently continue this thread, hoping that people whose
> > argument would be along the lines of "I teach Python, and I don't
> > want to teach my students 2 ways of doing the same thing, and which
> > way use when. Why, if PEP634 offers just one way?"
> >  
> 
> They don't do the same thing, though. One does traditional duck typing
> (checking for the presence of a specified set of attributes on an
> object), while the other matches a sequence of attributes specified
> by the class.

Yes, as I mentioned, I grokked the "construction" you made in your PEP,
and under given premises, it's a neat construction. But I don't think
that premises under which you did that are "right".

And from point of view of an average Python programmer, they both apply
to objects, so from that PoV, they "do the same thing".

> PEP 634 just conflates the two tasks into a single call-like syntax
> that may or may not bare any resemblance to the type's constructor
> signature.

But pattern matching concept in the first place conflates "instance-of"
checks and usual one-by-one comparisons of contained values with some
references, and extraction of other contained values.

That's how we treated the matter for decades - we knew that on the
other side, the esoteric functional languages use "pattern matching".
And we smiled at that, and told ourselves that our imperative languages
are more expressive - by combining our individual "isinstance" and
scalar comparisons we can easily achieve the same effect as pattern
matching, but also tons of other effects. Fast forward, and we have
slightly adjusted definition of "expressivity" - it's not "when you can
do anything with low-level means, combining them verbosely", it's "when
you can do practically useful things concisely/without extra verbosity".

In that regard, PEP642:

1. Steals some generality/expressivity from PEP634, limiting "Cls(a, b)"
style patterns to only classes which define __match_args__.
2. Hastily adds new pattern matching syntax, "obj{.a, .b}". But PEP634
forecasts that beyond patterns which it describes, it's easy to imagine
that over time, more of useful patterns can be added (up to
user-defined patterns). It just says that it doesn't want to haste with
trying to defining those right away, instead tries to lay the
grounds for the pattern matching per se.

> Cheers,
> Nick.

-- 
Best regards,
 Paul  mailto:pmis...@gmail.com
___
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/CW2MFXT5CCOVY7PLJ3XUOAGLGIFG2H3V/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: why include cpython/*.h, Need this macro ? # error "this header file must not be included directly"

2021-01-11 Thread Victor Stinner
On Sun, Jan 10, 2021 at 9:27 PM Serhiy Storchaka  wrote:
> Because it will not produce compile-time error when you include that
> header files directly.
>
> The division of these declarations on few files is a deep implementation
> detail. Header file "cpython/fileobject.h" is not a part of API and we
> do not want users to use them directly and then fail because in next
> feature (or even bugfix) release some declarations have been moved to
> other files.

Yeah, the cpython/ subdirectory is the API which is excluded from the
limited C API (Py_LIMITED_API macro). But for end users, #include
"Python.h" should be enough.

#ifndef Py_CPYTHON_FILEOBJECT_H
#  error "this header file must not be included directly"
#endif

This is to prevent users to include the API by mistake. Only the
parent header file must be used, like #include "fileobject.h".

Victor
-- 
Night gathers, and now my watch begins. It shall not end until my death.
___
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/5OYSITFK3ZXI5WJO6OW7YNZJCUHPJFFW/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Heap types (PyType_FromSpec) must fully implement the GC protocol

2021-01-11 Thread Victor Stinner
Hi,

There are multiple PEPs covering heap types. The latest one refers to
other PEPs: PEP 630 "Isolating Extension Modules" by Petr Viktorin.
https://www.python.org/dev/peps/pep-0630/#motivation

The use case is to embed multiple Python instances (interpreters) in
the same application process, or to embed Python with multiple calls
to Py_Initialize/Py_Finalize (sequentially, not in parallel). Static
types are causing different issues for these use cases.

Also, it's not possible to destroy static types at Python exit, which
goes against the on-going effort to destroy all Python objects at exit
(bpo-1635741).

Victor
___
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/XTOLFGQIYXPZXRD6BL4XO2XB53VDBDWC/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings



Howdy howdy.  While working on my PEP I stumbled over a lot of behavior by
annotations that I found inconsistent and inconvenient.  I think there are
several problems here that needs fixing.  This discussion will probably
evolve into a PEP, and I'll be happy to steer that process.  But I'm less
certain about what the right thing to do is.  (Although I do know what I'd
prefer!)  So let's talk about it!

Annotations are represented in Python as a dictionary.  They can be present
on functions, classes, and modules as an attribute called "__annotations__".

We start with: how do you get the annotations from one of these objects?
Surely it's as easy as this line from Lib/inspect.py shows us:

    return func.__annotations__

And yes, that's best practice for getting an annotation from a function 
object.

But consider this line from Lib/functools.py:

    ann = getattr(cls, '__annotations__', {})

Huh.  Why doesn't it simply look at cls.__annotations__?  It's because the
language declares that __annotations__ on a class or module is optional.
Since cls.__annotations__ may not be defined, evaluating that might throw an
exception.  Three-argument getattr() is much safer, and I assert it's best
practice for getting the annotations from a module.

But consider this line from Lib/dataclasses.py:

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

And a very similar line from Lib/typing.py:

    ann = base.__dict__.get('__annotations__', {})

Huh!  Why is this code skipping the attribute entirely, and examining
cls.__dict__?  It's because the getattr() approach has a subtle bug when
dealing with classes.  Consider this example:

    class A:
    ax:int=3
    class B(A):
    pass
    print(getattr(B, '__annotations__', {}))

That's right, B *inherits* A.__annotations__!  So this prints {'ax': int}.

This *can't* the intended behavior of __annotations__ on classes. It's only
supposed to contain annotations for the fields of B itself, not those of one
of its randomly-selected base classes.  But that's how it behaves today--and
people have had to work around this behavior for years.  Examining the class
dict is, sadly, best practice for getting __annotations__ from a class.

So, already: three different objects can have __annotations__, and there are
three different best practices for getting their __annotations__.

Let's zoom out for a moment.  Here's the list of predefined data fields
you can find on classes:

    __annotations__
    __bases__
    __class__
    __dict__
    __doc__
    __module__
    __mro__
    __name__
    __qualname__

All of these describe metadata about the class.  In every case *except one*,
the field is mandatory, which also means it's never inherited. And in every
case *except one*, you cannot delete the field.  (Though you *are* allowed
to overwrite some of them.)

You guessed it: __annotations__ is the exception.  It's optional, and
you're allowed to delete it.  And these exceptions are causing problems.
It seems to me that, if the only way to correctly use a language-defined
attribute of classes is by rooting around in its __dict__, the design is
a misfire.

(Much of the above also applies to modules, too.  The big difference: since
modules lack inheritance, you don't need to look in their __dict__.)

Now consider what happens if my "delayed annotations of annotations using
descriptors" PEP is accepted.  If that happens, pulling __annotations__
out of the class dict won't work if they haven't been generated yet.  So
today's "best practice" becomes tomorrow's "this code doesn't work".
To correctly examine class annotations, code would have to do something
like this, which should work correctly in any Python 3.x version:

    if (getattr(cls, '__co_annotations__', None)
    or ('__annotations__' in cls.__dict__)):
    ann = cls.__annotations__
    else:
    ann = {}

This is getting ridiculous.


Let's move on to a related topic.  For each of the objects that can
have annotations, what happens if o.__annotations__ is set, and you
"del o.__annotations__", then you access o.__annotations__?  It depends on
what the object is, because each of them behaves differently.

You already know what happens with classes: if any of the base classes has
__annotations__ set, you'll get the first one you find in the MRO.  If none
of the bases have __annotations__ set you'll get an AttributeError.

For a module, if you delete it then try to access it, you'll always get
an AttributeError.

For a function, if you delete it then try to get it, the function will
create a new empty dict, store it as its new annotations dict, and return
that.  Why does it do that?  I'm not sure.  The relevent PEP (3107) doesn't
specify this behavior.

So, annotations can be set on three different object types, and each of
those three have a different behavior when you delete the annotations
then try to get them again.



As a final topic: what are the permitted types for __annotations__?
If you say 

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

2021-01-11 Thread Larry Hastings


I've written a new PEP.  Please find it below.  Happy reading!


//arry/

--

PEP: 
Title: Deferred Evaluation Of Annotations Using Descriptors
Version: $Revision$
Last-Modified: $Date$
Author: Larry Hastings 
Discussions-To: Python-Dev 
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 11-Jan-2021


Abstract


As of Python 3.9, Python supports two different behaviors
for annotations:

* original Python semantics, in which annotations are evaluated
  at the time they are bound, and
* PEP 563 semantics, currently enabled per-module by
  ``from __future__ import annotations``, in which annotations
  are converted back into strings and must be parsed by ``eval()``
  to be used.

Original Python semantics created a circular references problem
for static typing analysis.  PEP 563 solved that problem, but
its novel semantics introduced new problems.

This PEP proposes a third way that embodies the best of both
previous approaches.  It solves the same circular reference
problems solved by PEP 563, while preserving Python's original
straightforward runtime semantics for annotations.

In this new approach, the code to generate the annotations
dict is written to its own callable, and ``__annotations__``
is a "data descriptor" which calls the callable once and
preserves the result.

If accepted, these new semantics for annotations would initially
be gated behind ``from __future__ import co_annotations``. However,
these semantics would eventually be promoted to be the default behavior.
Thus this PEP would *supercede* PEP 563, and PEP 563's behavior would
be deprecated and eventually removed.

Overview


.. note:: The code presented in this section is highly simplified
   for clarity.  The intention is to communicate the high-level
   concepts involved without getting lost in with the details.
   The actual details are often quite different.  See the
   Implementation_ section later in this PEP for a much more
   accurate description of how this PEP works.

Consider this example code::

    def foo(x: int = 3, y: MyType = None) -> float:
    ...
    class MyType:
    ...
    foo_y_type = foo.__annotations__['y']

As we see here, annotations are available at runtime through an
``__annotations__`` attribute on functions, classes, and modules.
When annotations are specified on one of these objects,
``__annotations__`` is a dictionary mapping the names of the
fields to the value specified as that field's annotation.

The default behavior in Python 3.9 is to evaluate the expressions
for the annotations, and build the annotations dict, at the time
the function, class, or module is bound.  At runtime the above
code actually works something like this::

    annotations = {'x': int, 'y': MyType, 'return': float}
    def foo(x = 3, y = "abc"):
    ...
    foo.__annotations__ = annotations
    class MyType:
    ...
    foo_y_type = foo.__annotations__['y']

The crucial detail here is that the values ``int``, ``MyType``,
and ``float`` are looked up at the time the function object is
bound, and these values are stored in the annotations dict.
But this code doesn't run—it throws a ``NameError`` on the first
line, because ``MyType`` hasn't been defined yet.

PEP 563's solution is to decompile the expressions back
into strings, and store those *strings* in the annotations dict.
The equivalent runtime code would look something like this::

    annotations = {'x': 'int', 'y': 'MyType', 'return': 'float'}
    def foo(x = 3, y = "abc"):
    ...
    foo.__annotations__ = annotations
    class MyType:
    ...
    foo_y_type = foo.__annotations__['y']

This code now runs successfully.  However, ``foo_y_type``
is no longer a reference to ``MyType``, it is the *string*
``'MyType'``.  The code would have to be further modified to
call ``eval()`` or ``typing.get_type_hints()`` to convert
the string into a useful reference to the actual ``MyType``
object.

This PEP proposes a third approach, delaying the evaluation of
the annotations by computing them in their own function.  If
this PEP was active, the generated code would work something
like this::

    class function:
    @property
    # __annotations__ on a function object is already a
    # "data descriptor", we're just changing what it does
    def __annotations__(self):
    return self.__co_annotations__()

    # ...

    def foo_annotations_fn():
    return {'x': int, 'y': MyType, 'return': float}
    def foo(x = 3, y = "abc"):
    ...
    foo.__co_annotations__ = foo_annotations_fn
    class MyType:
   ...
    foo_y_type = foo.__annotations__['y']

The important change is that the code constructing the
annotations dict now lives in a function—here, called
`` foo_annotations__fn()``.  But this function isn't called
until we ask for the value of ``foo.__annotations__``,
and we don't do that until *after* the definition of ``MyType``.
So this code also runs successfully, and ``foo_y_

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

2021-01-11 Thread Paul Bryan via Python-Dev
My code pretty much does what you suggest at the end of your message:

On Mon, 2021-01-11 at 09:22 -0800, Larry Hastings wrote:
> Or code can just use inspect.get_type_hints(), which is tied to the
> Python version
> anyway and should always do the right thing.

So far, this has proven mostly[1] sufficient for my needs in a runtime
type validation and encoding/decoding library.

[1] A pain point for me is the runtime cost of evaluating 3.10 style
type hints, as they're (re-)evaluated for every call to get_type_hints.
I've worked around this for now with my own function affix_type_hints,
which evaluates get_type_hints once and replaces __annotations__ with
the evaluated value. It also addresses a scoping problem where a type
hint may reference a value that's not globally scoped for the object
being annotated; the hint can be evaluated and affixed within that
scope.

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


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

2021-01-11 Thread Chris Angelico
On Tue, Jan 12, 2021 at 4:22 AM Larry Hastings  wrote:
>
>
> I've written a new PEP.  Please find it below.  Happy reading!
>

Can this get added to the PEPs repo and assigned a number and such?

BTW, the currently preferred wording for the copyright blurb is
slightly different. If you're the sole author of this text, can you
please consider the license terms shown in PEP 12?

ChrisA
PEP editor - if you need a hand, I'm here to help
___
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/QPVFGPHFGODJ4AJLLYFIGPKIYCKK2KPW/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


Certainly.  I'm just another victim in the copy-and-paste wars.

I actually have write access to the PEPs repo (I'm a former release 
manager) so I'd be happy to check it in myself once it gets a number, 
however that happens.  Before I do so I'll study PEP 12 as if it was 
gonna be on tomorrow's midterms.



//arry/

On 1/11/21 9:59 AM, Chris Angelico wrote:

On Tue, Jan 12, 2021 at 4:22 AM Larry Hastings  wrote:


I've written a new PEP.  Please find it below.  Happy reading!


Can this get added to the PEPs repo and assigned a number and such?

BTW, the currently preferred wording for the copyright blurb is
slightly different. If you're the sole author of this text, can you
please consider the license terms shown in PEP 12?

ChrisA
PEP editor - if you need a hand, I'm here to help
___
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/AHFBMKWOP6AOSDKFIFUFGGIO56I3UEPX/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Chris Angelico
On Tue, Jan 12, 2021 at 5:10 AM Larry Hastings  wrote:
>
>
> Certainly.  I'm just another victim in the copy-and-paste wars.
>

Ah yes, the Battle of the Clipboard. Iconic, epic, such a glorious
engagement! But the casualties were steep. Fortunately we can rebuild.

> I actually have write access to the PEPs repo (I'm a former release manager) 
> so I'd be happy to check it in myself once it gets a number, however that 
> happens.  Before I do so I'll study PEP 12 as if it was gonna be on 
> tomorrow's midterms.
>

Number allocation is pretty informal. Go ahead and grab PEP 649; in
the unlikely event that someone else pushes a commit creating that PEP
before you get to it, grab PEP 650 instead :)

I'm happy to help out if you need me, but it sounds like you got this!

ChrisA
___
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/F65N4HBJZFTZL2VNFV77ZTJN4RVPCHWU/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


On 1/11/21 10:16 AM, Chris Angelico wrote:

Number allocation is pretty informal. Go ahead and grab PEP 649;


It's now checked in as PEP 649, with a modern header, modern copyright, 
and I went ahead and grabbed the formatting stanza from the end too.


Welcome to the world, baby 649!


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


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

2021-01-11 Thread Barry Warsaw
Thanks for this detailed PEP and analysis, and for the interesting discussion 
in your separate thread.  I’m glad to see this work that we chatted about all 
that time before has coalesced into a PEP.

FYI: For those with write access to the PEPs repo, PEP number assignments are 
self-serve.  Just grab the next available one and manage any push race 
conditions accordingly.

Question:

> On Jan 11, 2021, at 09:21, Larry Hastings  wrote:
> 
> from __future__ import co_annotations
> -
> 
> In the prototype, the semantics presented in this PEP are gated with:
> 
> from __future__ import co_annotations

Given that PEP 563 is now the default in unreleased Python 3.10, does it make 
sense to introduce yet another __future__ import?  What would happen if you 
just piggybacked your idea onto that change?

-Barry



signature.asc
Description: Message signed with OpenPGP
___
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/FPXDS2D5RSSPDWWMIXJWNWUDAUI4QN3K/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


On 1/11/21 10:29 AM, Barry Warsaw wrote:

Given that PEP 563 is now the default in unreleased Python 3.10, does it make 
sense to introduce yet another __future__ import?  What would happen if you 
just piggybacked your idea onto that change?


Part of my proposal is to deprecate PEP 563's semantics.  If -> PEP 649 
<- was accepted, we'd undo making PEP 563 the default behavior in 3.10; 
the behavior would instead remain gated behind the "from __future__ 
import annotations".  It'd then go through a standard deprecation cycle 
(which is, what, three versions?) before finally being removed.


(If you look at the revision history of my repo, you'll see that my 
first checkin was to reverse Batuhan's checkin from October 6, restoring 
the "from __future__" gate for annotations.  Sorry, Batuhan!)


Frankly I'd be astonished if -> PEP 649 <- received such unanimous 
acceptance that it become the new default Python semantics without a 
"from __future__" introductory period.  You'd need a bigger brain than I 
have to think through all the ramifications of that sort of radical 
decision!  But if the steering committee requested it, I don't expect 
I'd put a fight.



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


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

2021-01-11 Thread Eric V. Smith

On 1/11/2021 1:10 PM, Larry Hastings wrote:



Certainly.  I'm just another victim in the copy-and-paste wars.

I actually have write access to the PEPs repo (I'm a former release 
manager) so I'd be happy to check it in myself once it gets a number, 
however that happens.  Before I do so I'll study PEP 12 as if it was 
gonna be on tomorrow's midterms.


It gets a number by you renaming your file to the next available number 
and checking it in. There's a race condition, so act fast!


Eric



//arry/

On 1/11/21 9:59 AM, Chris Angelico wrote:

On Tue, Jan 12, 2021 at 4:22 AM Larry Hastings  wrote:

I've written a new PEP.  Please find it below.  Happy reading!


Can this get added to the PEPs repo and assigned a number and such?

BTW, the currently preferred wording for the copyright blurb is
slightly different. If you're the sole author of this text, can you
please consider the license terms shown in PEP 12?

ChrisA
PEP editor - if you need a hand, I'm here to help


___
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/AHFBMKWOP6AOSDKFIFUFGGIO56I3UEPX/
Code of Conduct: http://python.org/psf/codeofconduct/
___
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/KMD6UQIHAPE7OL66PX4VUKTORY7RKN4M/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: 3.10 change (?) for __bool__

2021-01-11 Thread Mats Wichmann


On 1/8/21 4:31 PM, Mats Wichmann wrote:



Someone reported a testsuite break on stuff I work on (scons) with 
3.10a4, and it looks similar to this which appears in the changelog at


https://docs.python.org/3.10/whatsnew/changelog.html#changelog

bpo-23898: Fix inspect.classify_class_attrs() to support attributes with 
overloaded __eq__ and __bool__. Patch by Mike Bayer.


Except when I go look at that BPO issue, it's old - closed 2015.  Is 
this appearing in the 3.10 changelog in error?  Sorry - confused !!!


okay, that was silly, I didn't realize the changelog was cumulative over 
many versions, so that entry was not for 3.10 at all (teach me to do 
searching in browser window, where it just flies right past any section 
headings so I miss it was for a different version :) ).


The test in question does indeed touch a class which overrides __bool_ 
in order to raise an exception (to say "don't do that"), and in the test 
run the (expected) exception is not raised.


So updated information: the test in question is checking if a class (A) 
has an attribute using a truth test, where the attribute's value is an 
instance of another class (B) and expecting that that will cause the 
__bool__ method to be called. [aside: this test is done to validate that 
a class which really doesn't want this kind of test indeed rejects it] 
That apparently no longer happens, if it's wrapped in a try block ??? 
Distilled down to simple case:


class A:
pass

class B:
def __bool__(self):
raise AttributeError("don't do that!")

a = A()
b = B()
a.b = b
# expect this to cause b.__bool__ to be called
if a.b:
print("Found it!")

and it raises the exception.  But when protected:

try:
if a.b:
pass
except AttributeError:
print("Got expected exception")
else:
print("Missed expected exception")

it won't trigger. But if I add a "real" statement in the block following 
the "if", then it's back to the pre-3.10 behavior of calling __bool__:


 try:
if a.b:
dummy = True
except AttributeError:
print("Got expected exception")
else:
print("Missed expected exception")


Any thoughts on this?
___
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/VC6VRTL7LKE4PVFQBYJW4HYJX6D6TJVM/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: 3.10 change (?) for __bool__

2021-01-11 Thread Chris Angelico
On Tue, Jan 12, 2021 at 6:00 AM Mats Wichmann  wrote:
>
>
> On 1/8/21 4:31 PM, Mats Wichmann wrote:
> >
> >
> > Someone reported a testsuite break on stuff I work on (scons) with
> > 3.10a4, and it looks similar to this which appears in the changelog at
> >
> > https://docs.python.org/3.10/whatsnew/changelog.html#changelog
> >
> > bpo-23898: Fix inspect.classify_class_attrs() to support attributes with
> > overloaded __eq__ and __bool__. Patch by Mike Bayer.
> >
> > Except when I go look at that BPO issue, it's old - closed 2015.  Is
> > this appearing in the 3.10 changelog in error?  Sorry - confused !!!
>
> okay, that was silly, I didn't realize the changelog was cumulative over
> many versions, so that entry was not for 3.10 at all (teach me to do
> searching in browser window, where it just flies right past any section
> headings so I miss it was for a different version :) ).
>
> > The test in question does indeed touch a class which overrides __bool_
> > in order to raise an exception (to say "don't do that"), and in the test
> > run the (expected) exception is not raised.
>
> So updated information: the test in question is checking if a class (A)
> has an attribute using a truth test, where the attribute's value is an
> instance of another class (B) and expecting that that will cause the
> __bool__ method to be called. [aside: this test is done to validate that
> a class which really doesn't want this kind of test indeed rejects it]
> That apparently no longer happens, if it's wrapped in a try block ???
> Distilled down to simple case:
>
> class A:
>  pass
>
> class B:
>  def __bool__(self):
>  raise AttributeError("don't do that!")
>
> a = A()
> b = B()
> a.b = b
> # expect this to cause b.__bool__ to be called
> if a.b:
>  print("Found it!")
>
> and it raises the exception.  But when protected:
>
> try:
>  if a.b:
>  pass
> except AttributeError:
>  print("Got expected exception")
> else:
>  print("Missed expected exception")
>
> it won't trigger. But if I add a "real" statement in the block following
> the "if", then it's back to the pre-3.10 behavior of calling __bool__:
>
>   try:
>  if a.b:
>  dummy = True
> except AttributeError:
>  print("Got expected exception")
> else:
>  print("Missed expected exception")
>
>
> Any thoughts on this?

Oooh interesting. I tried on a build of 3.10 from October and:
1) The unguarded version bombed out with an exception
2) The "if... pass" version reported that it got the exception
3) The "if... dummy" version reported that it got the exception

ie every one of them did indeed raise. But on a fresh build from the
master branch, I got the same results you did. That means the change
happened some time between commit 497126f7ea and commit ace008c531, an
800ish commit span.

I'll start bisecting to try to track this down. It looks like "if a.b:
pass" is getting partially optimized out; the disassembly shows a
being loaded, its attribute b being looked up, and then it just jumps
to the else - there's no POP_JUMP_IF_FALSE as there is when there's a
bit of actual code in there.

ChrisA
___
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/K2LD2L5RF2ZFUYEXQ3Z5U4TY5QBRFPCQ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: 3.10 change (?) for __bool__

2021-01-11 Thread Pablo Galindo Salgado
This may be related to the changes in https://bugs.python.org/issue42246.
Could you open a new issue and add Mark Shannon to it if that turns to be
the case?

Pablo

On Mon, 11 Jan 2021 at 19:36, Chris Angelico  wrote:

> On Tue, Jan 12, 2021 at 6:00 AM Mats Wichmann  wrote:
> >
> >
> > On 1/8/21 4:31 PM, Mats Wichmann wrote:
> > >
> > >
> > > Someone reported a testsuite break on stuff I work on (scons) with
> > > 3.10a4, and it looks similar to this which appears in the changelog at
> > >
> > > https://docs.python.org/3.10/whatsnew/changelog.html#changelog
> > >
> > > bpo-23898: Fix inspect.classify_class_attrs() to support attributes
> with
> > > overloaded __eq__ and __bool__. Patch by Mike Bayer.
> > >
> > > Except when I go look at that BPO issue, it's old - closed 2015.  Is
> > > this appearing in the 3.10 changelog in error?  Sorry - confused !!!
> >
> > okay, that was silly, I didn't realize the changelog was cumulative over
> > many versions, so that entry was not for 3.10 at all (teach me to do
> > searching in browser window, where it just flies right past any section
> > headings so I miss it was for a different version :) ).
> >
> > > The test in question does indeed touch a class which overrides __bool_
> > > in order to raise an exception (to say "don't do that"), and in the
> test
> > > run the (expected) exception is not raised.
> >
> > So updated information: the test in question is checking if a class (A)
> > has an attribute using a truth test, where the attribute's value is an
> > instance of another class (B) and expecting that that will cause the
> > __bool__ method to be called. [aside: this test is done to validate that
> > a class which really doesn't want this kind of test indeed rejects it]
> > That apparently no longer happens, if it's wrapped in a try block ???
> > Distilled down to simple case:
> >
> > class A:
> >  pass
> >
> > class B:
> >  def __bool__(self):
> >  raise AttributeError("don't do that!")
> >
> > a = A()
> > b = B()
> > a.b = b
> > # expect this to cause b.__bool__ to be called
> > if a.b:
> >  print("Found it!")
> >
> > and it raises the exception.  But when protected:
> >
> > try:
> >  if a.b:
> >  pass
> > except AttributeError:
> >  print("Got expected exception")
> > else:
> >  print("Missed expected exception")
> >
> > it won't trigger. But if I add a "real" statement in the block following
> > the "if", then it's back to the pre-3.10 behavior of calling __bool__:
> >
> >   try:
> >  if a.b:
> >  dummy = True
> > except AttributeError:
> >  print("Got expected exception")
> > else:
> >  print("Missed expected exception")
> >
> >
> > Any thoughts on this?
>
> Oooh interesting. I tried on a build of 3.10 from October and:
> 1) The unguarded version bombed out with an exception
> 2) The "if... pass" version reported that it got the exception
> 3) The "if... dummy" version reported that it got the exception
>
> ie every one of them did indeed raise. But on a fresh build from the
> master branch, I got the same results you did. That means the change
> happened some time between commit 497126f7ea and commit ace008c531, an
> 800ish commit span.
>
> I'll start bisecting to try to track this down. It looks like "if a.b:
> pass" is getting partially optimized out; the disassembly shows a
> being loaded, its attribute b being looked up, and then it just jumps
> to the else - there's no POP_JUMP_IF_FALSE as there is when there's a
> bit of actual code in there.
>
> ChrisA
> ___
> 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/K2LD2L5RF2ZFUYEXQ3Z5U4TY5QBRFPCQ/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
___
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/7DMQXD3EF6CIAUIHFORMXEOUDZGUO2YW/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: 3.10 change (?) for __bool__

2021-01-11 Thread Guido van Rossum
All that said (I agree it's surprising that 3.10 seems backwards
incompatible here) I would personally not raise AttributeError but
TypeError in the `__bool__()` method.

On Mon, Jan 11, 2021 at 11:51 AM Pablo Galindo Salgado 
wrote:

> This may be related to the changes in https://bugs.python.org/issue42246.
> Could you open a new issue and add Mark Shannon to it if that turns to be
> the case?
>
> Pablo
>
> On Mon, 11 Jan 2021 at 19:36, Chris Angelico  wrote:
>
>> On Tue, Jan 12, 2021 at 6:00 AM Mats Wichmann  wrote:
>> >
>> >
>> > On 1/8/21 4:31 PM, Mats Wichmann wrote:
>> > >
>> > >
>> > > Someone reported a testsuite break on stuff I work on (scons) with
>> > > 3.10a4, and it looks similar to this which appears in the changelog at
>> > >
>> > > https://docs.python.org/3.10/whatsnew/changelog.html#changelog
>> > >
>> > > bpo-23898: Fix inspect.classify_class_attrs() to support attributes
>> with
>> > > overloaded __eq__ and __bool__. Patch by Mike Bayer.
>> > >
>> > > Except when I go look at that BPO issue, it's old - closed 2015.  Is
>> > > this appearing in the 3.10 changelog in error?  Sorry - confused !!!
>> >
>> > okay, that was silly, I didn't realize the changelog was cumulative over
>> > many versions, so that entry was not for 3.10 at all (teach me to do
>> > searching in browser window, where it just flies right past any section
>> > headings so I miss it was for a different version :) ).
>> >
>> > > The test in question does indeed touch a class which overrides __bool_
>> > > in order to raise an exception (to say "don't do that"), and in the
>> test
>> > > run the (expected) exception is not raised.
>> >
>> > So updated information: the test in question is checking if a class (A)
>> > has an attribute using a truth test, where the attribute's value is an
>> > instance of another class (B) and expecting that that will cause the
>> > __bool__ method to be called. [aside: this test is done to validate that
>> > a class which really doesn't want this kind of test indeed rejects it]
>> > That apparently no longer happens, if it's wrapped in a try block ???
>> > Distilled down to simple case:
>> >
>> > class A:
>> >  pass
>> >
>> > class B:
>> >  def __bool__(self):
>> >  raise AttributeError("don't do that!")
>> >
>> > a = A()
>> > b = B()
>> > a.b = b
>> > # expect this to cause b.__bool__ to be called
>> > if a.b:
>> >  print("Found it!")
>> >
>> > and it raises the exception.  But when protected:
>> >
>> > try:
>> >  if a.b:
>> >  pass
>> > except AttributeError:
>> >  print("Got expected exception")
>> > else:
>> >  print("Missed expected exception")
>> >
>> > it won't trigger. But if I add a "real" statement in the block following
>> > the "if", then it's back to the pre-3.10 behavior of calling __bool__:
>> >
>> >   try:
>> >  if a.b:
>> >  dummy = True
>> > except AttributeError:
>> >  print("Got expected exception")
>> > else:
>> >  print("Missed expected exception")
>> >
>> >
>> > Any thoughts on this?
>>
>> Oooh interesting. I tried on a build of 3.10 from October and:
>> 1) The unguarded version bombed out with an exception
>> 2) The "if... pass" version reported that it got the exception
>> 3) The "if... dummy" version reported that it got the exception
>>
>> ie every one of them did indeed raise. But on a fresh build from the
>> master branch, I got the same results you did. That means the change
>> happened some time between commit 497126f7ea and commit ace008c531, an
>> 800ish commit span.
>>
>> I'll start bisecting to try to track this down. It looks like "if a.b:
>> pass" is getting partially optimized out; the disassembly shows a
>> being loaded, its attribute b being looked up, and then it just jumps
>> to the else - there's no POP_JUMP_IF_FALSE as there is when there's a
>> bit of actual code in there.
>>
>> ChrisA
>> ___
>> 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/K2LD2L5RF2ZFUYEXQ3Z5U4TY5QBRFPCQ/
>> Code of Conduct: http://python.org/psf/codeofconduct/
>>
> ___
> 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/7DMQXD3EF6CIAUIHFORMXEOUDZGUO2YW/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
Python-Dev mailing list -- python-dev@python.org
To unsub

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

2021-01-11 Thread Paul Bryan via Python-Dev
I'm very much in favour of the this concept. A few points come to mind
right away:


1. Backwards Compatibility

> PEP 563 changed the semantics of annotations. When its semantics are
> active, annotations must assume they will be evaluated inmodule-level
> scope. They may no longer refer directly to local variables or class
> attributes. 

Given get_type_hints can be provided localns argument, this statement
is not exactly true.

Using localns is how I currently address scoping issues when affixing
type hints. Maybe it could be argued that I'm abusing this feature, but
then I would ask what the intent of localns is if not to provide
additional (missing) scope during type hint evaluation?

Under PEP 649, when __co_annotations__ is called (presumably by calling
get_type_hints), would localns effectively be ignored?


2. __co_annotations__ scope?

I'm wondering why __co_annotations__ function could not be scoped
(within a closure?) such that it can access the values where the
function, method, class or module are being declared? I acknowledge
that I'm railing against PEP 563 again, trying to reclaim lost ground. 


On Mon, 2021-01-11 at 10:27 -0800, Larry Hastings wrote:
> 
> On 1/11/21 10:16 AM, Chris Angelico wrote:
> 
> > Number allocation is pretty informal. Go ahead and grab PEP 649;
> It's now checked in as PEP 649, with a modern header, modern
> copyright, and I went ahead and grabbed the formatting stanza from
> the end too.
> Welcome to the world, baby 649!
> 
> /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/OKOQEAEPKYDX6AVEFD7DDPBKOHGXB4GB/
> Code of Conduct: http://python.org/psf/codeofconduct/

___
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/5GLDASRAWNEJXYG3Y7NDUBPPNR6TTIGX/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


Thanks for your feedback!  I'll reply piecemeal.


On 1/11/21 12:32 PM, Paul Bryan wrote:

*1. Backwards Compatibility*

PEP 563  changed the 
semantics of annotations. When its semantics are active, annotations 
must assume they will be evaluated in /module-level/ scope. They may 
no longer refer directly to local variables or class attributes.


Given get_type_hints can be provided localns argument, this statement 
is not exactly true.


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.



Under PEP 649, when __co_annotations__ is called (presumably by 
calling get_type_hints), would localns effectively be ignored?


Yes.  You can experiment with this in Python 3.9--just turn off 
annotation stringizing.  It seems that you can still use strings as 
annotations and typing.get_type_hints() will evaluate them--and I assume 
it'll use localns at that point, just as it does today.




*2. __co_annotations__ scope?*

I'm wondering why __co_annotations__ function could not be scoped 
(within a closure?) such that it can access the values where the 
function, method, class or module are being declared? I acknowledge 
that I'm railing against PEP 563 again, trying to reclaim lost ground.


This is addressed in PEP 563, when it rejected the idea of using 
"function local state when defining annotations":


   This would be prohibitively expensive for highly annotated code as
   the frames would keep all their objects alive. That includes
   predominantly objects that won't ever be accessed again.

   
https://www.python.org/dev/peps/pep-0563/#keeping-the-ability-to-use-function-local-state-when-defining-annotations

Doing this automatically would indeed incur a sizeable runtime cost, for 
a feature that is already rarely used at runtime.  I guess it would be 
remotely possible? to add this as an optional feature?  But this gets 
crazy quickly--what if it's defined inside a function inside another 
function inside a class inside another function?--and the use cases seem 
few, and TOOWTDI.


I've never understood how closures work in Python, so I'm not the guy to 
ask how possible / hard this would be.  Then again, the implementation 
of closures is obscure enough that I've never been able to understand 
them, so that seems to establish at least a base level of difficulty.


Anyway, one of the concepts my PEP is built on is that "annotations are 
always evaluated at module-level scope". I'd be against changing that 
unless it could be achieved without runtime cost--which AFAIK is impossible.



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/2Y5MYSPHKCYDVEQN5APBFGBE6ZZDTKHH/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Jim J. Jewett
Could you be more explicit about what is banned by the control-flow exclusion?

I'm assuming that:

class A:
bar=float
if FOO:
bar=int
def a(x:int, y:int)->int   # function defined with annotations 
inside control flow
return x+y

def b(x:bar)  # function annotated with value that depends on control 
flow

is OK, and you're just talking about direct access to (the unfinished class or 
module).__annotations__ but I'm not certain.

-jJ
___
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/VMPMQWCGWR7LRFCEK57VJTQVV6TCQOQN/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


The control-flow exclusion is for /module//attribute/ or /class 
attribute/ annotations:


   class C:
  if random.random() > 0.5:
    my_attr:int=3
  else:
    my_attr2:float=3.5

Your example doesn't define any module attributes or class attributes 
inside flow control statements, so that code should work fine.  
(Defining functions/methods inside flow control statements isn't a problem.)



Cheers,


//arry/

On 1/11/21 1:39 PM, Jim J. Jewett wrote:

Could you be more explicit about what is banned by the control-flow exclusion?

I'm assuming that:

 class A:
 bar=float
 if FOO:
 bar=int
 def a(x:int, y:int)->int   # function defined with annotations 
inside control flow
 return x+y

 def b(x:bar)  # function annotated with value that depends on control 
flow

is OK, and you're just talking about direct access to (the unfinished class or 
module).__annotations__ but I'm not certain.

-jJ
___
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/VMPMQWCGWR7LRFCEK57VJTQVV6TCQOQN/
Code of Conduct: http://python.org/psf/codeofconduct/
___
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/I2B6N34MBCD5PVNVWNCOZAI3FGHM5M4K/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


On 1/11/21 1:16 PM, Larry Hastings wrote:

On 1/11/21 12:32 PM, Paul Bryan wrote:

*1. Backwards Compatibility*

PEP 563  changed the 
semantics of annotations. When its semantics are active, annotations 
must assume they will be evaluated in /module-level/ scope. They may 
no longer refer directly to local variables or class attributes.


Given get_type_hints can be provided localns argument, this statement 
is not exactly true.


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.




Whoops!  Let me walk that back a little.  I'd been assuming that PEP 563 
used the terms "annotations" and "type hints" to mean the exact same 
thing.  But careful reading of PEP 484 suggests that they're distinct 
concepts; all "type hints" are annotations, but not all annotations are 
"type hints".


So: if you're using annotations for something besides "type hints", such 
that you have a use for a non-None localns, I guess you have two options 
with my PEP: either


a) use strings for your annotations where you need localns to work for 
you, or


b) skip using annotations syntax and instead write your own custom 
__co_annotations__ function.  Or, you could mix and match, using 
annotations syntax where it was convenient, and overriding only the 
troublesome spots in your custom __co_annotations__ function:


   def foo(a:int=3, b):
    ...

   foo_static_annotations = foo.__annotations__

   def foo_dynamic_closure_annotations():
    annotations = dict(foo_static_annotations)
    annotations['b'] = MyExcitingLocallyDefinedClosureTypeThing
    return annotations

   foo.__co_annotations = foo_dynamic_closure_annotations


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


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

2021-01-11 Thread Brett Cannon
On Mon, Jan 11, 2021 at 9:23 AM Larry Hastings  wrote:

> [SNIP - background info]
>
>
> If I could wave my magic wand and do whatever I wanted, I'd change the
> semantics for __annotations__ to the following:
>
> * Functions, classes, and modules always have an __annotations__ member
> set.
> * "del o.__annotations__" always throws a TypeError.
> * The language will set __annotations__ to a dict if the object has
>   annotations, or None if it has no annotations.
> * You may set __annotations__, but you can only set it to either None or a
>   dict (passes PyDict_Check).
> * You may only access __annotations__ as an attribute, and because it's
>   always set, best practice is to use "o.__annotations__" (though getattr
>   will always work too).
> * How __annotations__ is stored is implementation-specific behavior;
>   looking in the relevant __dict__ is unsupported.
>
> This would grant sanity and consistency to __annotations__ in a way it's
> never so far enjoyed.  The problem is, it's a breaking change.  But the
> existing semantics are kind of terrible, so at this point my goal is to
> break them. I think the best practice needs to stop requiring examining
> cls.__dict__; in fact I'd prefer people stop doing it altogether.
>

So the biggest potential breakages are code that:

   1. Directly get the attribute from __dict__
   2. The fact that it would no longer be inherited

Am I missing anything else?

For issue #1, it seems that inspect.get_type_hints(), as you point out
below, resolves that. As well, code could be updated appropriately without
much effort to check different places if the attribute was not found in
__dict__.

For issue #2, if the default was `None`, then couldn't that be used as an
implicit feature marker that you can't (incorrectly) rely on inheritance to
percolate up the annotations of the superclass if the subclass happens to
not define any annotations?

This all seems reasonable to me. Since it's a change to the object model it
will probably need a PEP, but I would suspect it would mostly revolve
around guiding people to how to update their code to work across Python
versions.

-Brett

>
> If we change the behavior as part of a new release of Python,
> code that examines annotations on classes can do a version check:
>
> if (sys.version_info.major >=3
> and sys.version_info.minor >= 10):
>
> def get_annotations(o):
> return o.__annotations__ or {}
> else:
> def get_annotations(o):
> # eight or ten lines of complex code goes here
> ...
>
> Or code can just use inspect.get_type_hints(), which is tied to the Python
> version
> anyway and should always do the right thing.
>
>
> */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/AWKVI3NRCHKPIDPCJYGVLW4HBYTEOQYL/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
___
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/IY55VJHCYD2P3ZJUJI4RXX6UZS3I2LB5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: 3.10 change (?) for __bool__

2021-01-11 Thread Stestagg
I've raised: https://bugs.python.org/issue42899.

The triggering commit was:  c71581c7a4192e6ba9a79eccc583aaadab300efa
bpo-42615: Delete redundant jump instructions that only bypass empty blocks
(GH-23733)




On Mon, Jan 11, 2021 at 8:04 PM Guido van Rossum  wrote:

> All that said (I agree it's surprising that 3.10 seems backwards
> incompatible here) I would personally not raise AttributeError but
> TypeError in the `__bool__()` method.
>
> On Mon, Jan 11, 2021 at 11:51 AM Pablo Galindo Salgado <
> pablog...@gmail.com> wrote:
>
>> This may be related to the changes in https://bugs.python.org/issue42246.
>> Could you open a new issue and add Mark Shannon to it if that turns to be
>> the case?
>>
>> Pablo
>>
>> On Mon, 11 Jan 2021 at 19:36, Chris Angelico  wrote:
>>
>>> On Tue, Jan 12, 2021 at 6:00 AM Mats Wichmann  wrote:
>>> >
>>> >
>>> > On 1/8/21 4:31 PM, Mats Wichmann wrote:
>>> > >
>>> > >
>>> > > Someone reported a testsuite break on stuff I work on (scons) with
>>> > > 3.10a4, and it looks similar to this which appears in the changelog
>>> at
>>> > >
>>> > > https://docs.python.org/3.10/whatsnew/changelog.html#changelog
>>> > >
>>> > > bpo-23898: Fix inspect.classify_class_attrs() to support attributes
>>> with
>>> > > overloaded __eq__ and __bool__. Patch by Mike Bayer.
>>> > >
>>> > > Except when I go look at that BPO issue, it's old - closed 2015.  Is
>>> > > this appearing in the 3.10 changelog in error?  Sorry - confused !!!
>>> >
>>> > okay, that was silly, I didn't realize the changelog was cumulative
>>> over
>>> > many versions, so that entry was not for 3.10 at all (teach me to do
>>> > searching in browser window, where it just flies right past any section
>>> > headings so I miss it was for a different version :) ).
>>> >
>>> > > The test in question does indeed touch a class which overrides
>>> __bool_
>>> > > in order to raise an exception (to say "don't do that"), and in the
>>> test
>>> > > run the (expected) exception is not raised.
>>> >
>>> > So updated information: the test in question is checking if a class (A)
>>> > has an attribute using a truth test, where the attribute's value is an
>>> > instance of another class (B) and expecting that that will cause the
>>> > __bool__ method to be called. [aside: this test is done to validate
>>> that
>>> > a class which really doesn't want this kind of test indeed rejects it]
>>> > That apparently no longer happens, if it's wrapped in a try block ???
>>> > Distilled down to simple case:
>>> >
>>> > class A:
>>> >  pass
>>> >
>>> > class B:
>>> >  def __bool__(self):
>>> >  raise AttributeError("don't do that!")
>>> >
>>> > a = A()
>>> > b = B()
>>> > a.b = b
>>> > # expect this to cause b.__bool__ to be called
>>> > if a.b:
>>> >  print("Found it!")
>>> >
>>> > and it raises the exception.  But when protected:
>>> >
>>> > try:
>>> >  if a.b:
>>> >  pass
>>> > except AttributeError:
>>> >  print("Got expected exception")
>>> > else:
>>> >  print("Missed expected exception")
>>> >
>>> > it won't trigger. But if I add a "real" statement in the block
>>> following
>>> > the "if", then it's back to the pre-3.10 behavior of calling __bool__:
>>> >
>>> >   try:
>>> >  if a.b:
>>> >  dummy = True
>>> > except AttributeError:
>>> >  print("Got expected exception")
>>> > else:
>>> >  print("Missed expected exception")
>>> >
>>> >
>>> > Any thoughts on this?
>>>
>>> Oooh interesting. I tried on a build of 3.10 from October and:
>>> 1) The unguarded version bombed out with an exception
>>> 2) The "if... pass" version reported that it got the exception
>>> 3) The "if... dummy" version reported that it got the exception
>>>
>>> ie every one of them did indeed raise. But on a fresh build from the
>>> master branch, I got the same results you did. That means the change
>>> happened some time between commit 497126f7ea and commit ace008c531, an
>>> 800ish commit span.
>>>
>>> I'll start bisecting to try to track this down. It looks like "if a.b:
>>> pass" is getting partially optimized out; the disassembly shows a
>>> being loaded, its attribute b being looked up, and then it just jumps
>>> to the else - there's no POP_JUMP_IF_FALSE as there is when there's a
>>> bit of actual code in there.
>>>
>>> ChrisA
>>> ___
>>> 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/K2LD2L5RF2ZFUYEXQ3Z5U4TY5QBRFPCQ/
>>> Code of Conduct: http://python.org/psf/codeofconduct/
>>>
>> ___
>> 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.pyth

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

2021-01-11 Thread Łukasz Langa


> On 11 Jan 2021, at 18:21, Larry Hastings  wrote:
> 
> I've written a new PEP.  Please find it below.  Happy reading!
> 

Interesting! I like the clever lazy-evaluation of the __annotations__ using a 
pre-set code object. My only real reservation is that the transition process 
will be weird but I don't have much to offer in terms of how to smooth it out. 
I have two questions though:

1. What do you anticipate the memory usage will look like for your solution 
compared to PEP 563?

To give you an example, EdgeDB is a sizeable application with 100k SLOC of 
Python. It's got around 3,500 typed functions, all in all >71% type coverage. 
EdgeDB uses stringified annotations exclusively which minimizes runtime memory 
usage of annotations because those strings are pretty much all ASCII and many 
can be interned. Does it matter? It does, actually. Let's look at 20 most 
popular annotations in the codebase and how often they appear:

946 -> s_schema.Schema
362 -> str
298 -> sd.CommandContext
118 -> base.PLBlock
107 -> irast.Set
99 -> CommandContext
95 -> Any
86 -> qlast.DDLOperation
85 -> s_types.Type
71 -> bool
70 -> irast.PathId
67 -> int
54 -> context.Environment
46 -> so.Object
45 -> pgast.Query
42 -> uuid.UUID
38 -> irast.Base
38 -> sn.Name
37 -> pgast.SelectStmt
33 -> context.ReplContext
(this list tapers of with a long tail after)

Turns out most annotations are simple and predictable. (As a side note: we 
could make interning even stronger for this purpose if we allowed periods and 
square brackets for interning.)


2. What is your expected startup performance of an annotated Python application 
using co_annotations?

The stringification process which your PEP describes as costly only happens 
during compilation of a .py file to .pyc. Since pip-installing pre-compiles 
modules for the user at installation time, there is very little runtime penalty 
for a fully annotated application.


Cheers,
Ł___
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/AC6YLF4ZKXVXXXDVD357WU6I5B7P2WX2/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: 3.10 change (?) for __bool__

2021-01-11 Thread Mats Wichmann

On 1/11/21 1:00 PM, Guido van Rossum wrote:
All that said (I agree it's surprising that 3.10 seems backwards 
incompatible here) I would personally not raise AttributeError but 
TypeError in the `__bool__()` method.


eh, that was just me picking a cheap something to demo it.  the program 
raises an application-specific error that I didn't feel like defining to 
keep the repro as short as possible.

___
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/SVGFN4DCDN462QVVMHY45IKH2XL4GVRD/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Paul Bryan via Python-Dev
On Mon, 2021-01-11 at 13:16 -0800, Larry Hastings wrote:
> 
> Thanks for your feedback!  I'll reply piecemeal.
> 
> On 1/11/21 12:32 PM, Paul Bryan wrote:
> 
> > 1. Backwards Compatibility
> > 
> > 
> > > PEP 563 changed the semantics of annotations. When its semantics
> > > are active, annotations must assume they will be evaluated in
> > > module-level scope. They may no longer refer directly to local
> > > variables or class attributes. 
> > 
> > Given get_type_hints can be provided localns argument, this
> > statement is not exactly true.
> 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 acknowledge that this will not fly with static type checkers. I also
want to make sure that annotations can continue to serve runtime type
validation.

PEP 563 does go on to state:
> For code which uses annotations for other purposes, a
> regulareval(ann, globals, locals) call is enough to resolve the
> annotation.

And I believe this would no longer be true under PEP 649;
further, localns (and maybe globalns) parameters in get_type_hints
would become meaningless.

This passage in PEP 563 appears not true in Python 3.9 with __future__
annotations, emphasis mine:
> The get_type_hints() function automatically resolves the correct
> value of globalns for functions and classes. It also automatically
> provides the correct localns for classes.

If this passage was true, I believe the issue that resulted in my
affixing type hints could have been averted. 

> > Under PEP 649, when __co_annotations__ is called (presumably by
> > calling get_type_hints), would localns effectively be ignored?
> Yes.  You can experiment with this in Python 3.9--just turn off
> annotation stringizing.  It seems that you can still use strings as
> annotations and typing.get_type_hints() will evaluate them--and I
> assume it'll use localns at that point, just as it does today.
OK, would string representations of type hints continue be supported
under PEP 649 if strings are used as annotations? And, would
get_type_hints continue evaluate the annotations in that case? 

> > 2. __co_annotations__ scope?
> > 
> > I'm wondering why __co_annotations__ function could not be scoped
> > (within a closure?) such that it can access the values where the
> > function, method, class or module are being declared? I acknowledge
> > that I'm railing against PEP 563 again, trying to reclaim lost
> > ground.
> This is addressed in PEP 563, when it rejected the idea of using
> "function local state when defining annotations":

I wasn't thinking the function local state of that being annotated
(agree, this would be prohibitive), but rather the scope in which the
annotated function, class, module, etc. are being defined.

> > This would be prohibitively expensive for highly annotated code as
> > the frames would keep all their objects alive. That includes
> > predominantly objects that won't ever be accessed again.
> > 
> > https://www.python.org/dev/peps/pep-0563/#keeping-the-ability-to-use-function-local-state-when-defining-annotations
> Doing this automatically would indeed incur a sizeable runtime cost,
> for a feature that is already rarely used at runtime.  I guess it
> would be remotely possible? to add this as an optional feature?  But
> this gets crazy quickly--what if it's defined inside a function
> inside another function inside a class inside another function?--and
> the use cases seem few, and TOOWTDI.

I think this exactly the case for closures today.

> I've never understood how closures work in Python, so I'm not the guy
> to ask how possible / hard this would be.  Then again, the
> implementation of closures is obscure enough that I've never been
> able to understand them, so that seems to establish at least a base
> level of difficulty.
> Anyway, one of the concepts my PEP is built on is that "annotations
> are always evaluated at module-level scope". I'd be against changing
> that unless it could be achieved without runtime cost--which AFAIK is
> impossible.
> 
> 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/ZOEQFZ4RPZJW7MWO7US5OH23357QQLNV/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Neil Schemenauer
On 2021-01-11, Łukasz Langa wrote:
> The stringification process which your PEP describes as costly
> only happens during compilation of a .py file to .pyc. Since
> pip-installing pre-compiles modules for the user at installation
> time, there is very little runtime penalty for a fully annotated
> application.

It should be possible to make Larry's approach cheap as well.  I
have an old experiment stashed away[1] where I made the code object
for functions to be lazily created.  I.e. when a module is first
loaded, functions are not fully loaded until they are first
executed.  My goal was to reduce startup time.  It didn't show a
significant gain so I didn't pursue it further.

In my experiment, I deferred the unmarshal of the code object.
However, it occurs to me you could go a bit further and have the
function object be mostly skeletal until someone runs it or tries to
inspect it.  The skeleton would really be nothing but a file offset
(or memory offset, if using mmap) into the .pyc file.

Of course this would be some work to implement but then all Python
functions would benefit and likely Python startup time would be
reduced.  I think memory use would be reduced too since typically
you import a lot of modules but only use some of the functions in
them.

I like the idea of Larry's PEP.  I understand why the string-based
annotations was done (I use the __future__ import for my own code).
Using eval() is ugly though and Larry's idea seems like a nice way
to remove the need to call eval().


[1] https://github.com/nascheme/cpython/commits/lazy_codeobject
___
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/64DP2LFFRA5NO53PN3G46YZ7V3OD3RT2/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


On 1/11/21 3:02 PM, Paul Bryan wrote:

PEP 563 does go on to state:
For code which uses annotations for other purposes, a regular 
eval(ann, globals, locals) call is enough to resolve the annotation.


And I believe this would no longer be true under PEP 649; further, 
localns (and maybe globalns) parameters in get_type_hints would become 
meaningless.


[...]
And, would get_type_hints continue evaluate [string] annotations in 
that case?


I don't work on typing.get_type_hints() so someone else will have to 
answer this question.  All I can say is, with PEP 649 semantics, if you 
set an annotation to a string, you'll get a string back.  And in 3.9 
(and my out-of-date 3.10) I observe that typing.get_type_hints() will 
eval() string annotations for you, and localns is significant.



This passage in PEP 563 appears not true in Python 3.9 with __future__ 
annotations, emphasis mine:
The get_type_hints() function automatically resolves the correct 
value of globalns for functions and classes. *It also automatically 
provides the correct localns for classes.*


If this passage was true, I believe the issue that resulted in my 
affixing type hints could have been averted.


As you've discovered, this is one of the places where PEP 563 seems to 
be out-of-date with respect to its implementation.  I sifted through the 
source code to typing.get_type_hints() twice, and near as I can figure 
out, localns is literally only ever set to None unless you override it 
with the parameter.



OK, would string representations of type hints continue be supported 
under PEP 649 if strings are used as annotations?


PEP 649 is itself totally agnostic as to what value you use as an 
annotation.  It disallows a couple funky things (yield, walrus 
operator), but beyond that it doesn't care.  Any Python expression or 
value is fine.




*2. __co_annotations__ scope?*

I'm wondering why __co_annotations__ function could not be scoped 
(within a closure?) such that it can access the values where the 
function, method, class or module are being declared? I acknowledge 
that I'm railing against PEP 563 again, trying to reclaim lost ground.


This is addressed in PEP 563, when it rejected the idea of using 
"function local state when defining annotations":




I wasn't thinking the function local state of that being annotated 
(agree, this would be prohibitive), but rather the scope in which the 
annotated function, class, module, etc. are being defined.


That's what PEP 563 is referring to.  If you read the thread from 
November 2017 where the idea was discussed, they were talking about 
referring to e.g. "class-level definitions", as in, things defined 
inside class scope.  Which is prohibitive.


(If I understand you correctly, you thought it was referring to the 
scope inside the function when it runs?  Because I can't imagine how 
that would ever work.  What if the function hasn't been called yet?  
What if it's been called a thousand times?  What if it's running right 
now in various stages of completeness in five threads and you inspect 
the annotation from a sixth thread?)



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


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

2021-01-11 Thread Larry Hastings


At last, a nibble on the other fishing line! ;-)


On 1/11/21 1:47 PM, Brett Cannon wrote:

So the biggest potential breakages are code that:

 1. Directly get the attribute from __dict__
 2. The fact that it would no longer be inherited

Am I missing anything else?


Those are the big ones, the ones I expect people to actually 
experience.  I can name three more breakages, though these get 
progressively more obscure:


 * Nobody expect o.__annotations__ to ever be None (unless they
   assigned None to it themselves).  If the attribute is set they
   expect its value to be a dict.
 * "del o.__annotations__" currently works on modules and classes if
   the attribute is set.  "del fn.__annotations__" always works.
 * On modules and classes you can set o.__annotations__ to any Python
   value.  (Functions already only permit you to set it to None or a dict.)

I have no idea if anybody is depending on these behaviors.  The lesson 
that years of Python core dev has taught me is: if Python exhibits a 
behavior, somebody out there depends on it, and you'll break their code 
if you change it.  Or, expressed more succinctly, any change is a 
breaking change for somebody.  So the question is, is the improvement 
this brings worth the breakage it also brings? In this case, I sure hope so!



For issue #2, if the default was `None`, then couldn't that be used as 
an implicit feature marker that you can't (incorrectly) rely on 
inheritance to percolate up the annotations of the superclass if the 
subclass happens to not define any annotations?


Currently Python never sets o.__annotations__ to None on any object.  So 
yes, assuming the user doesn't set it to None themselves, this would be 
new behavior.  If I understand your question correctly, yes, users could 
write new code that says


   if o.__annotations__ is None:
    # great, we're in Python 3.10+ and no annotation was set on o!
    
   else:
    

Or they could just look at sys.version_info ;-)


Thanks for your feedback,


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


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

2021-01-11 Thread Guido van Rossum
I think your analysis of the problems is great. I don't worry about people
deleting `__attributes__` (and those that do can switch to calling its
.clear() method instead) but I worry about people not expecting to get
None. So if you can avoid that in your solution that would be great. The
easiest thing would be just to create an empty `__annotations__` for
classes that have no annotated variables, and to hell with the cost. People
who delete it are already living dangerously.

(I noticed that `__slots__` is missing from your list. Maybe because it
follows yet another pattern?)

On Mon, Jan 11, 2021 at 4:07 PM Larry Hastings  wrote:

>
> At last, a nibble on the other fishing line! ;-)
>
>
> On 1/11/21 1:47 PM, Brett Cannon wrote:
>
> So the biggest potential breakages are code that:
>
>1. Directly get the attribute from __dict__
>2. The fact that it would no longer be inherited
>
> Am I missing anything else?
>
> Those are the big ones, the ones I expect people to actually experience.
> I can name three more breakages, though these get progressively more
> obscure:
>
>- Nobody expect o.__annotations__ to ever be None (unless they
>assigned None to it themselves).  If the attribute is set they expect its
>value to be a dict.
>- "del o.__annotations__" currently works on modules and classes if
>the attribute is set.  "del fn.__annotations__" always works.
>- On modules and classes you can set o.__annotations__ to any Python
>value.  (Functions already only permit you to set it to None or a dict.)
>
> I have no idea if anybody is depending on these behaviors.  The lesson
> that years of Python core dev has taught me is: if Python exhibits a
> behavior, somebody out there depends on it, and you'll break their code if
> you change it.  Or, expressed more succinctly, any change is a breaking
> change for somebody.  So the question is, is the improvement this brings
> worth the breakage it also brings?  In this case, I sure hope so!
>
>
> For issue #2, if the default was `None`, then couldn't that be used as an
> implicit feature marker that you can't (incorrectly) rely on inheritance to
> percolate up the annotations of the superclass if the subclass happens to
> not define any annotations?
>
> Currently Python never sets o.__annotations__ to None on any object.  So
> yes, assuming the user doesn't set it to None themselves, this would be new
> behavior.  If I understand your question correctly, yes, users could write
> new code that says
>
> if o.__annotations__ is None:
> # great, we're in Python 3.10+ and no annotation was set on o!
> 
> else:
> 
>
> Or they could just look at sys.version_info ;-)
>
>
> Thanks for your feedback,
>
>
> */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/WAWHRS6RYMCOVQEFMLLRXIYLCHF4FHUJ/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
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/QKO42ILYVJPBD6EL3Q4MQTI63ZMKEFAV/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: 3.10 change (?) for __bool__

2021-01-11 Thread Guido van Rossum
Ah never mind. Seems to be a real bug -- thanks for reporting!

On Mon, Jan 11, 2021 at 2:57 PM Mats Wichmann  wrote:

> On 1/11/21 1:00 PM, Guido van Rossum wrote:
> > All that said (I agree it's surprising that 3.10 seems backwards
> > incompatible here) I would personally not raise AttributeError but
> > TypeError in the `__bool__()` method.
>
> eh, that was just me picking a cheap something to demo it.  the program
> raises an application-specific error that I didn't feel like defining to
> keep the repro as short as possible.
> ___
> 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/SVGFN4DCDN462QVVMHY45IKH2XL4GVRD/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
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/BK4IUDXCZDDQCRSX3QGY7XUHOKMIDPG4/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Greg Ewing

On 12/01/21 6:21 am, Larry Hastings wrote:


Unbound code objects


...The "annotation code object" is then stored *unbound*
as the internal value of ``__co_annotations__``; it is then
bound on demand when the user asks for ``__annotations__``.


This seems like premature optimisation. Function objects are
tiny compared to the code object, which is already a fairly
complicated thing composed of a number of sub-objects.

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


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

2021-01-11 Thread Paul Bryan via Python-Dev
Some more questions...

"Binding"," bound" and "unbound" code objects:
Is your use of "binding" terminology in the PEP identical to the
binding of a function to an object instance as a method during object
creation?

Function Annotations:
> When binding an unbound annotation code object, a function will use
> its own __globals__ as the new function's globals.
I'm having trouble parsing this. Does this mean the newly bound
__co_annotations__ function will inherit __globals__ from the function
it's annotating?

Exceptions:
It's quite possible for a __co_annotation__ function call to raise an
exception (e.g. NameError). When accessing __annotations__, if such an
exception is raised during the call to __co_annotations__, what is the
expected behavior?

s/__co_//?:
I'm probably naive, but is there a reason that one could not just store
a callable in __annotations__, and use the descriptor to resolve it to
a dictionary and store it when it is accessed? It would be one less
dunder in the Python data model.


On Mon, 2021-01-11 at 15:46 -0800, Larry Hastings wrote:
> 
> On 1/11/21 3:02 PM, Paul Bryan wrote:
> 
> > PEP 563 does go on to state:
> > 
> > > For code which uses annotations for other purposes, a regular
> > > eval(ann, globals, locals) call is enough to resolve the
> > > annotation.
> > 
> > And I believe this would no longer be true under PEP 649;
> > further, localns (and maybe globalns) parameters in get_type_hints
> > would become meaningless.
> > 
> > [...]
> > And, would get_type_hints continue evaluate [string] annotations in
> > that case?
> I don't work on typing.get_type_hints() so someone else will have to
> answer this question.  All I can say is, with PEP 649 semantics, if
> you set an annotation to a string, you'll get a string back.  And in
> 3.9 (and my out-of-date 3.10) I observe that typing.get_type_hints()
> will eval() string annotations for you, and localns is significant.
> 
> 
> > This passage in PEP 563 appears not true in Python 3.9 with
> > __future__ annotations, emphasis mine:
> > 
> > > The get_type_hints() function automatically resolves the correct
> > > value of globalns for functions and classes. It also
> > > automatically provides the correct localns for classes.
> > 
> > If this passage was true, I believe the issue that resulted in my
> > affixing type hints could have been averted.
> As you've discovered, this is one of the places where PEP 563 seems
> to be out-of-date with respect to its implementation.  I sifted
> through the source code to typing.get_type_hints() twice, and near as
> I can figure out, localns is literally only ever set to None unless
> you override it with the parameter.
> 
> 
> > OK, would string representations of type hints continue be
> > supported under PEP 649 if strings are used as annotations?
> PEP 649 is itself totally agnostic as to what value you use as an
> annotation.  It disallows a couple funky things (yield, walrus
> operator), but beyond that it doesn't care.  Any Python expression or
> value is fine.
> 
> 
> > 
> > > 
> > > > 2. __co_annotations__ scope?
> > > > 
> > > > I'm wondering why __co_annotations__ function could not be
> > > > scoped (within a closure?) such that it can access the values
> > > > where the function, method, class or module are being declared?
> > > > I acknowledge that I'm railing against PEP 563 again, trying to
> > > > reclaim lost ground.
> > > This is addressed in PEP 563, when it rejected the idea of using
> > > "function local state when defining annotations":
> > 
> > I wasn't thinking the function local state of that being annotated
> > (agree, this would be prohibitive), but rather the scope in which
> > the annotated function, class, module, etc. are being defined.
> That's what PEP 563 is referring to.  If you read the thread from
> November 2017 where the idea was discussed, they were talking about
> referring to e.g. "class-level definitions", as in, things defined
> inside class scope.  Which is prohibitive.
> (If I understand you correctly, you thought it was referring to the
> scope inside the function when it runs?  Because I can't imagine how
> that would ever work.  What if the function hasn't been called yet? 
> What if it's been called a thousand times?  What if it's running
> right now in various stages of completeness in five threads and you
> inspect the annotation from a sixth thread?)
> 
> 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/4EB5ZQQ65KLH3LDRLM4N4BQMVWGPKAAW/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Greg Ewing

On 12/01/21 6:22 am, Larry Hastings wrote:

 * The language will set __annotations__ to a dict if the object has

   annotations, or None if it has no annotations.


That sounds inconvenient -- it means that any code referencing
__annotations__ has to guard against the possibility of it being
None.

If we're changing things, I'm wondering if the best thing would be
to introduce an annotations() function as the new best practice for
getting an object's annotations. It would know how to handle all
the type-specific pecularities, and could take care of things such
as manufacturing an empty dict if the object doesn't have any
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/DXQJ2V32YIV5KZQNFSPFVGKKVHIZDFPD/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


On 1/11/21 2:32 PM, Łukasz Langa wrote:
1. What do you anticipate the memory usage will look like for your 
solution compared to PEP 563?


It depends on the scenario.  I talk about three runtime scenarios in PEP 
649.  But the most relevant scenario is "annotations are defined but 
never examined", because it's by far the most common for people using 
annotations.  So for now let's just talk about that.  In this scenario, 
I expect PEP 649 to be on par with PEP 563.


PEP 563 will define a small dict that nobody looks at; PEP 649 will 
define a simple code object that nobody runs.  These objects consume 
pretty similar amounts of memory.


A quick experiment: on my 64-bit Linux laptop, with a function that had 
three annotated parameters, sys.sizeof() of the resulting annotation 
dict was 232 bytes.  PEP 649 generated a 176 byte code object--but we 
should also factor in its bytecode (45 bytes) and lnotab (33 bytes), 
giving us 257 bytes.  (The other fields of the code object are redundant 
references to stuff we already had lying around.)


In that case PEP 649 is slightly bigger.  But if we change it to twelve 
annotated parameters, PEP 649 becomes a big win.  The dict is now 640 
bytes (!), but the code object only totals 280 bytes. It seems to flip 
at about five parameters; less than that, and the dict wins a little, 
greater than that and the code object starts winning by more and more.



2. What is your expected startup performance of an annotated Python 
application using co_annotations?


Again, the most relevant scenario is "annotations are defined but not 
referenced" so we'll stick with that.


On balance it should be roughly equivalent to "PEP 563" semantics, and 
perhaps a teeny-tiny bit faster.


With PEP 563 semantics, defining a function / class / module with 
annotations must build the annotations dict, then store it on the 
object.  But all the keys and values are strings, so the bytecode isn't 
much--for functions, it's just a bunch of LOAD_CONSTs then a 
BUILD_CONST_KEY_MAP.  For classes and modules it's a bit wordier, but if 
the bytecode performance was important here, we could probably convert 
it to use BUILD_CONST_KEY_MAP too.


With my PEP, defining a function / class / module with annotations means 
you LOAD_CONST the code object, then store it on the object--and that's 
it.  (Right now there's the whole __globals__ thing but I expect to get 
rid of that eventually).  Of course, the code object isn't free, it has 
to be unmarshalled--but as code objects go these are pretty simple 
ones.  Annotations code objects tend to have two custom bytestrings and 
a non-"small" int, and all the other attributes we get for free.


"stock" Python semantics is a bit slower than either, because it also 
evaluates all the annotations at the time the function / class / module 
is bound.



I'd love to hear real-world results from someone with a large annotated 
code base.  Unfortunately, I'm pretty sure typing.py is broken in the 
prototype right now, so it's a bit early yet.  (I honestly don't think 
it'll be that hard to get it working again, it was just one of a million 
things and I didn't want to hold up releasing this stuff to the world 
any longer.)



The stringification process which your PEP describes as costly only 
happens during compilation of a .py file to .pyc. Since pip-installing 
pre-compiles modules for the user at installation time, there is very 
little runtime penalty for a fully annotated application.


I never intended to suggest that the stringification process /itself/ is 
costly at runtime--and I don't think I did.  Because, as you point out, 
it isn't.  PEP 563 semantics writ large are costly at runtime only when 
annotations are examined, because you have to call eval(), and calling 
eval() is expensive.


If the PEP does say that stringification is itself expensive at runtime, 
please point it out, and I'll fix it.



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


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

2021-01-11 Thread Guido van Rossum
Isn't that just typing.get_type_hints()?

On Mon, Jan 11, 2021 at 5:11 PM Greg Ewing 
wrote:

> On 12/01/21 6:22 am, Larry Hastings wrote:
>
>   * The language will set __annotations__ to a dict if the object has
> >annotations, or None if it has no annotations.
>
> That sounds inconvenient -- it means that any code referencing
> __annotations__ has to guard against the possibility of it being
> None.
>
> If we're changing things, I'm wondering if the best thing would be
> to introduce an annotations() function as the new best practice for
> getting an object's annotations. It would know how to handle all
> the type-specific pecularities, and could take care of things such
> as manufacturing an empty dict if the object doesn't have any
> 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/DXQJ2V32YIV5KZQNFSPFVGKKVHIZDFPD/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
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/6L65WGSVHNOQ4AHUSUQYMCE34EJL4NQX/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


On 1/11/21 4:39 PM, Guido van Rossum wrote:
The easiest thing would be just to create an empty `__annotations__` 
for classes that have no annotated variables, and to hell with the cost.


I assume you'd keep the existing behavior where functions lazy-create an 
empty dict if they have no annotations too?


That all would work fine and be consistent, but you'd probably have to 
set the empty __annotations__ dict on modules too.  I've noticed that 
code that examines annotations tends to handle two classes of objects: 
"functions" and "not-functions".  Modules also store their 
__annotations__ in their __dict__, so the same code path works fine for 
examining the annotations of both classes and modules.



(I noticed that `__slots__` is missing from your list. Maybe because 
it follows yet another pattern?)


I forgot about __slots__!  Yup, it's optional, and you can even delete 
it, though after the class is defined I'm not sure how much difference 
that makes.


Slots intelligently support inheritance, too.  I always kind of wondered 
why annotations didn't support inheritance--if D is a subclass of C, why 
doesn't D.__annotations__ contain all C's annotations too?  But we're 
way past reconsidering that behavior now.



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


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

2021-01-11 Thread Greg Ewing

On 12/01/21 10:16 am, Larry Hastings wrote:
This is addressed in PEP 563, when it rejected the idea of using 
"function local state when defining annotations":


This would be prohibitively expensive for highly annotated code as
the frames would keep all their objects alive. That includes
predominantly objects that won't ever be accessed again.


I'm not sure what that's supposed to mean.

Firstly, functions that reference nonlocal names don't keep whole
frames alive, only the particular objects they reference.

Secondly, if an annotation references something at module level,
that something will also be kept alive unless it is explicitly
removed from the module -- which could also be done at a local
level if you didn't want to keep those things around.

So I don't really see any difference between global and local
state when it comes to things being kept alive by 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/GEO6SQL2ZN4KR5A72RK3OGEN4SZQM4TK/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Inada Naoki
> Performance
> ---
>
> Performance with this PEP should be favorable.  In general,
> resources are only consumed on demand—"you only pay for what you use".
>

Nice!

> There are three scenarios to consider:
>
> * the runtime cost when annotations aren't defined,
> * the runtime cost when annotations are defined but *not* referenced, and
> * the runtime cost when annotations are defined *and* referenced.
>

Note: The first two cases are major. Many codes doesn't have
annotations. Many codes use annotations just only for documentation or
static checker.
In the second scenario, the annotations must be very cheap. Its cost
must be comparable with docstrings.
Otherwise, people can not use annotation freely in a large codebase.
Or we must provide an option like -OO to strip annotations.


> We'll examine each of these scenarios in the context of all three
> semantics for annotations: stock, PEP 563, and this PEP.
>
> When there are no annotations, all three semantics have the same
> runtime cost: zero. No annotations dict is created and no code is
> generated for it.  This requires no runtime processor time and
> consumes no memory.
>
> When annotations are defined but not referenced, the runtime cost
> of Python with this PEP should be slightly faster than either
> original Python semantics or PEP 563 semantics.  With those, the
> annotations dicts are built but never examined; with this PEP,
> the annotations dicts won't even be built.  All that happens at
> runtime is the loading of a single constant (a simple code
> object) which is then set as an attribute on an object.  Since
> the annotations are never referenced, the code object is never
> bound to a function, the code to create the dict is never
> executed, and the dict is never constructed.
>

Note that PEP 563 semantics allows more efficient implementation.
Annotation is just a single constant tuple, not a dict.
We already have the efficient implementation for Python 3.10.

The efficient implementation in 3.10 can share tuples. If there are
hundreds of methods with the same signature, annotation is just a
single tuple, not hundreds of tuples. This is very efficient for auto
generated codebase. I think this PEP can share the code objects for
same signature by removing co_firstlineno information too.

Additionally, we should include the cost for loading annotations from
PYC files, because most annotations are "load once, set once".
Loading "simple code object" from pyc files is not so cheap. It may
affect importing time of large annotated codebase and memory
footprints.

I think we need a reference application that has a large codebase and
highly annotated. But we need to beware even if the large application
is 100% annotated, libraries used are not 100% annotated.
Many libraries are dropping Python 2 support and start annotating. The
cost of the annotations will become much more important in next
several years.
___
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/Q5MSBHXD3VPCVQODSSO3FOB3DRQS4SVG/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


On 1/11/21 4:55 PM, Greg Ewing wrote:

On 12/01/21 6:21 am, Larry Hastings wrote:


Unbound code objects


...The "annotation code object" is then stored *unbound*
as the internal value of ``__co_annotations__``; it is then
bound on demand when the user asks for ``__annotations__``.


This seems like premature optimisation. Function objects are
tiny compared to the code object, which is already a fairly
complicated thing composed of a number of sub-objects.



I'll have to let people with large code bases speak up about this, but 
my understanding is that most people would prefer Python to use less 
memory.  On my 64-bit Linux machine, a code object is 136 bytes, its 
empty __dict__ is 64 bytes, and the other stuff you get for free.  So 
that's 200 bytes even.  Multiply that by 1000 and the back of my 
envelope says you've wasted 200k.  Is that a big deal?  I dunno.


On the other hand, the code to support dynamically binding the code 
object on demand wasn't a big deal.



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


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

2021-01-11 Thread Guido van Rossum
On Mon, Jan 11, 2021 at 5:21 PM Larry Hastings  wrote:

>
> On 1/11/21 4:39 PM, Guido van Rossum wrote:
>
> The easiest thing would be just to create an empty `__annotations__` for
> classes that have no annotated variables, and to hell with the cost.
>
> I assume you'd keep the existing behavior where functions lazy-create an
> empty dict if they have no annotations too?
>
Indeed -- that's trying to provide a uniform interface.

> That all would work fine and be consistent, but you'd probably have to set
> the empty __annotations__ dict on modules too.  I've noticed that code that
> examines annotations tends to handle two classes of objects: "functions"
> and "not-functions".  Modules also store their __annotations__ in their
> __dict__, so the same code path works fine for examining the annotations of
> both classes and modules.
>

I'm not against giving all modules an empty `__annotations__` by default.

>
> (I noticed that `__slots__` is missing from your list. Maybe because it
> follows yet another pattern?)
>
> I forgot about __slots__!  Yup, it's optional, and you can even delete it,
> though after the class is defined I'm not sure how much difference that
> makes.
>
> Slots intelligently support inheritance, too.  I always kind of wondered
> why annotations didn't support inheritance--if D is a subclass of C, why
> doesn't D.__annotations__ contain all C's annotations too?  But we're way
> past reconsidering that behavior now.
>

Anyway, `__slots__` doesn't behave that way -- seems it behaves similar to
`__annotations__`.
```
Python 3.9.1 (tags/v3.9.1:1e5d33e, Dec  7 2020, 17:08:21) [MSC v.1927 64
bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class A: __slots__ = ['a']
...
>>> class B(A): __slots__ = ['b']
...
>>> B.__slots__
['b']
>>> class X(A): pass
...
>>> X.__slots__
['a']
>>> A().__dict__
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'A' object has no attribute '__dict__'
>>> X().__dict__
{}
>>>
```

-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
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/KDBOXAFOGAQGPWYLCQSR2CNPC6QEX772/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: __init_subclass__ and metaclasses

2021-01-11 Thread Ethan Furman

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.  
;-)

But it does work.

--
~Ethan~
___
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/57NF3L53RJPJGUCSFIG7KPKYDLTCUCZL/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


On 1/11/21 5:28 PM, Guido van Rossum wrote:
On Mon, Jan 11, 2021 at 5:21 PM Larry Hastings > wrote:


Slots intelligently support inheritance, too.  I always kind of
wondered why annotations didn't support inheritance--if D is a
subclass of C, why doesn't D.__annotations__ contain all C's
annotations too?  But we're way past reconsidering that behavior now.


Anyway, `__slots__` doesn't behave that way -- seems it behaves 
similar to `__annotations__`.


__slots__ itself doesn't behave that way, but subclasses do inherit the 
slots defined on their parent:


   class C:
    __slots__ = ['a']

   class D(C):
    __slots__ = ['b']

   d = D()
   d.a = 5
   d.b = "foo"
   print(f"{d.a=} {d.b=}")

prints

   d.a=5 d.b='foo'

That's the inheritance behavior I was referring to.


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


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

2021-01-11 Thread Larry Hastings


On 1/11/21 5:05 PM, Greg Ewing wrote:

On 12/01/21 6:22 am, Larry Hastings wrote:

 * The language will set __annotations__ to a dict if the object has

   annotations, or None if it has no annotations.


That sounds inconvenient -- it means that any code referencing
__annotations__ has to guard against the possibility of it being
None.


It was a balancing act.  Using an 64-byte empty dict per object with no 
defined annotations seems so wasteful.  And anything short of an empty 
dict, you'd have to guard against.  Current code already has to guard 
against "__annotations__ aren't set" anyway, so I figured the cost of 
migrating to checking a different condition would be small.  And None is 
so cheap, and the guard is so easy:


   if o.__annotations__:



If we're changing things, I'm wondering if the best thing would be
to introduce an annotations() function as the new best practice for
getting an object's annotations. It would know how to handle all
the type-specific pecularities, and could take care of things such
as manufacturing an empty dict if the object doesn't have any
annotations.


I guess I'm marginally against this, just because it seems like a 
needless change.  We don't need the flexibility of a function with 
optional parameters and such, and with a data descriptor we can already 
put code behind __annotations__ (as I have already done). Plus, the 
function should probably cache its result--you wouldn't want code that 
called it ten times to generate ten fresh dicts, would you?--and already 
we're most of the way to what I proposed in PEP 649.



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


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

2021-01-11 Thread Larry Hastings


On 1/11/21 5:02 PM, Paul Bryan wrote:

Some more questions...

"Binding"," bound" and "unbound" code objects:
Is your use of "binding" terminology in the PEP identical to the 
binding of a function to an object instance as a method during object 
creation?


I'm not.  In PEP 649 I think every reference of "binding" is talking 
about binding a code object to a globals dict to produce a function 
object.   The process of binding a function to an object instance to 
make a method is conceptually very similar, but distinct.


(and btw, functions aren't bound to their object to make methods during 
object creation, it's done lazily at the time you ask for it--that's 
what the "descriptor protocol" is all about!)





Function Annotations:
When binding an unbound annotation code object, a function will use 
its own __globals__ as the new function's globals.
I'm having trouble parsing this. Does this mean the newly bound 
__co_annotations__ function will inherit __globals__ from the function 
it's annotating?


Yes.  Though I wouldn't use "inherit", I'd just say it "uses" the 
__globals__ from the function.




Exceptions:
It's quite possible for a __co_annotation__ function call to raise an 
exception (e.g. NameError). When accessing __annotations__, if such an 
exception is raised during the call to __co_annotations__, what is the 
expected behavior?


If the function fails for any reason--throws an exception, or just 
doesn't return an acceptable value--then the getter immediately exits, 
and the internal state of the object is unchanged.  If you wanted to, 
you could catch the exception, fix the error, and get __annotations__ 
again, and it'd work.




s/__co_//?:
I'm probably naive, but is there a reason that one could not just 
store a callable in __annotations__, and use the descriptor to resolve 
it to a dictionary and store it when it is accessed? It would be one 
less dunder in the Python data model.


That would work, but I think the API is a bit of a code smell. 
__annotations__ would no longer be stable:


   a.__annotations__ = o
   assert a.__annotations__ == o

Would that assert fail?  It depends on what type(o) is, which is surprising.


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


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

2021-01-11 Thread Chris Angelico
On Tue, Jan 12, 2021 at 12:56 PM Larry Hastings  wrote:
>
> It was a balancing act.  Using an 64-byte empty dict per object with no 
> defined annotations seems so wasteful.  And anything short of an empty dict, 
> you'd have to guard against.  Current code already has to guard against 
> "__annotations__ aren't set" anyway, so I figured the cost of migrating to 
> checking a different condition would be small.  And None is so cheap, and the 
> guard is so easy:
>
> if o.__annotations__:
>

Does it have to be mutable? If not, maybe there could be a singleton
"immutable empty dict-like object", in the same way that an empty
tuple can be put anywhere that expects a sequence. That'd be as cheap
as None (modulo a once-per-interpreter cost for the additional static
object).

ChrisA
___
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/TWBC545KC7CK3DS5JZL4WBJM4BXZZBTI/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


On 1/11/21 6:09 PM, Chris Angelico wrote:

On Tue, Jan 12, 2021 at 12:56 PM Larry Hastings  wrote:

It was a balancing act.  Using an 64-byte empty dict per object with no defined 
annotations seems so wasteful.  And anything short of an empty dict, you'd have to guard 
against.  Current code already has to guard against "__annotations__ aren't 
set" anyway, so I figured the cost of migrating to checking a different condition 
would be small.  And None is so cheap, and the guard is so easy:

if o.__annotations__:


Does it have to be mutable? If not, maybe there could be a singleton
"immutable empty dict-like object", in the same way that an empty
tuple can be put anywhere that expects a sequence. That'd be as cheap
as None (modulo a once-per-interpreter cost for the additional static
object).


Historically, annotations dicts are mutable.  I don't know how often 
people mutate them, but I would assume it's uncommon.  So technically 
this would be a breaking change.  But it does seem low-risk.



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


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

2021-01-11 Thread Greg Ewing

On 12/01/21 10:41 am, Larry Hastings wrote:


So: if you're using annotations for something besides "type hints",


Didn't Guido decree that using annotations for anything other than
type hints is no longer supported?

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


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

2021-01-11 Thread Greg Ewing

On 12/01/21 11:32 am, Łukasz Langa wrote:


EdgeDB uses stringified annotations exclusively which 
minimizes runtime memory usage of annotations because those strings are 
pretty much all ASCII and many can be interned.


946 -> s_schema.Schema
362 -> str
298 -> sd.CommandContext


Seems to me the code objects for those would be identical
wherever they're used, and so could be cached and re-used
the same way as interned strings.

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


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

2021-01-11 Thread Paul Bryan via Python-Dev
On Mon, 2021-01-11 at 17:56 -0800, Larry Hastings wrote:

> On 1/11/21 5:02 PM, Paul Bryan wrote:
> 
> > 
> > Some more questions...
> > 
> > "Binding"," bound" and "unbound" code objects:
> > Is your use of "binding" terminology in the PEP identical to the
> > binding of a function to an object instance as a method during
> > object creation?
> I'm not.  In PEP 649 I think every reference of "binding" is talking
> about binding a code object to a globals dict to produce a function
> object.   The process of binding a function to an object instance to
> make a method is conceptually very similar, but distinct.
> (and btw, functions aren't bound to their object to make methods
> during object creation, it's done lazily at the time you ask for it--
> that's what the "descriptor protocol" is all about!)
I think it'd be worth briefly describing binding, to avoid confusion.
I'm now more educated on function/method binding lifecycle though, so
bonus! 🙂

> > Function Annotations:
> > 
> > > When binding an unbound annotation code object, a function will
> > > use its own __globals__ as the new function's globals.
> > I'm having trouble parsing this. Does this mean the newly bound
> > __co_annotations__ function will inherit __globals__ from the
> > function it's annotating?
> Yes.  Though I wouldn't use "inherit", I'd just say it "uses" the
> __globals__ from the function.

Then I think this solves my particular scoping problem with 3.10
string-evaluated annotations!

> > Exceptions:
> > It's quite possible for a __co_annotation__ function call to raise
> > an exception (e.g. NameError). When accessing __annotations__, if
> > such an exception is raised during the call to __co_annotations__,
> > what is the expected behavior?
> If the function fails for any reason--throws an exception, or just
> doesn't return an acceptable value--then the getter immediately
> exits, and the internal state of the object is unchanged.  If you
> wanted to, you could catch the exception, fix the error, and get
> __annotations__ again, and it'd work.
OK.

> > s/__co_//?:
> > I'm probably naive, but is there a reason that one could not just
> > store a callable in __annotations__, and use the descriptor to
> > resolve it to a dictionary and store it when it is accessed? It
> > would be one less dunder in the Python data model.
> That would work, but I think the API is a bit of a code smell. 
> __annotations__ would no longer be stable:
> > a.__annotations__ = o
> > assert a.__annotations__ == o
> Would that assert fail?  It depends on what type(o) is, which is
> surprising.

Equally surprising?:

a.__co_annotations__ = o
a.__annotations__
assert a.__co_annotations__ == o

Paul

___
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/Z7SFYJZQHUQKZC77L4EMNZIYAQUXK3GJ/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Greg Ewing

On 12/01/21 2:46 pm, Larry Hastings wrote:


Using an 64-byte empty dict per object with no 
defined annotations seems so wasteful.  And anything short of an empty 
dict, you'd have to guard against.


If __annotations__ were to return a read-only mapping object
instead of a dict, the same empty object could be used for all
annotationless objects.

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


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

2021-01-11 Thread Greg Ewing

On 12/01/21 2:21 pm, Larry Hastings wrote:

Slots intelligently support inheritance, too.


Are you sure about that? My experiments suggest that it has
the same problem as __annotations__:

Python 3.8.2 (default, Mar 23 2020, 11:36:18)
[Clang 8.1.0 (clang-802.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class C:
...  __slots__ = ['a', 'b']
...
>>> class D(C):
...  __slots__ = ['c', 'd']
...
>>> class E(D):
...  pass
...
>>> C.__slots__
['a', 'b']
>>> D.__slots__
['c', 'd']
>>> E.__slots__
['c', 'd']
>>>

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


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

2021-01-11 Thread Greg Ewing

On 12/01/21 2:56 pm, Larry Hastings wrote:


In PEP 649 I think every reference of "binding" is talking 
about binding a code object to a globals dict to produce a function 
object.   The process of binding a function to an object instance to 
make a method is conceptually very similar, but distinct.


Maybe "bare code object" would be a less confusing term?

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


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

2021-01-11 Thread Larry Hastings


On 1/11/21 6:31 PM, Greg Ewing wrote:

On 12/01/21 2:21 pm, Larry Hastings wrote:

Slots intelligently support inheritance, too.


Are you sure about that? My experiments suggest that it has
the same problem as __annotations__:

Python 3.8.2 (default, Mar 23 2020, 11:36:18)
[Clang 8.1.0 (clang-802.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class C:
...  __slots__ = ['a', 'b']
...
>>> class D(C):
...  __slots__ = ['c', 'd']
...
>>> class E(D):
...  pass
...
>>> C.__slots__
['a', 'b']
>>> D.__slots__
['c', 'd']
>>> E.__slots__
['c', 'd']
>>>



Guido said the same thing.  I did say "Slots", not "__slots__", though.  
You'll find that your class D supports attributes "a", "b", "c", and 
"d", and that's the inheritance I was referring to.



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


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

2021-01-11 Thread Larry Hastings


On 1/11/21 6:29 PM, Greg Ewing wrote:

On 12/01/21 10:41 am, Larry Hastings wrote:


So: if you're using annotations for something besides "type hints",


Didn't Guido decree that using annotations for anything other than
type hints is no longer supported?



It was never an official decree.  And, for what it's worth, Guido mostly 
walked that back, last month:


   
https://mail.python.org/archives/list/python-dev@python.org/message/OSA7VKZSAHQTTVEJDU2MZXESQKTDNKTB/


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


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

2021-01-11 Thread Guido van Rossum
Oh, but the behavior of annotations in e.g. mypy is the same. They are
cumulative.

On Mon, Jan 11, 2021 at 17:42 Larry Hastings  wrote:

>
> On 1/11/21 5:28 PM, Guido van Rossum wrote:
>
> On Mon, Jan 11, 2021 at 5:21 PM Larry Hastings  wrote:
>
>> Slots intelligently support inheritance, too.  I always kind of wondered
>> why annotations didn't support inheritance--if D is a subclass of C, why
>> doesn't D.__annotations__ contain all C's annotations too?  But we're way
>> past reconsidering that behavior now.
>>
>
> Anyway, `__slots__` doesn't behave that way -- seems it behaves similar to
> `__annotations__`.
>
> __slots__ itself doesn't behave that way, but subclasses do inherit the
> slots defined on their parent:
>
> class C:
> __slots__ = ['a']
>
> class D(C):
> __slots__ = ['b']
>
> d = D()
> d.a = 5
> d.b = "foo"
> print(f"{d.a=} {d.b=}")
>
> prints
>
> d.a=5 d.b='foo'
>
> That's the inheritance behavior I was referring to.
>
>
> Cheers,
>
>
> */arry*
>
-- 
--Guido (mobile)
___
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/BSHNRRCVORY7LZT7VCHZSEKRX73R4O7B/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Larry Hastings


On 1/11/21 6:34 PM, Paul Bryan wrote:


a.__annotations__ = o
assert a.__annotations__ == o

Would that assert fail?  It depends on what type(o) is, which is 
surprising.




Equally surprising?:

a.__co_annotations__ = o
a.__annotations__
assert a.__co_annotations__ == o



Well, since you asked, no.  It's a good point, but I think my example is 
a touch more "surprising".  In my example, a.__annotations__ has two 
different True values; in yours, a.__co_annotations__ goes from True to 
False.  It's acting more like a cache.


Also consider: if you set o.__annotations__ to a function, what if you 
want to examine the function later?  What if you want to examine the 
function built by Python?


   def enhance_annotations(co_annotations):
    def enhance():
    d = co_annotations()
    d['extra'] = ...
    return d
   o.__co_annotations__ = enhance_annotations(o.__co_annotations__)

Finally, it's just a code smell to have one attribute support such a 
bewildering variety of types.  Of course, this /works,/ but it's bad 
hygiene to store such violently different types in what is ostensibly 
the same attribute.



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/5OSZHRZUXD2DZG54ROUYQ3IQW42DFUAK/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Guido van Rossum
On Mon, Jan 11, 2021 at 1:20 PM Larry Hastings  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, {}, {}). There is
considerable discussion below in that same section, but it doesn't come
closer than stating "using local state in annotations is no longer possible
in general", and it states that get_type_hints() correctly provides the
right localns for classes.

Apart from how PEP 563 should be interpreted (maybe we should ask Lukasz) I
would say that basically all feedback I've seen over the changed semantics
of annotations in 3.10 is about this problem, and when I first saw your
proposal I thought that this would solve those issues. I'd be much less
enthusiastic if we're still going to force annotations to only reference
globals.

My read on PEP 563 is that it *primarily* solves the issue of having to put
forward references in string quotes (the most common case probably being
methods that accept or return an instance of the class in which they are
being defined). You have come up with a better solution for that -- the
solution that eluded us all when we were debating PEP 563. But I think that
you should try for maximal backward compatibility with the state of the
world *before* PEP 563.

In particular, static type checkers have no problem with annotations
referencing types defined in the local scope. This has always worked (hence
the complaints about PEP 563) and it would actually be extra work to forbid
this in certain situations but not others.


[...]

> *2. __co_annotations__ scope?*
>
> I'm wondering why __co_annotations__ function could not be scoped (within
> a closure?) such that it can access the values where the function, method,
> class or module are being declared? I acknowledge that I'm railing against
> PEP 563 again, trying to reclaim lost ground.
>
> This is addressed in PEP 563, when it rejected the idea of using "function
> local state when defining annotations":
>
> This would be prohibitively expensive for highly annotated code as the
> frames would keep all their objects alive. That includes predominantly
> objects that won't ever be accessed again.
>
>
> https://www.python.org/dev/peps/pep-0563/#keeping-the-ability-to-use-function-local-state-when-defining-annotations
>
>
But that's a strawman for your PEP. In PEP 563, they would have to keep the
whole frame alive. But with your PEP only the cells containing objects that
are *actually* referenced by annotations, i.e. exactly the objects that the
user wants to see preserved in `__annotations__`. (Well, at Instagram they
probably don't want to see any annotations survive the compilation stage,
but they can hack their own Python compiler -- we know they do that anyway.
:-)

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}
```

(Note that for *function* locals you don't have to do anything, they just
work, because of closures -- you may not understand them, but they are
important here. :-)

-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
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/IC2FDBJLHZA6HC7X5Q52Y46S66WTKPRS/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Guido van Rossum
On Mon, Jan 11, 2021 at 3:51 PM Larry Hastings  wrote:

> [...]
>
> This passage in PEP 563 appears not true in Python 3.9 with __future__
> annotations, emphasis mine:
>
> The get_type_hints() function automatically resolves the correct value of
> globalns for functions and classes. *It also automatically provides the
> correct localns for classes.*
>
>
> If this passage was true, I believe the issue that resulted in my affixing
> type hints could have been averted.
>
> As you've discovered, this is one of the places where PEP 563 seems to be
> out-of-date with respect to its implementation.  I sifted through the
> source code to typing.get_type_hints() twice, and near as I can figure out,
> localns is literally only ever set to None unless you override it with the
> parameter.
>

This seems to be a bug in get_type_hints() for which someone should file a
bug on bpo, please!
-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
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/FP5FMEHEA3FXOURIP5SSNDZMRBM7MHWU/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Paul Bryan via Python-Dev
Will do.

On Mon, 2021-01-11 at 20:55 -0800, Guido van Rossum wrote:
> On Mon, Jan 11, 2021 at 3:51 PM Larry Hastings 
> wrote:
> > [...]
> > > This passage in PEP 563 appears not true in Python 3.9 with
> > > __future__ annotations, emphasis mine:
> > > 
> > > > The get_type_hints() function automatically resolves the
> > > > correct value of globalns for functions and classes. It also
> > > > automatically provides the correct localns for classes.
> > > 
> > > If this passage was true, I believe the issue that resulted in my
> > > affixing type hints could have been averted.
> > As you've discovered, this is one of the places where PEP 563 seems
> > to be out-of-date with respect to its implementation.  I sifted
> > through the source code to typing.get_type_hints() twice, and near
> > as I can figure out, localns is literally only ever set to None
> > unless you override it with the parameter.
> > 
> 
> 
> This seems to be a bug in get_type_hints() for which someone should
> file a bug on bpo, please!

___
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/LS36RQ2UPLFKIOJAXF7EAGEMKBOCEIKW/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-01-11 Thread Guido van Rossum
 Another thought about this PEP (hopefully my last one tonight).

The section on backwards compatibility doesn't mention what should happen
with annotations that are stringified by the user (as is needed for forward
references in code that hasn't been PEP-563-ified yet).

That's a PEP 484 feature. Should we start deprecating that at the same
time? Static checkers support it but don't need it (for example, stubs in
typeshed don't use it since their code is never evaluated).

At the very least I think your PEP should mention what happens for these --
presumably `__annotations__` will just contain the string literal, so
get_type_hints() would be needed to evaluate these.

-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
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/VDLEMVDGBHKJEQR4JYZUWOPQMYYTCH5J/
Code of Conduct: http://python.org/psf/codeofconduct/