mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
bpo-44863: Allow generic typing.TypedDict (#27663)
Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Co-authored-by: Yurii Karabas <1998uriyyo@gmail.com> Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
6c7249f265
commit
f6f36cc269
6 changed files with 172 additions and 7 deletions
|
@ -4530,9 +4530,16 @@ class Point2D(TypedDict):
|
|||
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):
|
||||
|
@ -5890,6 +5897,17 @@ class TypedDictTests(BaseTestCase):
|
|||
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})
|
||||
|
||||
|
@ -6074,6 +6092,124 @@ class TypedDictTests(BaseTestCase):
|
|||
{'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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue