|
| 1 | +# pyright: ignore |
1 | 2 | import abc |
2 | 3 | import builtins |
3 | 4 | import collections |
@@ -1105,17 +1106,73 @@ def __subclasscheck__(cls, other): |
1105 | 1106 |
|
1106 | 1107 | _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {}) |
1107 | 1108 |
|
| 1109 | + |
| 1110 | + class _TypedDictSpecialForm(_ExtensionsSpecialForm, _root=True): |
| 1111 | + def __call__( |
| 1112 | + self, |
| 1113 | + typename, |
| 1114 | + fields=_marker, |
| 1115 | + /, |
| 1116 | + *, |
| 1117 | + total=True, |
| 1118 | + closed=None, |
| 1119 | + extra_items=NoExtraItems, |
| 1120 | + __typing_is_inline__=False, |
| 1121 | + **kwargs |
| 1122 | + ): |
| 1123 | + if fields is _marker or fields is None: |
| 1124 | + if fields is _marker: |
| 1125 | + deprecated_thing = ( |
| 1126 | + "Failing to pass a value for the 'fields' parameter" |
| 1127 | + ) |
| 1128 | + else: |
| 1129 | + deprecated_thing = "Passing `None` as the 'fields' parameter" |
| 1130 | + |
| 1131 | + example = f"`{typename} = TypedDict({typename!r}, {{}})`" |
| 1132 | + deprecation_msg = ( |
| 1133 | + f"{deprecated_thing} is deprecated and will be disallowed in " |
| 1134 | + "Python 3.15. To create a TypedDict class with 0 fields " |
| 1135 | + "using the functional syntax, pass an empty dictionary, e.g. " |
| 1136 | + ) + example + "." |
| 1137 | + warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2) |
| 1138 | + # Support a field called "closed" |
| 1139 | + if closed is not False and closed is not True and closed is not None: |
| 1140 | + kwargs["closed"] = closed |
| 1141 | + closed = None |
| 1142 | + # Or "extra_items" |
| 1143 | + if extra_items is not NoExtraItems: |
| 1144 | + kwargs["extra_items"] = extra_items |
| 1145 | + extra_items = NoExtraItems |
| 1146 | + fields = kwargs |
| 1147 | + elif kwargs: |
| 1148 | + raise TypeError("TypedDict takes either a dict or keyword arguments," |
| 1149 | + " but not both") |
| 1150 | + if kwargs: |
| 1151 | + if sys.version_info >= (3, 13): |
| 1152 | + raise TypeError("TypedDict takes no keyword arguments") |
| 1153 | + warnings.warn( |
| 1154 | + "The kwargs-based syntax for TypedDict definitions is deprecated " |
| 1155 | + "in Python 3.11, will be removed in Python 3.13, and may not be " |
| 1156 | + "understood by third-party type checkers.", |
| 1157 | + DeprecationWarning, |
| 1158 | + stacklevel=2, |
| 1159 | + ) |
| 1160 | + |
| 1161 | + ns = {'__annotations__': dict(fields)} |
| 1162 | + module = _caller(depth=5 if __typing_is_inline__ else 2) |
| 1163 | + if module is not None: |
| 1164 | + # Setting correct module is necessary to make typed dict classes |
| 1165 | + # pickleable. |
| 1166 | + ns['__module__'] = module |
| 1167 | + |
| 1168 | + td = _TypedDictMeta(typename, (), ns, total=total, closed=closed, |
| 1169 | + extra_items=extra_items) |
| 1170 | + td.__orig_bases__ = (TypedDict,) |
| 1171 | + return td |
| 1172 | + |
1108 | 1173 | @_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 | | - ): |
| 1174 | + @_TypedDictSpecialForm |
| 1175 | + def TypedDict(self, args): |
1119 | 1176 | """A simple typed namespace. At runtime it is equivalent to a plain dict. |
1120 | 1177 |
|
1121 | 1178 | TypedDict creates a dictionary type such that a type checker will expect all |
@@ -1162,52 +1219,16 @@ class Point2D(TypedDict): |
1162 | 1219 |
|
1163 | 1220 | See PEP 655 for more details on Required and NotRequired. |
1164 | 1221 | """ |
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" |
1170 | | - |
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, |
| 1222 | + # This runs when creating inline TypedDicts: |
| 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" |
1199 | 1228 | ) |
1200 | 1229 |
|
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 |
| 1230 | + # Delegate to _TypedDictSpecialForm.__call__: |
| 1231 | + return self("<inlined TypedDict>", args[0], __typing_is_inline__=True) |
1211 | 1232 |
|
1212 | 1233 | if hasattr(typing, "_TypedDictMeta"): |
1213 | 1234 | _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta) |
|
0 commit comments