The other thing to keep in mind is we are talking about every module, class, and function getting 64 bytes ... which I bet isn't that much.
Actually it's only every module and class. Functions don't have this problem because they've always stored __annotations__ internally--meaning, peeking in their __dict__ doesn't work, and they don't support inheritance anyway. So the number is even smaller than that.
If we can just make __annotations__ default to an empty dict on
classes and modules, and not worry about the memory consumption,
that goes a long way to cleaning up the semantics.
And I know you were somewhat joking when you mentioned using sys.version_info, but since this would be behind a __future__ import
My original proposal would make breaking changes to how you
examine __annotations__. Let's say we put those behind a from
__future__ import. Now we're gonna write library code that
examines annotations. A user passes in a class and asks us to
examine its annotations. The old semantics might be active on it,
or the new ones. How do we know which set of semantics we need to
It occurs to me that you could take kls.__module__, pull out the module from sys.modules, then look inside to see if it contains the correct "future" object imported from the __future__ module. Is that an approach we would suggest to our users?
Also, very little code ever examines annotations; most code with
annotations merely defines them. So I suspect most annotations
users wouldn't care either way--which also means a "from
__future__ import" that changes the semantics of examining or
modifying annotations isn't going to see a lot of uptake, because
it doesn't really affect them. The change in semantics only
affects people whose code examines annotations, which I suspect is
So I wasn't really joking when I proposed making these changes without a from __future__ import, and suggested users use a version check. The library code would know based on the Python version number which semantics were active, no peeking in modules to find future object. They could literally write what I suggested:
if you know you're running python 3.10 or higher:
examine using the new semantics
examine using the old semantics
I realize that's a pretty aggressive approach, which is why I prefaced it with "if I could wave my magic wand". But if we're going to make breaking changes, then whatever we do, it's going to break some people's code until it gets updated to cope with the new semantics. In that light this approach seemed reasonable.
But really this is why I started this thread in the first place.
My idea of what's reasonable is probably all out of whack. So I
wanted to start the conversation, to get feedback on how much
breakage is allowable and how best to mitigate it. If it wasn't a
controversial change, then we wouldn't need to talk about it!
And finally: if we really do set a default of an empty dict on classes and modules, then my other in-theory breaking changes:
will, I expect, in practice breaking exactly zero code. Who
deletes __annotations__? Who ever sets __annotations__ to
something besides a dict? So if the practical breakage is zero,
why bother gating it with "from __future__ import" at all?
I think it really means people need to rely on typing.get_type_hints() more than they may be doing right now.
What I find frustrating about that answer--and part of what motivated me to work on this in the first place--is that typing.get_type_hints() requires your annotations to be type hints. All type hints are annotations, but not all annotations are type hints, and it's entirely plausible for users to have reasonable uses for non-type-hint annotations that typing.get_type_hints() wouldn't like.
The two things typing.get_type_hints() does, that I know of, that
can impede such non-type-hint annotations are:
PEP 484 "explicitly does NOT prevent other uses of annotations". But if you force everyone to use typing.get_type_hints() to examine their annotations, then you have de facto prevented any use of annotations that isn't compatible with type hints.