On Mon, Jan 11, 2021 at 9:23 AM Larry Hastings <larry@hastings.org> 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-leave@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/