@@ -991,13 +991,16 @@ def __index__(self) -> int:
991991 pass
992992
993993
994- if sys . version_info >= ( 3 , 9 , 2 ):
994+ if hasattr ( typing , "Required" ):
995995 # The standard library TypedDict in Python 3.8 does not store runtime information
996996 # about which (if any) keys are optional. See https://bugs.python.org/issue38834
997997 # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
998998 # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059
999+ # The standard library TypedDict below Python 3.11 does not store runtime
1000+ # information about optional and required keys when using Required or NotRequired.
9991001 TypedDict = typing .TypedDict
10001002 _TypedDictMeta = typing ._TypedDictMeta
1003+ is_typeddict = typing .is_typeddict
10011004else :
10021005 def _check_fails (cls , other ):
10031006 try :
@@ -1081,7 +1084,6 @@ def __new__(cls, name, bases, ns, total=True):
10811084
10821085 annotations = {}
10831086 own_annotations = ns .get ('__annotations__' , {})
1084- own_annotation_keys = set (own_annotations .keys ())
10851087 msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
10861088 own_annotations = {
10871089 n : typing ._type_check (tp , msg ) for n , tp in own_annotations .items ()
@@ -1095,10 +1097,29 @@ def __new__(cls, name, bases, ns, total=True):
10951097 optional_keys .update (base .__dict__ .get ('__optional_keys__' , ()))
10961098
10971099 annotations .update (own_annotations )
1098- if total :
1099- required_keys .update (own_annotation_keys )
1100+ if PEP_560 :
1101+ for annotation_key , annotation_type in own_annotations .items ():
1102+ annotation_origin = get_origin (annotation_type )
1103+ if annotation_origin is Annotated :
1104+ annotation_args = get_args (annotation_type )
1105+ if annotation_args :
1106+ annotation_type = annotation_args [0 ]
1107+ annotation_origin = get_origin (annotation_type )
1108+
1109+ if annotation_origin is Required :
1110+ required_keys .add (annotation_key )
1111+ elif annotation_origin is NotRequired :
1112+ optional_keys .add (annotation_key )
1113+ elif total :
1114+ required_keys .add (annotation_key )
1115+ else :
1116+ optional_keys .add (annotation_key )
11001117 else :
1101- optional_keys .update (own_annotation_keys )
1118+ own_annotation_keys = set (own_annotations .keys ())
1119+ if total :
1120+ required_keys .update (own_annotation_keys )
1121+ else :
1122+ optional_keys .update (own_annotation_keys )
11021123
11031124 tp_dict .__annotations__ = annotations
11041125 tp_dict .__required_keys__ = frozenset (required_keys )
@@ -1141,10 +1162,6 @@ class Point2D(TypedDict):
11411162 syntax forms work for Python 2.7 and 3.2+
11421163 """
11431164
1144-
1145- if hasattr (typing , "is_typeddict" ):
1146- is_typeddict = typing .is_typeddict
1147- else :
11481165 if hasattr (typing , "_TypedDictMeta" ):
11491166 _TYPEDDICT_TYPES = (typing ._TypedDictMeta , _TypedDictMeta )
11501167 else :
@@ -1163,11 +1180,83 @@ class Film(TypedDict):
11631180 """
11641181 return isinstance (tp , tuple (_TYPEDDICT_TYPES ))
11651182
1183+ if hasattr (typing , "Required" ):
1184+ get_type_hints = typing .get_type_hints
1185+ elif PEP_560 :
1186+ import functools
1187+ import types
11661188
1167- # Python 3.9+ has PEP 593 (Annotated and modified get_type_hints)
1189+ # replaces _strip_annotations()
1190+ def _strip_extras (t ):
1191+ """Strips Annotated, Required and NotRequired from a given type."""
1192+ if isinstance (t , _AnnotatedAlias ):
1193+ return _strip_extras (t .__origin__ )
1194+ if hasattr (t , "__origin__" ) and t .__origin__ in (Required , NotRequired ):
1195+ return _strip_extras (t .__args__ [0 ])
1196+ if isinstance (t , typing ._GenericAlias ):
1197+ stripped_args = tuple (_strip_extras (a ) for a in t .__args__ )
1198+ if stripped_args == t .__args__ :
1199+ return t
1200+ return t .copy_with (stripped_args )
1201+ if hasattr (types , "GenericAlias" ) and isinstance (t , types .GenericAlias ):
1202+ stripped_args = tuple (_strip_extras (a ) for a in t .__args__ )
1203+ if stripped_args == t .__args__ :
1204+ return t
1205+ return types .GenericAlias (t .__origin__ , stripped_args )
1206+ if hasattr (types , "UnionType" ) and isinstance (t , types .UnionType ):
1207+ stripped_args = tuple (_strip_extras (a ) for a in t .__args__ )
1208+ if stripped_args == t .__args__ :
1209+ return t
1210+ return functools .reduce (operator .or_ , stripped_args )
1211+
1212+ return t
1213+
1214+ def get_type_hints (obj , globalns = None , localns = None , include_extras = False ):
1215+ """Return type hints for an object.
1216+
1217+ This is often the same as obj.__annotations__, but it handles
1218+ forward references encoded as string literals, adds Optional[t] if a
1219+ default value equal to None is set and recursively replaces all
1220+ 'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T'
1221+ (unless 'include_extras=True').
1222+
1223+ The argument may be a module, class, method, or function. The annotations
1224+ are returned as a dictionary. For classes, annotations include also
1225+ inherited members.
1226+
1227+ TypeError is raised if the argument is not of a type that can contain
1228+ annotations, and an empty dictionary is returned if no annotations are
1229+ present.
1230+
1231+ BEWARE -- the behavior of globalns and localns is counterintuitive
1232+ (unless you are familiar with how eval() and exec() work). The
1233+ search order is locals first, then globals.
1234+
1235+ - If no dict arguments are passed, an attempt is made to use the
1236+ globals from obj (or the respective module's globals for classes),
1237+ and these are also used as the locals. If the object does not appear
1238+ to have globals, an empty dictionary is used.
1239+
1240+ - If one dict argument is passed, it is used for both globals and
1241+ locals.
1242+
1243+ - If two dict arguments are passed, they specify globals and
1244+ locals, respectively.
1245+ """
1246+ if hasattr (typing , "Annotated" ):
1247+ hint = typing .get_type_hints (
1248+ obj , globalns = globalns , localns = localns , include_extras = True
1249+ )
1250+ else :
1251+ hint = typing .get_type_hints (obj , globalns = globalns , localns = localns )
1252+ if include_extras :
1253+ return hint
1254+ return {k : _strip_extras (t ) for k , t in hint .items ()}
1255+
1256+
1257+ # Python 3.9+ has PEP 593 (Annotated)
11681258if hasattr (typing , 'Annotated' ):
11691259 Annotated = typing .Annotated
1170- get_type_hints = typing .get_type_hints
11711260 # Not exported and not a public API, but needed for get_origin() and get_args()
11721261 # to work.
11731262 _AnnotatedAlias = typing ._AnnotatedAlias
@@ -1269,56 +1358,6 @@ def __init_subclass__(cls, *args, **kwargs):
12691358 raise TypeError (
12701359 f"Cannot subclass { cls .__module__ } .Annotated"
12711360 )
1272-
1273- def _strip_annotations (t ):
1274- """Strips the annotations from a given type.
1275- """
1276- if isinstance (t , _AnnotatedAlias ):
1277- return _strip_annotations (t .__origin__ )
1278- if isinstance (t , typing ._GenericAlias ):
1279- stripped_args = tuple (_strip_annotations (a ) for a in t .__args__ )
1280- if stripped_args == t .__args__ :
1281- return t
1282- res = t .copy_with (stripped_args )
1283- res ._special = t ._special
1284- return res
1285- return t
1286-
1287- def get_type_hints (obj , globalns = None , localns = None , include_extras = False ):
1288- """Return type hints for an object.
1289-
1290- This is often the same as obj.__annotations__, but it handles
1291- forward references encoded as string literals, adds Optional[t] if a
1292- default value equal to None is set and recursively replaces all
1293- 'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
1294-
1295- The argument may be a module, class, method, or function. The annotations
1296- are returned as a dictionary. For classes, annotations include also
1297- inherited members.
1298-
1299- TypeError is raised if the argument is not of a type that can contain
1300- annotations, and an empty dictionary is returned if no annotations are
1301- present.
1302-
1303- BEWARE -- the behavior of globalns and localns is counterintuitive
1304- (unless you are familiar with how eval() and exec() work). The
1305- search order is locals first, then globals.
1306-
1307- - If no dict arguments are passed, an attempt is made to use the
1308- globals from obj (or the respective module's globals for classes),
1309- and these are also used as the locals. If the object does not appear
1310- to have globals, an empty dictionary is used.
1311-
1312- - If one dict argument is passed, it is used for both globals and
1313- locals.
1314-
1315- - If two dict arguments are passed, they specify globals and
1316- locals, respectively.
1317- """
1318- hint = typing .get_type_hints (obj , globalns = globalns , localns = localns )
1319- if include_extras :
1320- return hint
1321- return {k : _strip_annotations (t ) for k , t in hint .items ()}
13221361# 3.6
13231362else :
13241363
0 commit comments