@@ -1569,7 +1569,9 @@ def runtime_checkable(cls):
15691569 runtime = runtime_checkable
15701570
15711571
1572- if hasattr (typing , 'TypedDict' ):
1572+ if sys .version_info [:2 ] >= (3 , 9 ):
1573+ # The standard library TypedDict in Python 3.8 does not store runtime information
1574+ # about which (if any) keys are optional. See https://bugs.python.org/issue38834
15731575 TypedDict = typing .TypedDict
15741576else :
15751577 def _check_fails (cls , other ):
@@ -1652,9 +1654,20 @@ def __new__(cls, name, bases, ns, total=True):
16521654 anns = ns .get ('__annotations__' , {})
16531655 msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
16541656 anns = {n : typing ._type_check (tp , msg ) for n , tp in anns .items ()}
1657+ required = set (anns if total else ())
1658+ optional = set (() if total else anns )
1659+
16551660 for base in bases :
1656- anns .update (base .__dict__ .get ('__annotations__' , {}))
1661+ base_anns = base .__dict__ .get ('__annotations__' , {})
1662+ anns .update (base_anns )
1663+ if getattr (base , '__total__' , True ):
1664+ required .update (base_anns )
1665+ else :
1666+ optional .update (base_anns )
1667+
16571668 tp_dict .__annotations__ = anns
1669+ tp_dict .__required_keys__ = frozenset (required )
1670+ tp_dict .__optional_keys__ = frozenset (optional )
16581671 if not hasattr (tp_dict , '__total__' ):
16591672 tp_dict .__total__ = total
16601673 return tp_dict
@@ -1682,8 +1695,9 @@ class Point2D(TypedDict):
16821695
16831696 assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
16841697
1685- The type info could be accessed via Point2D.__annotations__. TypedDict
1686- supports two additional equivalent forms::
1698+ The type info can be accessed via the Point2D.__annotations__ dict, and
1699+ the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
1700+ TypedDict supports two additional equivalent forms::
16871701
16881702 Point2D = TypedDict('Point2D', x=int, y=int, label=str)
16891703 Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
0 commit comments