gh-112509: Fix keys being present in both required_keys and optional_keys in TypedDict#112512
gh-112509: Fix keys being present in both required_keys and optional_keys in TypedDict#112512JelleZijlstra merged 3 commits intopython:mainfrom
Conversation
…ional_keys in TypedDict
|
I don't think this change is required; see #112509 (comment) |
AlexWaygood
left a comment
There was a problem hiding this comment.
Thanks, I agree that this is a problem that's worth fixing.
I don't think this change is required; see #112509 (comment)
I actually think whether or not static type checkers should consider this behaviour illegal according to PEP-589 has little bearing on what we should do at runtime here. I don't want us to start raising a TypeError if a TypedDict incompatibly overrides a specific key to make it Required when the subclass specified the same key as being NotRequired. Doing that now would break backwards compatibility at runtime, and I don't think there's a strong motivation for doing so: it's the job of type checkers to catch this kind of error and emit warnings about it. So, if we've ruled out raising a TypeError in this kind of situation, we just have to do the thing at runtime that makes the most sense -- and the status quo doesn't really make any sense, I don't think :)
| class Child(Base1, Base2): | ||
| pass | ||
|
|
||
| # Last base wins |
There was a problem hiding this comment.
Is it correct that the last base should win? Doesn't that go against how multiple inheritance in Python usually works, where earlier bases in the __bases__ tuple generally have priority?
Python 3.13.0a2+ (heads/main:e9d1360c9a, Nov 24 2023, 11:23:45) [MSC v.1932 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
... x = 1
...
>>> class Bar:
... x = 2
...
>>> class Baz(Foo, Bar): pass
...
>>> Baz.x
1There was a problem hiding this comment.
It's arguably not correct as you say, but it's the current behavior for __annotations__ and changing that behavior seems difficult.
>>> class A(TypedDict):
... a: int
...
>>> class B(TypedDict):
... a: str
...
>>> class C(A, B): pass
...
>>> C.__annotations__
{'a': <class 'str'>}
There was a problem hiding this comment.
Oof, that seems unfortunate. As you say, though, better to be internally consistent for now, I guess!
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
|
Thanks @JelleZijlstra for the PR 🌮🎉.. I'm working now to backport this PR to: 3.11, 3.12. |
|
GH-112530 is a backport of this pull request to the 3.12 branch. |
…ional_keys in TypedDict (pythonGH-112512) (cherry picked from commit 4038869) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
…ional_keys in TypedDict (pythonGH-112512) (cherry picked from commit 4038869) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
|
GH-112531 is a backport of this pull request to the 3.11 branch. |
…ional_keys in TypedDict (python#112512) Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
…ional_keys in TypedDict (python#112512) Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
__required_keys__and__optional_keys__can be wrong in the presence of inheritance #112509