1+ # pyright: ignore[reportShadowedImports]
12import abc
23import builtins
34import collections
@@ -935,11 +936,11 @@ def __reduce__(self):
935936 del SingletonMeta
936937
937938
938- # Update this to something like >=3.13.0b1 if and when
939- # PEP 728 is implemented in CPython
940- _PEP_728_IMPLEMENTED = False
939+ # Update this to something like >=3.14 if and when
940+ # PEP 728/PEP 764 is implemented in CPython
941+ _PEP_728_OR_764_IMPLEMENTED = False
941942
942- if _PEP_728_IMPLEMENTED :
943+ if _PEP_728_OR_764_IMPLEMENTED :
943944 # The standard library TypedDict in Python 3.8 does not store runtime information
944945 # about which (if any) keys are optional. See https://bugs.python.org/issue38834
945946 # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
@@ -951,7 +952,7 @@ def __reduce__(self):
951952 # to enable better runtime introspection.
952953 # On 3.13 we deprecate some odd ways of creating TypedDicts.
953954 # Also on 3.13, PEP 705 adds the ReadOnly[] qualifier.
954- # PEP 728 (still pending) makes more changes.
955+ # PEP 728 and PEP 764 (still pending) makes more changes.
955956 TypedDict = typing .TypedDict
956957 _TypedDictMeta = typing ._TypedDictMeta
957958 is_typeddict = typing .is_typeddict
@@ -1095,7 +1096,11 @@ def __new__(cls, name, bases, ns, *, total=True, closed=None,
10951096 tp_dict .__extra_items__ = extra_items_type
10961097 return tp_dict
10971098
1098- __call__ = dict # static method
1099+ def __call__ (cls , * args , ** kwargs ):
1100+ if cls is TypedDict :
1101+ # Functional syntax, let `TypedDict.__new__` handle it:
1102+ return super ().__call__ (* args , ** kwargs )
1103+ return dict (* args , ** kwargs )
10991104
11001105 def __subclasscheck__ (cls , other ):
11011106 # Typed dicts are only for static structural subtyping.
@@ -1105,17 +1110,7 @@ def __subclasscheck__(cls, other):
11051110
11061111 _TypedDict = type .__new__ (_TypedDictMeta , 'TypedDict' , (), {})
11071112
1108- @_ensure_subclassable (lambda bases : (_TypedDict ,))
1109- def TypedDict (
1110- typename ,
1111- fields = _marker ,
1112- / ,
1113- * ,
1114- total = True ,
1115- closed = None ,
1116- extra_items = NoExtraItems ,
1117- ** kwargs
1118- ):
1113+ class TypedDict (metaclass = _TypedDictMeta ):
11191114 """A simple typed namespace. At runtime it is equivalent to a plain dict.
11201115
11211116 TypedDict creates a dictionary type such that a type checker will expect all
@@ -1162,52 +1157,77 @@ class Point2D(TypedDict):
11621157
11631158 See PEP 655 for more details on Required and NotRequired.
11641159 """
1165- if fields is _marker or fields is None :
1166- if fields is _marker :
1167- deprecated_thing = "Failing to pass a value for the 'fields' parameter"
1168- else :
1169- deprecated_thing = "Passing `None` as the 'fields' parameter"
11701160
1171- example = f"`{ typename } = TypedDict({ typename !r} , {{}})`"
1172- deprecation_msg = (
1173- f"{ deprecated_thing } is deprecated and will be disallowed in "
1174- "Python 3.15. To create a TypedDict class with 0 fields "
1175- "using the functional syntax, pass an empty dictionary, e.g. "
1176- ) + example + "."
1177- warnings .warn (deprecation_msg , DeprecationWarning , stacklevel = 2 )
1178- # Support a field called "closed"
1179- if closed is not False and closed is not True and closed is not None :
1180- kwargs ["closed" ] = closed
1181- closed = None
1182- # Or "extra_items"
1183- if extra_items is not NoExtraItems :
1184- kwargs ["extra_items" ] = extra_items
1185- extra_items = NoExtraItems
1186- fields = kwargs
1187- elif kwargs :
1188- raise TypeError ("TypedDict takes either a dict or keyword arguments,"
1189- " but not both" )
1190- if kwargs :
1191- if sys .version_info >= (3 , 13 ):
1192- raise TypeError ("TypedDict takes no keyword arguments" )
1193- warnings .warn (
1194- "The kwargs-based syntax for TypedDict definitions is deprecated "
1195- "in Python 3.11, will be removed in Python 3.13, and may not be "
1196- "understood by third-party type checkers." ,
1197- DeprecationWarning ,
1198- stacklevel = 2 ,
1199- )
1161+ def __new__ (
1162+ cls ,
1163+ typename ,
1164+ fields = _marker ,
1165+ / ,
1166+ * ,
1167+ total = True ,
1168+ closed = None ,
1169+ extra_items = NoExtraItems ,
1170+ ** kwargs
1171+ ):
1172+ if fields is _marker or fields is None :
1173+ if fields is _marker :
1174+ deprecated_thing = (
1175+ "Failing to pass a value for the 'fields' parameter"
1176+ )
1177+ else :
1178+ deprecated_thing = "Passing `None` as the 'fields' parameter"
1179+
1180+ example = f"`{ typename } = TypedDict({ typename !r} , {{}})`"
1181+ deprecation_msg = (
1182+ f"{ deprecated_thing } is deprecated and will be disallowed in "
1183+ "Python 3.15. To create a TypedDict class with 0 fields "
1184+ "using the functional syntax, pass an empty dictionary, e.g. "
1185+ ) + example + "."
1186+ warnings .warn (deprecation_msg , DeprecationWarning , stacklevel = 2 )
1187+ # Support a field called "closed"
1188+ if closed is not False and closed is not True and closed is not None :
1189+ kwargs ["closed" ] = closed
1190+ closed = None
1191+ # Or "extra_items"
1192+ if extra_items is not NoExtraItems :
1193+ kwargs ["extra_items" ] = extra_items
1194+ extra_items = NoExtraItems
1195+ fields = kwargs
1196+ elif kwargs :
1197+ raise TypeError ("TypedDict takes either a dict or keyword arguments,"
1198+ " but not both" )
1199+ if kwargs :
1200+ if sys .version_info >= (3 , 13 ):
1201+ raise TypeError ("TypedDict takes no keyword arguments" )
1202+ warnings .warn (
1203+ "The kwargs-based syntax for TypedDict definitions is deprecated "
1204+ "in Python 3.11, will be removed in Python 3.13, and may not be "
1205+ "understood by third-party type checkers." ,
1206+ DeprecationWarning ,
1207+ stacklevel = 2 ,
1208+ )
12001209
1201- ns = {'__annotations__' : dict (fields )}
1202- module = _caller ()
1203- if module is not None :
1204- # Setting correct module is necessary to make typed dict classes pickleable.
1205- ns ['__module__' ] = module
1210+ ns = {'__annotations__' : dict (fields )}
1211+ module = _caller (depth = 3 )
1212+ if module is not None :
1213+ # Setting correct module is necessary to make typed dict classes
1214+ # pickleable.
1215+ ns ['__module__' ] = module
12061216
1207- td = _TypedDictMeta (typename , (), ns , total = total , closed = closed ,
1208- extra_items = extra_items )
1209- td .__orig_bases__ = (TypedDict ,)
1210- return td
1217+ td = _TypedDictMeta (typename , (), ns , total = total , closed = closed ,
1218+ extra_items = extra_items )
1219+ td .__orig_bases__ = (TypedDict ,)
1220+ return td
1221+
1222+ def __class_getitem__ (cls , args ):
1223+ if not isinstance (args , tuple ):
1224+ args = (args ,)
1225+ if len (args ) != 1 or not isinstance (args [0 ], dict ):
1226+ raise TypeError (
1227+ "TypedDict[...] should be used with a single dict argument"
1228+ )
1229+
1230+ return cls .__new__ (cls , "<inlined TypedDict>" , args [0 ])
12111231
12121232 if hasattr (typing , "_TypedDictMeta" ):
12131233 _TYPEDDICT_TYPES = (typing ._TypedDictMeta , _TypedDictMeta )
@@ -1225,10 +1245,11 @@ class Film(TypedDict):
12251245 is_typeddict(Film) # => True
12261246 is_typeddict(Union[list, str]) # => False
12271247 """
1228- # On 3.8, this would otherwise return True
1229- if hasattr (typing , "TypedDict" ) and tp is typing .TypedDict :
1230- return False
1231- return isinstance (tp , _TYPEDDICT_TYPES )
1248+ return (
1249+ tp is not TypedDict
1250+ and tp is not typing .TypedDict
1251+ and isinstance (tp , _TYPEDDICT_TYPES )
1252+ )
12321253
12331254
12341255if hasattr (typing , "assert_type" ):
0 commit comments