-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpo-43901: Lazy-create an empty annotations dict in all unannotated user classes and modules #25623
bpo-43901: Lazy-create an empty annotations dict in all unannotated user classes and modules #25623
Conversation
I've marked it as draft because it's not ready for merging. In particular, it needs unit tests for this new behavior. I uploaded the PR so we might get a head start on reviewing the changes. In its current state it does pass the unit test suite. |
I copied some code from module_dir into module_{get|set}_annotations(). It seemed like it had a minor flaw: if the module dict failed a PyDict_Check(), it wanted to raise an exception. But to raise the exception, it wanted the name of the module, and if getting the name failed it skipped raising the exception. It turns out, if getting the name of the module fails, *that* raises an exception too. So, rather than stomp on that exception, we should do the same thing module_dir() does: leave that exception alone and return an error.
Added a test to ensure that the new C getsetdef for __annotations__ doesn't interfere with classes (or metaclasses!) redefining __annotations__ using the descriptor protocol.
I think this is ready for review. I have test coverage for all the new lines of code. Everything seems to work. It's as good as I can make it without adult supervision. |
@larryhastings: Please replace |
The behavior of the
__annotations__
member is wildly different between the three objects (function, class, module) that support it. Many of these differences are minor, but one in particular has been an awful wart for years: classes can inherit__annotations__
from their base classes. This is incontestably a misfire of the original design--it's never useful, it's only confusing, and libraries who examine annotations have had to work around it for years.I started a thread in January 2021 in which I brought up this and other inconsistencies with respect to
__annotations__
:The reaction was positive: yes, that's a genuine problem, and there's an easy, highly compatible fix that everybody liked: classes should always have an
__annotations__
dict set. So that the behavior is consistent, modules should always have one set too.Happily, lazy-creating the annotations dict is an adequate fix, as long as the annotations dict is still stored in the object's
__dict__
. Code written using the old best practice, which peeks in the__dict__
for annotations on classes, will still see (or not see) the dict as it did before, so it will continue to work unchanged. (Lazy-creating the annotations dict doesn't actually fix the inherited annotations problem for such code--but this code is already working around the problem, so this is fine.) Going forward, code written for 3.10+ can always usegetattr()
to get the annotations dict from any object.https://bugs.python.org/issue43901