From 5bdeab67a609308b59f53e7b5f738269eaf28095 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Fri, 25 Mar 2022 21:59:46 +0000 Subject: [PATCH 01/10] Implement pickling of TypeVarTuples --- Lib/test/test_typing.py | 137 ++++++++++++++++++++++++++++++++++++++++ Lib/typing.py | 25 ++++++-- 2 files changed, 156 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 0e28655296d14f..c14e7a0a00dc02 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -838,6 +838,143 @@ class C(Generic[Unpack[Ts]]): pass self.assertNotEqual(C[Unpack[Ts1]], C[Unpack[Ts2]]) +class TypeVarTuplePicklingTests(BaseTestCase): + # These are slightly awkward tests to run, because TypeVarTuples (and things + # made generic using TypeVarTuples) are only picklable if defined in the + # global scope. We therefore need to push various things defined in these + # tests into the global scope with `global` statements at the start of each + # test. + + def test_typevartuple_can_be_pickled_without_error(self): + global Ts # See explanation at start of class. + Ts = TypeVarTuple('Ts') + pickle.dumps(Ts) + + def test_pickling_then_unpickling_results_in_equality(self): + global Ts1 # See explanation at start of class. + Ts1 = TypeVarTuple('Ts1') + Ts2 = pickle.loads(pickle.dumps(Ts1)) + self.assertEqual(Ts1, Ts2) + + def test_typevartuple_is_immutable_by_pickle(self): + global Ts1 # See explanation at start of class. + Ts1 = TypeVarTuple('Ts1') + Ts2 = pickle.loads(pickle.dumps(Ts1)) + self.assertIs(Ts1, Ts2) + + def test_unpacked_typevartuple_can_be_pickled_without_error(self): + global Ts # See explanation at start of class. + Ts = TypeVarTuple('Ts') + pickle.dumps(Unpack[Ts]) + + def test_pickling_then_unpickling_typevartuple_results_in_equality(self): + global Ts # See explanation at start of class. + Ts = TypeVarTuple('Ts') + unpacked1 = Unpack[Ts] + unpacked2 = pickle.loads(pickle.dumps(unpacked1)) + self.assertEqual(unpacked1, unpacked2) + + def test_variadic_class_can_be_pickled_without_error(self): + global T, Ts, A, B # See explanation at start of class. + T = TypeVar('T') + Ts = TypeVarTuple('Ts') + + class A(Generic[Unpack[Ts]]): pass + pickle.dumps(A) + + class B(Generic[T, Unpack[Ts]]): pass + pickle.dumps(B) + + def test_pickling_then_unpickling_variadic_class_results_in_equality(self): + global T, Ts, A1, B1 # See explanation at start of class. + T = TypeVar('T') + Ts = TypeVarTuple('Ts') + + class A1(Generic[Unpack[Ts]]): pass + A2 = pickle.loads(pickle.dumps(A1)) + self.assertEqual(A1, A2) + + class B1(Generic[T, Unpack[Ts]]): pass + B2 = pickle.loads(pickle.dumps(B1)) + self.assertEqual(B1, B2) + + def test_tuple_with_typevartuple_can_be_pickled_without_error(self): + global T, Ts # See explanation at start of class. + T = TypeVarTuple('T') + Ts = TypeVarTuple('Ts') + + a = Tuple[Unpack[Ts]] + pickle.dumps(a) + + b = Tuple[T, Unpack[Ts]] + pickle.dumps(b) + + c = Tuple[int, Unpack[Ts]] + pickle.dumps(c) + + def test_pickling_then_unpickling_tuple_with_typevartuple_equality(self): + global T, Ts # See explanation at start of class. + T = TypeVar('T') + Ts = TypeVarTuple('Ts') + + a1 = Tuple[Unpack[Ts]] + a2 = pickle.loads(pickle.dumps(a1)) + self.assertEqual(a1, a2) + + a1 = Tuple[T, Unpack[Ts]] + a2 = pickle.loads(pickle.dumps(a1)) + self.assertEqual(a1, a2) + + a1 = Tuple[int, Unpack[Ts]] + a2 = pickle.loads(pickle.dumps(a1)) + self.assertEqual(a1, a2) + + def test_variadic_func_can_be_pickled_without_error(self): + global T, Ts, a, b, c, d, e # See explanation at start of class. + T = TypeVar('T') + Ts = TypeVarTuple('Ts') + + def a(*args: Unpack[Ts]): pass + pickle.dumps(a) + + def b(*args: Unpack[Tuple[int]]): pass + pickle.dumps(b) + + def c(*args: Unpack[Tuple[int, ...]]): pass + pickle.dumps(c) + + def d(*args: Unpack[Tuple[int, Unpack[Ts]]]): pass + pickle.dumps(d) + + def e(*args: Unpack[Tuple[T, Unpack[Ts]]]): pass + pickle.dumps(e) + + def test_pickling_then_unpickling_variadic_func_results_in_equality(self): + global T, Ts, a1, b1, c1, d1, e1 # See explanation at start of class. + T = TypeVar('T') + Ts = TypeVarTuple('Ts') + + def a1(*args: Unpack[Ts]): pass + a2 = pickle.loads(pickle.dumps(a1)) + self.assertEqual(a1, a2) + + def b1(*args: Unpack[Tuple[int]]): pass + b2 = pickle.loads(pickle.dumps(b1)) + self.assertEqual(b1, b2) + + def c1(*args: Unpack[Tuple[int, ...]]): pass + c2 = pickle.loads(pickle.dumps(c1)) + self.assertEqual(c1, c2) + + def d1(*args: Unpack[Tuple[int, Unpack[Ts]]]): pass + d2 = pickle.loads(pickle.dumps(d1)) + self.assertEqual(d1, d2) + + def e1(*args: Unpack[Tuple[T, Unpack[Ts]]]): pass + e2 = pickle.loads(pickle.dumps(e1)) + self.assertEqual(e1, e2) + + class UnionTests(BaseTestCase): def test_basics(self): diff --git a/Lib/typing.py b/Lib/typing.py index 64b348e0b9d5ca..f61c3a404e1e31 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -811,6 +811,13 @@ def _is_typevar_like(x: Any) -> bool: return isinstance(x, (TypeVar, ParamSpec)) or _is_unpacked_typevartuple(x) +class _PickleUsingNameMixin: + """Mixin enabling pickling based on self.__name__.""" + + def __reduce__(self): + return self.__name__ + + class _BoundVarianceMixin: """Mixin giving __init__ bound and variance arguments. @@ -847,11 +854,9 @@ def __repr__(self): prefix = '~' return prefix + self.__name__ - def __reduce__(self): - return self.__name__ - -class TypeVar(_Final, _Immutable, _BoundVarianceMixin, _root=True): +class TypeVar(_Final, _Immutable, _BoundVarianceMixin, _PickleUsingNameMixin, + _root=True): """Type variable. Usage:: @@ -915,7 +920,7 @@ def __typing_subst__(self, arg): return arg -class TypeVarTuple(_Final, _Immutable, _root=True): +class TypeVarTuple(_Final, _Immutable, _PickleUsingNameMixin, _root=True): """Type variable tuple. Usage: @@ -936,11 +941,18 @@ class C(Generic[*Ts]): ... C[()] # Even this is fine For more details, see PEP 646. + + Note that only type variables tuples defined in global scope can be pickled. """ def __init__(self, name): self.__name__ = name + # Used for pickling. + def_mod = _caller() + if def_mod != 'typing': + self.__module__ = def_mod + def __iter__(self): yield Unpack[self] @@ -999,7 +1011,8 @@ def __eq__(self, other): return self.__origin__ == other.__origin__ -class ParamSpec(_Final, _Immutable, _BoundVarianceMixin, _root=True): +class ParamSpec(_Final, _Immutable, _BoundVarianceMixin, _PickleUsingNameMixin, + _root=True): """Parameter specification variable. Usage:: From 2ea4127690b4be44eecf56cbd9fe2ca7f158d643 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 26 Mar 2022 09:51:05 +0000 Subject: [PATCH 02/10] Remove redundant tests --- Lib/test/test_typing.py | 63 +---------------------------------------- 1 file changed, 1 insertion(+), 62 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c14e7a0a00dc02..e241aa37e8bf1d 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -845,46 +845,19 @@ class TypeVarTuplePicklingTests(BaseTestCase): # tests into the global scope with `global` statements at the start of each # test. - def test_typevartuple_can_be_pickled_without_error(self): - global Ts # See explanation at start of class. - Ts = TypeVarTuple('Ts') - pickle.dumps(Ts) - def test_pickling_then_unpickling_results_in_equality(self): global Ts1 # See explanation at start of class. Ts1 = TypeVarTuple('Ts1') Ts2 = pickle.loads(pickle.dumps(Ts1)) self.assertEqual(Ts1, Ts2) - def test_typevartuple_is_immutable_by_pickle(self): - global Ts1 # See explanation at start of class. - Ts1 = TypeVarTuple('Ts1') - Ts2 = pickle.loads(pickle.dumps(Ts1)) - self.assertIs(Ts1, Ts2) - - def test_unpacked_typevartuple_can_be_pickled_without_error(self): - global Ts # See explanation at start of class. - Ts = TypeVarTuple('Ts') - pickle.dumps(Unpack[Ts]) - - def test_pickling_then_unpickling_typevartuple_results_in_equality(self): + def test_pickling_then_unpickling_unpacked_results_in_equality(self): global Ts # See explanation at start of class. Ts = TypeVarTuple('Ts') unpacked1 = Unpack[Ts] unpacked2 = pickle.loads(pickle.dumps(unpacked1)) self.assertEqual(unpacked1, unpacked2) - def test_variadic_class_can_be_pickled_without_error(self): - global T, Ts, A, B # See explanation at start of class. - T = TypeVar('T') - Ts = TypeVarTuple('Ts') - - class A(Generic[Unpack[Ts]]): pass - pickle.dumps(A) - - class B(Generic[T, Unpack[Ts]]): pass - pickle.dumps(B) - def test_pickling_then_unpickling_variadic_class_results_in_equality(self): global T, Ts, A1, B1 # See explanation at start of class. T = TypeVar('T') @@ -898,20 +871,6 @@ class B1(Generic[T, Unpack[Ts]]): pass B2 = pickle.loads(pickle.dumps(B1)) self.assertEqual(B1, B2) - def test_tuple_with_typevartuple_can_be_pickled_without_error(self): - global T, Ts # See explanation at start of class. - T = TypeVarTuple('T') - Ts = TypeVarTuple('Ts') - - a = Tuple[Unpack[Ts]] - pickle.dumps(a) - - b = Tuple[T, Unpack[Ts]] - pickle.dumps(b) - - c = Tuple[int, Unpack[Ts]] - pickle.dumps(c) - def test_pickling_then_unpickling_tuple_with_typevartuple_equality(self): global T, Ts # See explanation at start of class. T = TypeVar('T') @@ -929,26 +888,6 @@ def test_pickling_then_unpickling_tuple_with_typevartuple_equality(self): a2 = pickle.loads(pickle.dumps(a1)) self.assertEqual(a1, a2) - def test_variadic_func_can_be_pickled_without_error(self): - global T, Ts, a, b, c, d, e # See explanation at start of class. - T = TypeVar('T') - Ts = TypeVarTuple('Ts') - - def a(*args: Unpack[Ts]): pass - pickle.dumps(a) - - def b(*args: Unpack[Tuple[int]]): pass - pickle.dumps(b) - - def c(*args: Unpack[Tuple[int, ...]]): pass - pickle.dumps(c) - - def d(*args: Unpack[Tuple[int, Unpack[Ts]]]): pass - pickle.dumps(d) - - def e(*args: Unpack[Tuple[T, Unpack[Ts]]]): pass - pickle.dumps(e) - def test_pickling_then_unpickling_variadic_func_results_in_equality(self): global T, Ts, a1, b1, c1, d1, e1 # See explanation at start of class. T = TypeVar('T') From f234214190a57e013b7e8abee3dc0844a470e581 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 28 Mar 2022 18:49:28 +0100 Subject: [PATCH 03/10] Test all pickle protocol versions --- Lib/test/test_typing.py | 59 ++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index e241aa37e8bf1d..6090da7ec4e74b 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -1,6 +1,6 @@ import contextlib import collections -from functools import lru_cache +from functools import lru_cache, wraps import inspect import pickle import re @@ -67,6 +67,18 @@ def clear_caches(self): f() +def all_pickle_protocols(test_func): + """Runs `test_func` with various values for `proto` argument.""" + + @wraps(test_func) + def wrapper(self): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(pickle_proto=proto): + test_func(self, proto=proto) + + return wrapper + + class Employee: pass @@ -845,72 +857,83 @@ class TypeVarTuplePicklingTests(BaseTestCase): # tests into the global scope with `global` statements at the start of each # test. - def test_pickling_then_unpickling_results_in_equality(self): + @all_pickle_protocols + def test_pickling_then_unpickling_results_in_equality(self, proto): global Ts1 # See explanation at start of class. Ts1 = TypeVarTuple('Ts1') - Ts2 = pickle.loads(pickle.dumps(Ts1)) + Ts2 = pickle.loads(pickle.dumps(Ts1, proto)) self.assertEqual(Ts1, Ts2) - def test_pickling_then_unpickling_unpacked_results_in_equality(self): + @all_pickle_protocols + def test_pickling_then_unpickling_unpacked_results_in_equality(self, proto): global Ts # See explanation at start of class. Ts = TypeVarTuple('Ts') unpacked1 = Unpack[Ts] - unpacked2 = pickle.loads(pickle.dumps(unpacked1)) + unpacked2 = pickle.loads(pickle.dumps(unpacked1, proto)) self.assertEqual(unpacked1, unpacked2) - def test_pickling_then_unpickling_variadic_class_results_in_equality(self): + @all_pickle_protocols + def test_pickling_then_unpickling_variadic_class_results_in_equality( + self, proto + ): global T, Ts, A1, B1 # See explanation at start of class. T = TypeVar('T') Ts = TypeVarTuple('Ts') class A1(Generic[Unpack[Ts]]): pass - A2 = pickle.loads(pickle.dumps(A1)) + A2 = pickle.loads(pickle.dumps(A1, proto)) self.assertEqual(A1, A2) class B1(Generic[T, Unpack[Ts]]): pass - B2 = pickle.loads(pickle.dumps(B1)) + B2 = pickle.loads(pickle.dumps(B1, proto)) self.assertEqual(B1, B2) - def test_pickling_then_unpickling_tuple_with_typevartuple_equality(self): + @all_pickle_protocols + def test_pickling_then_unpickling_tuple_with_typevartuple_equality( + self, proto + ): global T, Ts # See explanation at start of class. T = TypeVar('T') Ts = TypeVarTuple('Ts') a1 = Tuple[Unpack[Ts]] - a2 = pickle.loads(pickle.dumps(a1)) + a2 = pickle.loads(pickle.dumps(a1, proto)) self.assertEqual(a1, a2) a1 = Tuple[T, Unpack[Ts]] - a2 = pickle.loads(pickle.dumps(a1)) + a2 = pickle.loads(pickle.dumps(a1, proto)) self.assertEqual(a1, a2) a1 = Tuple[int, Unpack[Ts]] - a2 = pickle.loads(pickle.dumps(a1)) + a2 = pickle.loads(pickle.dumps(a1, proto)) self.assertEqual(a1, a2) - def test_pickling_then_unpickling_variadic_func_results_in_equality(self): + @all_pickle_protocols + def test_pickling_then_unpickling_variadic_func_results_in_equality( + self, proto + ): global T, Ts, a1, b1, c1, d1, e1 # See explanation at start of class. T = TypeVar('T') Ts = TypeVarTuple('Ts') def a1(*args: Unpack[Ts]): pass - a2 = pickle.loads(pickle.dumps(a1)) + a2 = pickle.loads(pickle.dumps(a1, proto)) self.assertEqual(a1, a2) def b1(*args: Unpack[Tuple[int]]): pass - b2 = pickle.loads(pickle.dumps(b1)) + b2 = pickle.loads(pickle.dumps(b1, proto)) self.assertEqual(b1, b2) def c1(*args: Unpack[Tuple[int, ...]]): pass - c2 = pickle.loads(pickle.dumps(c1)) + c2 = pickle.loads(pickle.dumps(c1, proto)) self.assertEqual(c1, c2) def d1(*args: Unpack[Tuple[int, Unpack[Ts]]]): pass - d2 = pickle.loads(pickle.dumps(d1)) + d2 = pickle.loads(pickle.dumps(d1, proto)) self.assertEqual(d1, d2) def e1(*args: Unpack[Tuple[T, Unpack[Ts]]]): pass - e2 = pickle.loads(pickle.dumps(e1)) + e2 = pickle.loads(pickle.dumps(e1, proto)) self.assertEqual(e1, e2) From de682324fae78fef09b4530dba92fc0316bd2920 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 28 Mar 2022 18:54:04 +0100 Subject: [PATCH 04/10] Switch other tests to use @all_pickle_protocols too --- Lib/test/test_typing.py | 194 ++++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 6090da7ec4e74b..61e041977081eb 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -1235,15 +1235,15 @@ def test_weakref(self): alias = Callable[[int, str], float] self.assertEqual(weakref.ref(alias)(), alias) - def test_pickle(self): + @all_pickle_protocols + def test_pickle(self, proto): Callable = self.Callable alias = Callable[[int, str], float] - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - s = pickle.dumps(alias, proto) - loaded = pickle.loads(s) - self.assertEqual(alias.__origin__, loaded.__origin__) - self.assertEqual(alias.__args__, loaded.__args__) - self.assertEqual(alias.__parameters__, loaded.__parameters__) + s = pickle.dumps(alias, proto) + loaded = pickle.loads(s) + self.assertEqual(alias.__origin__, loaded.__origin__) + self.assertEqual(alias.__args__, loaded.__args__) + self.assertEqual(alias.__parameters__, loaded.__parameters__) def test_var_substitution(self): Callable = self.Callable @@ -2218,7 +2218,8 @@ class P(Protocol): Alias2 = typing.Union[P, typing.Iterable] self.assertEqual(Alias, Alias2) - def test_protocols_pickleable(self): + @all_pickle_protocols + def test_protocols_pickleable(self, proto): global P, CP # pickle wants to reference the class by name T = TypeVar('T') @@ -2232,20 +2233,20 @@ class CP(P[int]): c = CP() c.foo = 42 c.bar = 'abc' - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(c, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.x, 1) - self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) - s = pickle.dumps(P) - D = pickle.loads(s) - class E: - x = 1 + z = pickle.dumps(c, proto) + x = pickle.loads(z) + self.assertEqual(x.foo, 42) + self.assertEqual(x.bar, 'abc') + self.assertEqual(x.x, 1) + self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) + s = pickle.dumps(P) + D = pickle.loads(s) - self.assertIsInstance(E(), D) + class E: + x = 1 + + self.assertIsInstance(E(), D) def test_supports_int(self): self.assertIsSubclass(int, typing.SupportsInt) @@ -2844,7 +2845,8 @@ def test_all_repr_eq_any(self): self.assertNotEqual(repr(base), '') self.assertEqual(base, base) - def test_pickle(self): + @all_pickle_protocols + def test_pickle(self, proto): global C # pickle wants to reference the class by name T = TypeVar('T') @@ -2857,27 +2859,26 @@ class C(B[int]): c = C() c.foo = 42 c.bar = 'abc' - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(c, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) + + z = pickle.dumps(c, proto) + x = pickle.loads(z) + self.assertEqual(x.foo, 42) + self.assertEqual(x.bar, 'abc') + self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) + samples = [Any, Union, Tuple, Callable, ClassVar, Union[int, str], ClassVar[List], Tuple[int, ...], Callable[[str], bytes], typing.DefaultDict, typing.FrozenSet[int]] for s in samples: - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(s, proto) - x = pickle.loads(z) - self.assertEqual(s, x) + z = pickle.dumps(s, proto) + x = pickle.loads(z) + self.assertEqual(s, x) more_samples = [List, typing.Iterable, typing.Type, List[int], typing.Type[typing.Mapping], typing.AbstractSet[Tuple[int, str]]] for s in more_samples: - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(s, proto) - x = pickle.loads(z) - self.assertEqual(s, x) + z = pickle.dumps(s, proto) + x = pickle.loads(z) + self.assertEqual(s, x) def test_copy_and_deepcopy(self): T = TypeVar('T') @@ -2891,7 +2892,8 @@ class Node(Generic[T]): ... self.assertEqual(t, copy(t)) self.assertEqual(t, deepcopy(t)) - def test_immutability_by_copy_and_pickle(self): + @all_pickle_protocols + def test_immutability_by_copy_and_pickle(self, proto): # Special forms like Union, Any, etc., generic aliases to containers like List, # Mapping, etc., and type variabcles are considered immutable by copy and pickle. global TP, TPB, TPV, PP # for pickle @@ -2905,8 +2907,7 @@ def test_immutability_by_copy_and_pickle(self): with self.subTest(thing=X): self.assertIs(copy(X), X) self.assertIs(deepcopy(X), X) - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - self.assertIs(pickle.loads(pickle.dumps(X, proto)), X) + self.assertIs(pickle.loads(pickle.dumps(X, proto)), X) del TP, TPB, TPV, PP # Check that local type variables are copyable. @@ -4977,20 +4978,20 @@ def test_repr(self): self.assertEqual(repr(self.UserName), f'{__name__}.{self.__class__.__qualname__}.UserName') - def test_pickle(self): + @all_pickle_protocols + def test_pickle(self, proto): UserAge = self.module.NewType('UserAge', float) - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - with self.subTest(proto=proto): - pickled = pickle.dumps(UserId, proto) - loaded = pickle.loads(pickled) - self.assertIs(loaded, UserId) - pickled = pickle.dumps(self.UserName, proto) - loaded = pickle.loads(pickled) - self.assertIs(loaded, self.UserName) + pickled = pickle.dumps(UserId, proto) + loaded = pickle.loads(pickled) + self.assertIs(loaded, UserId) + + pickled = pickle.dumps(self.UserName, proto) + loaded = pickle.loads(pickled) + self.assertIs(loaded, self.UserName) - with self.assertRaises(pickle.PicklingError): - pickle.dumps(UserAge, proto) + with self.assertRaises(pickle.PicklingError): + pickle.dumps(UserAge, proto) def test_missing__name__(self): code = ("import typing\n" @@ -5142,17 +5143,18 @@ def test_namedtuple_errors(self): with self.assertRaises(TypeError): NamedTuple(typename='Emp', name=str, id=int) - def test_copy_and_pickle(self): + @all_pickle_protocols + def test_copy_and_pickle(self, proto): global Emp # pickle wants to reference the class by name Emp = NamedTuple('Emp', [('name', str), ('cool', int)]) for cls in Emp, CoolEmployee, self.NestedEmployee: with self.subTest(cls=cls): jane = cls('jane', 37) - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(jane, proto) - jane2 = pickle.loads(z) - self.assertEqual(jane2, jane) - self.assertIsInstance(jane2, cls) + + z = pickle.dumps(jane, proto) + jane2 = pickle.loads(z) + self.assertEqual(jane2, jane) + self.assertIsInstance(jane2, cls) jane2 = copy(jane) self.assertEqual(jane2, jane) @@ -5244,18 +5246,19 @@ def test_py36_class_syntax_usage(self): other = LabelPoint2D(x=0, y=1, label='hi') self.assertEqual(other['label'], 'hi') - def test_pickle(self): + @all_pickle_protocols + def test_pickle(self, proto): global EmpD # pickle wants to reference the class by name EmpD = TypedDict('EmpD', {'name': str, 'id': int}) jane = EmpD({'name': 'jane', 'id': 37}) - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(jane, proto) - jane2 = pickle.loads(z) - self.assertEqual(jane2, jane) - self.assertEqual(jane2, {'name': 'jane', 'id': 37}) - ZZ = pickle.dumps(EmpD, proto) - EmpDnew = pickle.loads(ZZ) - self.assertEqual(EmpDnew({'name': 'jane', 'id': 37}), jane) + + z = pickle.dumps(jane, proto) + jane2 = pickle.loads(z) + self.assertEqual(jane2, jane) + self.assertEqual(jane2, {'name': 'jane', 'id': 37}) + ZZ = pickle.dumps(EmpD, proto) + EmpDnew = pickle.loads(ZZ) + self.assertEqual(EmpDnew({'name': 'jane', 'id': 37}), jane) def test_optional(self): EmpD = TypedDict('EmpD', {'name': str, 'id': int}) @@ -5645,7 +5648,8 @@ def test_too_few_type_args(self): with self.assertRaisesRegex(TypeError, 'at least two arguments'): Annotated[int] - def test_pickle(self): + @all_pickle_protocols + def test_pickle(self, proto): samples = [typing.Any, typing.Union[int, str], typing.Optional[str], Tuple[int, ...], typing.Callable[[str], bytes]] @@ -5653,11 +5657,10 @@ def test_pickle(self): for t in samples: x = Annotated[t, "a"] - for prot in range(pickle.HIGHEST_PROTOCOL + 1): - with self.subTest(protocol=prot, type=t): - pickled = pickle.dumps(x, prot) - restored = pickle.loads(pickled) - self.assertEqual(x, restored) + with self.subTest(type=t): + pickled = pickle.dumps(x, proto) + restored = pickle.loads(pickled) + self.assertEqual(x, restored) global _Annotated_test_G @@ -5668,12 +5671,11 @@ class _Annotated_test_G(Generic[T]): G.foo = 42 G.bar = 'abc' - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(G, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.x, 1) + z = pickle.dumps(G, proto) + x = pickle.loads(z) + self.assertEqual(x.foo, 42) + self.assertEqual(x.bar, 'abc') + self.assertEqual(x.x, 1) def test_subst(self): dec = "a decoration" @@ -6040,7 +6042,8 @@ def test_no_isinstance(self): class SpecialAttrsTests(BaseTestCase): - def test_special_attrs(self): + @all_pickle_protocols + def test_special_attrs(self, proto): cls_to_check = { # ABC classes typing.AbstractSet: 'AbstractSet', @@ -6160,14 +6163,15 @@ def test_special_attrs(self): self.assertEqual(cls.__name__, name, str(cls)) self.assertEqual(cls.__qualname__, name, str(cls)) self.assertEqual(cls.__module__, 'typing', str(cls)) - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - s = pickle.dumps(cls, proto) - loaded = pickle.loads(s) - self.assertIs(cls, loaded) + + s = pickle.dumps(cls, proto) + loaded = pickle.loads(s) + self.assertIs(cls, loaded) TypeName = typing.NewType('SpecialAttrsTests.TypeName', Any) - def test_special_attrs2(self): + @all_pickle_protocols + def test_special_attrs2(self, proto): # Forward refs provide a different introspection API. __name__ and # __qualname__ make little sense for forward refs as they can store # complex typing expressions. @@ -6176,9 +6180,8 @@ def test_special_attrs2(self): self.assertFalse(hasattr(fr, '__qualname__')) self.assertEqual(fr.__module__, 'typing') # Forward refs are currently unpicklable. - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - with self.assertRaises(TypeError) as exc: - pickle.dumps(fr, proto) + with self.assertRaises(TypeError) as exc: + pickle.dumps(fr, proto) self.assertEqual(SpecialAttrsTests.TypeName.__name__, 'TypeName') self.assertEqual( @@ -6190,10 +6193,9 @@ def test_special_attrs2(self): __name__, ) # NewTypes are picklable assuming correct qualname information. - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - s = pickle.dumps(SpecialAttrsTests.TypeName, proto) - loaded = pickle.loads(s) - self.assertIs(SpecialAttrsTests.TypeName, loaded) + s = pickle.dumps(SpecialAttrsTests.TypeName) + loaded = pickle.loads(s) + self.assertIs(SpecialAttrsTests.TypeName, loaded) # Type variables don't support non-global instantiation per PEP 484 # restriction that "The argument to TypeVar() must be a string equal @@ -6203,19 +6205,17 @@ def test_special_attrs2(self): self.assertFalse(hasattr(SpecialAttrsT, '__qualname__')) self.assertEqual(SpecialAttrsT.__module__, __name__) # Module-level type variables are picklable. - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - s = pickle.dumps(SpecialAttrsT, proto) - loaded = pickle.loads(s) - self.assertIs(SpecialAttrsT, loaded) + s = pickle.dumps(SpecialAttrsT, proto) + loaded = pickle.loads(s) + self.assertIs(SpecialAttrsT, loaded) self.assertEqual(SpecialAttrsP.__name__, 'SpecialAttrsP') self.assertFalse(hasattr(SpecialAttrsP, '__qualname__')) self.assertEqual(SpecialAttrsP.__module__, __name__) # Module-level ParamSpecs are picklable. - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - s = pickle.dumps(SpecialAttrsP, proto) - loaded = pickle.loads(s) - self.assertIs(SpecialAttrsP, loaded) + s = pickle.dumps(SpecialAttrsP, proto) + loaded = pickle.loads(s) + self.assertIs(SpecialAttrsP, loaded) def test_genericalias_dir(self): class Foo(Generic[T]): From 69197c2113a138a8d91bd0db786f9b3dde74c5cf Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Wed, 30 Mar 2022 20:41:05 +0100 Subject: [PATCH 05/10] Revert "Switch other tests to use @all_pickle_protocols too" This reverts commit de682324fae78fef09b4530dba92fc0316bd2920. --- Lib/test/test_typing.py | 194 ++++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 61e041977081eb..6090da7ec4e74b 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -1235,15 +1235,15 @@ def test_weakref(self): alias = Callable[[int, str], float] self.assertEqual(weakref.ref(alias)(), alias) - @all_pickle_protocols - def test_pickle(self, proto): + def test_pickle(self): Callable = self.Callable alias = Callable[[int, str], float] - s = pickle.dumps(alias, proto) - loaded = pickle.loads(s) - self.assertEqual(alias.__origin__, loaded.__origin__) - self.assertEqual(alias.__args__, loaded.__args__) - self.assertEqual(alias.__parameters__, loaded.__parameters__) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + s = pickle.dumps(alias, proto) + loaded = pickle.loads(s) + self.assertEqual(alias.__origin__, loaded.__origin__) + self.assertEqual(alias.__args__, loaded.__args__) + self.assertEqual(alias.__parameters__, loaded.__parameters__) def test_var_substitution(self): Callable = self.Callable @@ -2218,8 +2218,7 @@ class P(Protocol): Alias2 = typing.Union[P, typing.Iterable] self.assertEqual(Alias, Alias2) - @all_pickle_protocols - def test_protocols_pickleable(self, proto): + def test_protocols_pickleable(self): global P, CP # pickle wants to reference the class by name T = TypeVar('T') @@ -2233,20 +2232,20 @@ class CP(P[int]): c = CP() c.foo = 42 c.bar = 'abc' + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(c, proto) + x = pickle.loads(z) + self.assertEqual(x.foo, 42) + self.assertEqual(x.bar, 'abc') + self.assertEqual(x.x, 1) + self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) + s = pickle.dumps(P) + D = pickle.loads(s) - z = pickle.dumps(c, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.x, 1) - self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) - s = pickle.dumps(P) - D = pickle.loads(s) - - class E: - x = 1 + class E: + x = 1 - self.assertIsInstance(E(), D) + self.assertIsInstance(E(), D) def test_supports_int(self): self.assertIsSubclass(int, typing.SupportsInt) @@ -2845,8 +2844,7 @@ def test_all_repr_eq_any(self): self.assertNotEqual(repr(base), '') self.assertEqual(base, base) - @all_pickle_protocols - def test_pickle(self, proto): + def test_pickle(self): global C # pickle wants to reference the class by name T = TypeVar('T') @@ -2859,26 +2857,27 @@ class C(B[int]): c = C() c.foo = 42 c.bar = 'abc' - - z = pickle.dumps(c, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) - + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(c, proto) + x = pickle.loads(z) + self.assertEqual(x.foo, 42) + self.assertEqual(x.bar, 'abc') + self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) samples = [Any, Union, Tuple, Callable, ClassVar, Union[int, str], ClassVar[List], Tuple[int, ...], Callable[[str], bytes], typing.DefaultDict, typing.FrozenSet[int]] for s in samples: - z = pickle.dumps(s, proto) - x = pickle.loads(z) - self.assertEqual(s, x) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(s, proto) + x = pickle.loads(z) + self.assertEqual(s, x) more_samples = [List, typing.Iterable, typing.Type, List[int], typing.Type[typing.Mapping], typing.AbstractSet[Tuple[int, str]]] for s in more_samples: - z = pickle.dumps(s, proto) - x = pickle.loads(z) - self.assertEqual(s, x) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(s, proto) + x = pickle.loads(z) + self.assertEqual(s, x) def test_copy_and_deepcopy(self): T = TypeVar('T') @@ -2892,8 +2891,7 @@ class Node(Generic[T]): ... self.assertEqual(t, copy(t)) self.assertEqual(t, deepcopy(t)) - @all_pickle_protocols - def test_immutability_by_copy_and_pickle(self, proto): + def test_immutability_by_copy_and_pickle(self): # Special forms like Union, Any, etc., generic aliases to containers like List, # Mapping, etc., and type variabcles are considered immutable by copy and pickle. global TP, TPB, TPV, PP # for pickle @@ -2907,7 +2905,8 @@ def test_immutability_by_copy_and_pickle(self, proto): with self.subTest(thing=X): self.assertIs(copy(X), X) self.assertIs(deepcopy(X), X) - self.assertIs(pickle.loads(pickle.dumps(X, proto)), X) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + self.assertIs(pickle.loads(pickle.dumps(X, proto)), X) del TP, TPB, TPV, PP # Check that local type variables are copyable. @@ -4978,20 +4977,20 @@ def test_repr(self): self.assertEqual(repr(self.UserName), f'{__name__}.{self.__class__.__qualname__}.UserName') - @all_pickle_protocols - def test_pickle(self, proto): + def test_pickle(self): UserAge = self.module.NewType('UserAge', float) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(proto=proto): + pickled = pickle.dumps(UserId, proto) + loaded = pickle.loads(pickled) + self.assertIs(loaded, UserId) - pickled = pickle.dumps(UserId, proto) - loaded = pickle.loads(pickled) - self.assertIs(loaded, UserId) - - pickled = pickle.dumps(self.UserName, proto) - loaded = pickle.loads(pickled) - self.assertIs(loaded, self.UserName) + pickled = pickle.dumps(self.UserName, proto) + loaded = pickle.loads(pickled) + self.assertIs(loaded, self.UserName) - with self.assertRaises(pickle.PicklingError): - pickle.dumps(UserAge, proto) + with self.assertRaises(pickle.PicklingError): + pickle.dumps(UserAge, proto) def test_missing__name__(self): code = ("import typing\n" @@ -5143,18 +5142,17 @@ def test_namedtuple_errors(self): with self.assertRaises(TypeError): NamedTuple(typename='Emp', name=str, id=int) - @all_pickle_protocols - def test_copy_and_pickle(self, proto): + def test_copy_and_pickle(self): global Emp # pickle wants to reference the class by name Emp = NamedTuple('Emp', [('name', str), ('cool', int)]) for cls in Emp, CoolEmployee, self.NestedEmployee: with self.subTest(cls=cls): jane = cls('jane', 37) - - z = pickle.dumps(jane, proto) - jane2 = pickle.loads(z) - self.assertEqual(jane2, jane) - self.assertIsInstance(jane2, cls) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(jane, proto) + jane2 = pickle.loads(z) + self.assertEqual(jane2, jane) + self.assertIsInstance(jane2, cls) jane2 = copy(jane) self.assertEqual(jane2, jane) @@ -5246,19 +5244,18 @@ def test_py36_class_syntax_usage(self): other = LabelPoint2D(x=0, y=1, label='hi') self.assertEqual(other['label'], 'hi') - @all_pickle_protocols - def test_pickle(self, proto): + def test_pickle(self): global EmpD # pickle wants to reference the class by name EmpD = TypedDict('EmpD', {'name': str, 'id': int}) jane = EmpD({'name': 'jane', 'id': 37}) - - z = pickle.dumps(jane, proto) - jane2 = pickle.loads(z) - self.assertEqual(jane2, jane) - self.assertEqual(jane2, {'name': 'jane', 'id': 37}) - ZZ = pickle.dumps(EmpD, proto) - EmpDnew = pickle.loads(ZZ) - self.assertEqual(EmpDnew({'name': 'jane', 'id': 37}), jane) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(jane, proto) + jane2 = pickle.loads(z) + self.assertEqual(jane2, jane) + self.assertEqual(jane2, {'name': 'jane', 'id': 37}) + ZZ = pickle.dumps(EmpD, proto) + EmpDnew = pickle.loads(ZZ) + self.assertEqual(EmpDnew({'name': 'jane', 'id': 37}), jane) def test_optional(self): EmpD = TypedDict('EmpD', {'name': str, 'id': int}) @@ -5648,8 +5645,7 @@ def test_too_few_type_args(self): with self.assertRaisesRegex(TypeError, 'at least two arguments'): Annotated[int] - @all_pickle_protocols - def test_pickle(self, proto): + def test_pickle(self): samples = [typing.Any, typing.Union[int, str], typing.Optional[str], Tuple[int, ...], typing.Callable[[str], bytes]] @@ -5657,10 +5653,11 @@ def test_pickle(self, proto): for t in samples: x = Annotated[t, "a"] - with self.subTest(type=t): - pickled = pickle.dumps(x, proto) - restored = pickle.loads(pickled) - self.assertEqual(x, restored) + for prot in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=prot, type=t): + pickled = pickle.dumps(x, prot) + restored = pickle.loads(pickled) + self.assertEqual(x, restored) global _Annotated_test_G @@ -5671,11 +5668,12 @@ class _Annotated_test_G(Generic[T]): G.foo = 42 G.bar = 'abc' - z = pickle.dumps(G, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.x, 1) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(G, proto) + x = pickle.loads(z) + self.assertEqual(x.foo, 42) + self.assertEqual(x.bar, 'abc') + self.assertEqual(x.x, 1) def test_subst(self): dec = "a decoration" @@ -6042,8 +6040,7 @@ def test_no_isinstance(self): class SpecialAttrsTests(BaseTestCase): - @all_pickle_protocols - def test_special_attrs(self, proto): + def test_special_attrs(self): cls_to_check = { # ABC classes typing.AbstractSet: 'AbstractSet', @@ -6163,15 +6160,14 @@ def test_special_attrs(self, proto): self.assertEqual(cls.__name__, name, str(cls)) self.assertEqual(cls.__qualname__, name, str(cls)) self.assertEqual(cls.__module__, 'typing', str(cls)) - - s = pickle.dumps(cls, proto) - loaded = pickle.loads(s) - self.assertIs(cls, loaded) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + s = pickle.dumps(cls, proto) + loaded = pickle.loads(s) + self.assertIs(cls, loaded) TypeName = typing.NewType('SpecialAttrsTests.TypeName', Any) - @all_pickle_protocols - def test_special_attrs2(self, proto): + def test_special_attrs2(self): # Forward refs provide a different introspection API. __name__ and # __qualname__ make little sense for forward refs as they can store # complex typing expressions. @@ -6180,8 +6176,9 @@ def test_special_attrs2(self, proto): self.assertFalse(hasattr(fr, '__qualname__')) self.assertEqual(fr.__module__, 'typing') # Forward refs are currently unpicklable. - with self.assertRaises(TypeError) as exc: - pickle.dumps(fr, proto) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.assertRaises(TypeError) as exc: + pickle.dumps(fr, proto) self.assertEqual(SpecialAttrsTests.TypeName.__name__, 'TypeName') self.assertEqual( @@ -6193,9 +6190,10 @@ def test_special_attrs2(self, proto): __name__, ) # NewTypes are picklable assuming correct qualname information. - s = pickle.dumps(SpecialAttrsTests.TypeName) - loaded = pickle.loads(s) - self.assertIs(SpecialAttrsTests.TypeName, loaded) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + s = pickle.dumps(SpecialAttrsTests.TypeName, proto) + loaded = pickle.loads(s) + self.assertIs(SpecialAttrsTests.TypeName, loaded) # Type variables don't support non-global instantiation per PEP 484 # restriction that "The argument to TypeVar() must be a string equal @@ -6205,17 +6203,19 @@ def test_special_attrs2(self, proto): self.assertFalse(hasattr(SpecialAttrsT, '__qualname__')) self.assertEqual(SpecialAttrsT.__module__, __name__) # Module-level type variables are picklable. - s = pickle.dumps(SpecialAttrsT, proto) - loaded = pickle.loads(s) - self.assertIs(SpecialAttrsT, loaded) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + s = pickle.dumps(SpecialAttrsT, proto) + loaded = pickle.loads(s) + self.assertIs(SpecialAttrsT, loaded) self.assertEqual(SpecialAttrsP.__name__, 'SpecialAttrsP') self.assertFalse(hasattr(SpecialAttrsP, '__qualname__')) self.assertEqual(SpecialAttrsP.__module__, __name__) # Module-level ParamSpecs are picklable. - s = pickle.dumps(SpecialAttrsP, proto) - loaded = pickle.loads(s) - self.assertIs(SpecialAttrsP, loaded) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + s = pickle.dumps(SpecialAttrsP, proto) + loaded = pickle.loads(s) + self.assertIs(SpecialAttrsP, loaded) def test_genericalias_dir(self): class Foo(Generic[T]): From 9a38a91c9fec9bd0064e89dfb540d9025dcb4483 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Fri, 15 Apr 2022 09:43:16 +0100 Subject: [PATCH 06/10] Tweak comment wording Co-authored-by: Jelle Zijlstra --- Lib/typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/typing.py b/Lib/typing.py index e19ce17c0d2d2f..e4c8ff3c7de920 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -993,7 +993,7 @@ class C(Generic[*Ts]): ... For more details, see PEP 646. - Note that only type variables tuples defined in global scope can be pickled. + Note that only TypeVarTuples defined in global scope can be pickled. """ def __init__(self, name): From 8fd2b4f8462ad76a3f61df022372de07efba03f5 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Fri, 15 Apr 2022 09:45:39 +0100 Subject: [PATCH 07/10] Remove test_pickling_then_unpickling_variadic_class_results_in_equality --- Lib/test/test_typing.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index cfe47aa65daf5a..8c4f12c427e7b4 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -928,22 +928,6 @@ def test_pickling_then_unpickling_unpacked_results_in_equality(self, proto): unpacked2 = pickle.loads(pickle.dumps(unpacked1, proto)) self.assertEqual(unpacked1, unpacked2) - @all_pickle_protocols - def test_pickling_then_unpickling_variadic_class_results_in_equality( - self, proto - ): - global T, Ts, A1, B1 # See explanation at start of class. - T = TypeVar('T') - Ts = TypeVarTuple('Ts') - - class A1(Generic[Unpack[Ts]]): pass - A2 = pickle.loads(pickle.dumps(A1, proto)) - self.assertEqual(A1, A2) - - class B1(Generic[T, Unpack[Ts]]): pass - B2 = pickle.loads(pickle.dumps(B1, proto)) - self.assertEqual(B1, B2) - @all_pickle_protocols def test_pickling_then_unpickling_tuple_with_typevartuple_equality( self, proto From ea8d5fd85b8a5ae185f7818eaeab195c8969c76e Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Fri, 15 Apr 2022 09:46:32 +0100 Subject: [PATCH 08/10] Remove parenthetic from comment --- Lib/test/test_typing.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 8c4f12c427e7b4..5e7a79c14d8c80 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -907,11 +907,10 @@ class C(Generic[Unpack[Ts]]): pass class TypeVarTuplePicklingTests(BaseTestCase): - # These are slightly awkward tests to run, because TypeVarTuples (and things - # made generic using TypeVarTuples) are only picklable if defined in the - # global scope. We therefore need to push various things defined in these - # tests into the global scope with `global` statements at the start of each - # test. + # These are slightly awkward tests to run, because TypeVarTuples are only + # picklable if defined in the global scope. We therefore need to push + # various things defined in these tests into the global scope with `global` + # statements at the start of each test. @all_pickle_protocols def test_pickling_then_unpickling_results_in_equality(self, proto): From d638d928a18842e36737ea5555597828b8b04581 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Fri, 15 Apr 2022 09:49:29 +0100 Subject: [PATCH 09/10] Replace assertEqual with assertIs --- Lib/test/test_typing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 5e7a79c14d8c80..38b0bfa43be27b 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -913,19 +913,19 @@ class TypeVarTuplePicklingTests(BaseTestCase): # statements at the start of each test. @all_pickle_protocols - def test_pickling_then_unpickling_results_in_equality(self, proto): + def test_pickling_then_unpickling_results_in_same_identity(self, proto): global Ts1 # See explanation at start of class. Ts1 = TypeVarTuple('Ts1') Ts2 = pickle.loads(pickle.dumps(Ts1, proto)) - self.assertEqual(Ts1, Ts2) + self.assertIs(Ts1, Ts2) @all_pickle_protocols - def test_pickling_then_unpickling_unpacked_results_in_equality(self, proto): + def test_pickling_then_unpickling_unpacked_results_in_same_identity(self, proto): global Ts # See explanation at start of class. Ts = TypeVarTuple('Ts') unpacked1 = Unpack[Ts] unpacked2 = pickle.loads(pickle.dumps(unpacked1, proto)) - self.assertEqual(unpacked1, unpacked2) + self.assertIs(unpacked1, unpacked2) @all_pickle_protocols def test_pickling_then_unpickling_tuple_with_typevartuple_equality( From c5d8dc6ce1b0209699be21cd90efdba153bdd064 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Fri, 15 Apr 2022 09:51:15 +0100 Subject: [PATCH 10/10] Remove test_pickling_then_unpickling_variadic_func_results_in_equality --- Lib/test/test_typing.py | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 38b0bfa43be27b..cb7138716075b4 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -947,34 +947,6 @@ def test_pickling_then_unpickling_tuple_with_typevartuple_equality( a2 = pickle.loads(pickle.dumps(a1, proto)) self.assertEqual(a1, a2) - @all_pickle_protocols - def test_pickling_then_unpickling_variadic_func_results_in_equality( - self, proto - ): - global T, Ts, a1, b1, c1, d1, e1 # See explanation at start of class. - T = TypeVar('T') - Ts = TypeVarTuple('Ts') - - def a1(*args: Unpack[Ts]): pass - a2 = pickle.loads(pickle.dumps(a1, proto)) - self.assertEqual(a1, a2) - - def b1(*args: Unpack[Tuple[int]]): pass - b2 = pickle.loads(pickle.dumps(b1, proto)) - self.assertEqual(b1, b2) - - def c1(*args: Unpack[Tuple[int, ...]]): pass - c2 = pickle.loads(pickle.dumps(c1, proto)) - self.assertEqual(c1, c2) - - def d1(*args: Unpack[Tuple[int, Unpack[Ts]]]): pass - d2 = pickle.loads(pickle.dumps(d1, proto)) - self.assertEqual(d1, d2) - - def e1(*args: Unpack[Tuple[T, Unpack[Ts]]]): pass - e2 = pickle.loads(pickle.dumps(e1, proto)) - self.assertEqual(e1, e2) - class UnionTests(BaseTestCase):