summaryrefslogtreecommitdiffstats
path: root/Lib/typing.py
diff options
context:
space:
mode:
authorJelle Zijlstra <jelle.zijlstra@gmail.com>2023-11-29 17:36:48 (GMT)
committerGitHub <noreply@github.com>2023-11-29 17:36:48 (GMT)
commit403886942376210662610627b01fea6acd77d331 (patch)
treee70297a9e1e59194ee4ad32840fbd1facc722dbe /Lib/typing.py
parente0449b9a7fffc0c0eed806bf4cbb8f1f65397bbb (diff)
downloadcpython-403886942376210662610627b01fea6acd77d331.zip
cpython-403886942376210662610627b01fea6acd77d331.tar.gz
cpython-403886942376210662610627b01fea6acd77d331.tar.bz2
gh-112509: Fix keys being present in both required_keys and optional_keys in TypedDict (#112512)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Diffstat (limited to 'Lib/typing.py')
-rw-r--r--Lib/typing.py25
1 files changed, 20 insertions, 5 deletions
diff --git a/Lib/typing.py b/Lib/typing.py
index 216f0c1..b3af701 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -2884,8 +2884,14 @@ class _TypedDictMeta(type):
for base in bases:
annotations.update(base.__dict__.get('__annotations__', {}))
- required_keys.update(base.__dict__.get('__required_keys__', ()))
- optional_keys.update(base.__dict__.get('__optional_keys__', ()))
+
+ base_required = base.__dict__.get('__required_keys__', set())
+ required_keys |= base_required
+ optional_keys -= base_required
+
+ base_optional = base.__dict__.get('__optional_keys__', set())
+ required_keys -= base_optional
+ optional_keys |= base_optional
annotations.update(own_annotations)
for annotation_key, annotation_type in own_annotations.items():
@@ -2897,14 +2903,23 @@ class _TypedDictMeta(type):
annotation_origin = get_origin(annotation_type)
if annotation_origin is Required:
- required_keys.add(annotation_key)
+ is_required = True
elif annotation_origin is NotRequired:
- optional_keys.add(annotation_key)
- elif total:
+ is_required = False
+ else:
+ is_required = total
+
+ if is_required:
required_keys.add(annotation_key)
+ optional_keys.discard(annotation_key)
else:
optional_keys.add(annotation_key)
+ required_keys.discard(annotation_key)
+ assert required_keys.isdisjoint(optional_keys), (
+ f"Required keys overlap with optional keys in {name}:"
+ f" {required_keys=}, {optional_keys=}"
+ )
tp_dict.__annotations__ = annotations
tp_dict.__required_keys__ = frozenset(required_keys)
tp_dict.__optional_keys__ = frozenset(optional_keys)