@@ -1105,109 +1105,83 @@ def __subclasscheck__(cls, other):
11051105
11061106 _TypedDict = type .__new__ (_TypedDictMeta , 'TypedDict' , (), {})
11071107
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- ):
1119- """A simple typed namespace. At runtime it is equivalent to a plain dict.
1120-
1121- TypedDict creates a dictionary type such that a type checker will expect all
1122- instances to have a certain set of keys, where each key is
1123- associated with a value of a consistent type. This expectation
1124- is not checked at runtime.
1125-
1126- Usage::
1127-
1128- class Point2D(TypedDict):
1129- x: int
1130- y: int
1131- label: str
1132-
1133- a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK
1134- b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check
1135-
1136- assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
11371108
1138- The type info can be accessed via the Point2D.__annotations__ dict, and
1139- the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
1140- TypedDict supports an additional equivalent form::
1141-
1142- Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
1143-
1144- By default, all keys must be present in a TypedDict. It is possible
1145- to override this by specifying totality::
1146-
1147- class Point2D(TypedDict, total=False):
1148- x: int
1149- y: int
1150-
1151- This means that a Point2D TypedDict can have any of the keys omitted. A type
1152- checker is only expected to support a literal False or True as the value of
1153- the total argument. True is the default, and makes all items defined in the
1154- class body be required.
1109+ class _TypedDictSpecialForm (_ExtensionsSpecialForm , _root = True ):
1110+ def __call__ (
1111+ self ,
1112+ typename ,
1113+ fields = _marker ,
1114+ / ,
1115+ * ,
1116+ total = True ,
1117+ closed = None ,
1118+ extra_items = NoExtraItems ,
1119+ __typing_is_inline__ = False ,
1120+ ** kwargs
1121+ ):
1122+ if fields is _marker or fields is None :
1123+ if fields is _marker :
1124+ deprecated_thing = (
1125+ "Failing to pass a value for the 'fields' parameter"
1126+ )
1127+ else :
1128+ deprecated_thing = "Passing `None` as the 'fields' parameter"
11551129
1156- The Required and NotRequired special forms can also be used to mark
1157- individual keys as being required or not required::
1130+ example = f"`{ typename } = TypedDict({ typename !r} , {{}})`"
1131+ deprecation_msg = (
1132+ f"{ deprecated_thing } is deprecated and will be disallowed in "
1133+ "Python 3.15. To create a TypedDict class with 0 fields "
1134+ "using the functional syntax, pass an empty dictionary, e.g. "
1135+ ) + example + "."
1136+ warnings .warn (deprecation_msg , DeprecationWarning , stacklevel = 2 )
1137+ # Support a field called "closed"
1138+ if closed is not False and closed is not True and closed is not None :
1139+ kwargs ["closed" ] = closed
1140+ closed = None
1141+ # Or "extra_items"
1142+ if extra_items is not NoExtraItems :
1143+ kwargs ["extra_items" ] = extra_items
1144+ extra_items = NoExtraItems
1145+ fields = kwargs
1146+ elif kwargs :
1147+ raise TypeError ("TypedDict takes either a dict or keyword arguments,"
1148+ " but not both" )
1149+ if kwargs :
1150+ if sys .version_info >= (3 , 13 ):
1151+ raise TypeError ("TypedDict takes no keyword arguments" )
1152+ warnings .warn (
1153+ "The kwargs-based syntax for TypedDict definitions is deprecated "
1154+ "in Python 3.11, will be removed in Python 3.13, and may not be "
1155+ "understood by third-party type checkers." ,
1156+ DeprecationWarning ,
1157+ stacklevel = 2 ,
1158+ )
11581159
1159- class Point2D(TypedDict):
1160- x: int # the "x" key must always be present (Required is the default)
1161- y: NotRequired[int] # the "y" key can be omitted
1160+ ns = {'__annotations__' : dict (fields )}
1161+ module = _caller (depth = 5 if __typing_is_inline__ else 2 )
1162+ if module is not None :
1163+ # Setting correct module is necessary to make typed dict classes
1164+ # pickleable.
1165+ ns ['__module__' ] = module
11621166
1163- See PEP 655 for more details on Required and NotRequired.
1164- """
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"
1167+ td = _TypedDictMeta (typename , (), ns , total = total , closed = closed ,
1168+ extra_items = extra_items )
1169+ td .__orig_bases__ = (TypedDict ,)
1170+ return td
11701171
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 ,
1172+ @_ensure_subclassable (lambda bases : (_TypedDict ,))
1173+ @_TypedDictSpecialForm
1174+ def TypedDict (self , args ):
1175+ # This runs when creating inline TypedDicts:
1176+ if not isinstance (args , tuple ):
1177+ args = (args ,)
1178+ if len (args ) != 1 or not isinstance (args [0 ], dict ):
1179+ raise TypeError (
1180+ "TypedDict[...] should be used with a single dict argument"
11991181 )
12001182
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
1206-
1207- td = _TypedDictMeta (typename , (), ns , total = total , closed = closed ,
1208- extra_items = extra_items )
1209- td .__orig_bases__ = (TypedDict ,)
1210- return td
1183+ # Delegate to _TypedDictSpecialForm.__call__:
1184+ return self ("<inlined TypedDict>" , args [0 ], __typing_is_inline__ = True )
12111185
12121186 if hasattr (typing , "_TypedDictMeta" ):
12131187 _TYPEDDICT_TYPES = (typing ._TypedDictMeta , _TypedDictMeta )
0 commit comments