diff options
author | Zac Hatfield-Dodds <Zac-HD@users.noreply.github.com> | 2019-11-24 10:48:48 (GMT) |
---|---|---|
committer | Ivan Levkivskyi <levkivskyi@gmail.com> | 2019-11-24 10:48:48 (GMT) |
commit | 665ad3dfa9993b9a4000b097ddead4e292590e8c (patch) | |
tree | 886741ee4ffc36e769575e51b5e576d1f8e22b02 /Lib/typing.py | |
parent | 041d8b48a2e59fa642b2c5124d78086baf74e339 (diff) | |
download | cpython-665ad3dfa9993b9a4000b097ddead4e292590e8c.zip cpython-665ad3dfa9993b9a4000b097ddead4e292590e8c.tar.gz cpython-665ad3dfa9993b9a4000b097ddead4e292590e8c.tar.bz2 |
Better runtime TypedDict (GH-17214)
This patch enables downstream projects inspecting a TypedDict subclass at runtime to tell which keys are optional.
This is essential for generating test data with Hypothesis or validating inputs with typeguard or pydantic.
Diffstat (limited to 'Lib/typing.py')
-rw-r--r-- | Lib/typing.py | 18 |
1 files changed, 15 insertions, 3 deletions
diff --git a/Lib/typing.py b/Lib/typing.py index 5523ee0..7de3e34 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1715,9 +1715,20 @@ class _TypedDictMeta(type): anns = ns.get('__annotations__', {}) msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" anns = {n: _type_check(tp, msg) for n, tp in anns.items()} + required = set(anns if total else ()) + optional = set(() if total else anns) + for base in bases: - anns.update(base.__dict__.get('__annotations__', {})) + base_anns = base.__dict__.get('__annotations__', {}) + anns.update(base_anns) + if getattr(base, '__total__', True): + required.update(base_anns) + else: + optional.update(base_anns) + tp_dict.__annotations__ = anns + tp_dict.__required_keys__ = frozenset(required) + tp_dict.__optional_keys__ = frozenset(optional) if not hasattr(tp_dict, '__total__'): tp_dict.__total__ = total return tp_dict @@ -1744,8 +1755,9 @@ class TypedDict(dict, metaclass=_TypedDictMeta): assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') - The type info can be accessed via Point2D.__annotations__. TypedDict - supports two additional equivalent forms:: + The type info can be accessed via the Point2D.__annotations__ dict, and + the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. + TypedDict supports two additional equivalent forms:: Point2D = TypedDict('Point2D', x=int, y=int, label=str) Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) |