x: int
y: int
+class Point2DGeneric(Generic[T], TypedDict):
+ a: T
+ b: T
+
class Bar(_typed_dict_helper.Foo, total=False):
b: int
+class BarGeneric(_typed_dict_helper.FooGeneric[T], total=False):
+ b: int
+
class LabelPoint2D(Point2D, Label): ...
class Options(TypedDict, total=False):
EmpDnew = pickle.loads(ZZ)
self.assertEqual(EmpDnew({'name': 'jane', 'id': 37}), jane)
+ def test_pickle_generic(self):
+ point = Point2DGeneric(a=5.0, b=3.0)
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ z = pickle.dumps(point, proto)
+ point2 = pickle.loads(z)
+ self.assertEqual(point2, point)
+ self.assertEqual(point2, {'a': 5.0, 'b': 3.0})
+ ZZ = pickle.dumps(Point2DGeneric, proto)
+ Point2DGenericNew = pickle.loads(ZZ)
+ self.assertEqual(Point2DGenericNew({'a': 5.0, 'b': 3.0}), point)
+
def test_optional(self):
EmpD = TypedDict('EmpD', {'name': str, 'id': int})
{'a': typing.Optional[int], 'b': int}
)
+ def test_get_type_hints_generic(self):
+ self.assertEqual(
+ get_type_hints(BarGeneric),
+ {'a': typing.Optional[T], 'b': int}
+ )
+
+ class FooBarGeneric(BarGeneric[int]):
+ c: str
+
+ self.assertEqual(
+ get_type_hints(FooBarGeneric),
+ {'a': typing.Optional[T], 'b': int, 'c': str}
+ )
+
+ def test_generic_inheritance(self):
+ class A(TypedDict, Generic[T]):
+ a: T
+
+ self.assertEqual(A.__bases__, (Generic, dict))
+ self.assertEqual(A.__orig_bases__, (TypedDict, Generic[T]))
+ self.assertEqual(A.__mro__, (A, Generic, dict, object))
+ self.assertEqual(A.__parameters__, (T,))
+ self.assertEqual(A[str].__parameters__, ())
+ self.assertEqual(A[str].__args__, (str,))
+
+ class A2(Generic[T], TypedDict):
+ a: T
+
+ self.assertEqual(A2.__bases__, (Generic, dict))
+ self.assertEqual(A2.__orig_bases__, (Generic[T], TypedDict))
+ self.assertEqual(A2.__mro__, (A2, Generic, dict, object))
+ self.assertEqual(A2.__parameters__, (T,))
+ self.assertEqual(A2[str].__parameters__, ())
+ self.assertEqual(A2[str].__args__, (str,))
+
+ class B(A[KT], total=False):
+ b: KT
+
+ self.assertEqual(B.__bases__, (Generic, dict))
+ self.assertEqual(B.__orig_bases__, (A[KT],))
+ self.assertEqual(B.__mro__, (B, Generic, dict, object))
+ self.assertEqual(B.__parameters__, (KT,))
+ self.assertEqual(B.__total__, False)
+ self.assertEqual(B.__optional_keys__, frozenset(['b']))
+ self.assertEqual(B.__required_keys__, frozenset(['a']))
+
+ self.assertEqual(B[str].__parameters__, ())
+ self.assertEqual(B[str].__args__, (str,))
+ self.assertEqual(B[str].__origin__, B)
+
+ class C(B[int]):
+ c: int
+
+ self.assertEqual(C.__bases__, (Generic, dict))
+ self.assertEqual(C.__orig_bases__, (B[int],))
+ self.assertEqual(C.__mro__, (C, Generic, dict, object))
+ self.assertEqual(C.__parameters__, ())
+ self.assertEqual(C.__total__, True)
+ self.assertEqual(C.__optional_keys__, frozenset(['b']))
+ self.assertEqual(C.__required_keys__, frozenset(['a', 'c']))
+ assert C.__annotations__ == {
+ 'a': T,
+ 'b': KT,
+ 'c': int,
+ }
+ with self.assertRaises(TypeError):
+ C[str]
+
+
+ class Point3D(Point2DGeneric[T], Generic[T, KT]):
+ c: KT
+
+ self.assertEqual(Point3D.__bases__, (Generic, dict))
+ self.assertEqual(Point3D.__orig_bases__, (Point2DGeneric[T], Generic[T, KT]))
+ self.assertEqual(Point3D.__mro__, (Point3D, Generic, dict, object))
+ self.assertEqual(Point3D.__parameters__, (T, KT))
+ self.assertEqual(Point3D.__total__, True)
+ self.assertEqual(Point3D.__optional_keys__, frozenset())
+ self.assertEqual(Point3D.__required_keys__, frozenset(['a', 'b', 'c']))
+ assert Point3D.__annotations__ == {
+ 'a': T,
+ 'b': T,
+ 'c': KT,
+ }
+ self.assertEqual(Point3D[int, str].__origin__, Point3D)
+
+ with self.assertRaises(TypeError):
+ Point3D[int]
+
+ with self.assertRaises(TypeError):
+ class Point3D(Point2DGeneric[T], Generic[KT]):
+ c: KT
+
+ def test_implicit_any_inheritance(self):
+ class A(TypedDict, Generic[T]):
+ a: T
+
+ class B(A[KT], total=False):
+ b: KT
+
+ class WithImplicitAny(B):
+ c: int
+
+ self.assertEqual(WithImplicitAny.__bases__, (Generic, dict,))
+ self.assertEqual(WithImplicitAny.__mro__, (WithImplicitAny, Generic, dict, object))
+ # Consistent with GenericTests.test_implicit_any
+ self.assertEqual(WithImplicitAny.__parameters__, ())
+ self.assertEqual(WithImplicitAny.__total__, True)
+ self.assertEqual(WithImplicitAny.__optional_keys__, frozenset(['b']))
+ self.assertEqual(WithImplicitAny.__required_keys__, frozenset(['a', 'c']))
+ assert WithImplicitAny.__annotations__ == {
+ 'a': T,
+ 'b': KT,
+ 'c': int,
+ }
+ with self.assertRaises(TypeError):
+ WithImplicitAny[str]
+
def test_non_generic_subscript(self):
# For backward compatibility, subscription works
# on arbitrary TypedDict types.
if '__orig_bases__' in cls.__dict__:
error = Generic in cls.__orig_bases__
else:
- error = Generic in cls.__bases__ and cls.__name__ != 'Protocol'
+ error = (Generic in cls.__bases__ and
+ cls.__name__ != 'Protocol' and
+ type(cls) != _TypedDictMeta)
if error:
raise TypeError("Cannot inherit from plain Generic")
if '__orig_bases__' in cls.__dict__:
Subclasses and instances of TypedDict return actual dictionaries.
"""
for base in bases:
- if type(base) is not _TypedDictMeta:
+ if type(base) is not _TypedDictMeta and base is not Generic:
raise TypeError('cannot inherit from both a TypedDict type '
'and a non-TypedDict base class')
- tp_dict = type.__new__(_TypedDictMeta, name, (dict,), ns)
+
+ if any(issubclass(b, Generic) for b in bases):
+ generic_base = (Generic,)
+ else:
+ generic_base = ()
+
+ tp_dict = type.__new__(_TypedDictMeta, name, (*generic_base, dict), ns)
annotations = {}
own_annotations = ns.get('__annotations__', {})
- own_annotation_keys = set(own_annotations.keys())
msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
own_annotations = {
n: _type_check(tp, msg, module=tp_dict.__module__)