8000 bpo-43224: Implement pickling of TypeVarTuples by mrahtz · Pull Request #32119 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-43224: Implement pickling of TypeVarTuples #32119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 22, 2022
Merged
Prev Previous commit
Next Next commit
Revert "Switch other tests to use @all_pickle_protocols too"
This reverts commit de68232.
  • Loading branch information
mrahtz committed Mar 30, 2022
commit 69197c2113a138a8d91bd0db786f9b3dde74c5cf
194 changes: 97 additions & 97 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')

Expand All @@ -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)
Expand Down Expand Up @@ -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')

Expand All @@ -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')
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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})
Expand Down Expand Up @@ -5648,19 +5645,19 @@ 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]]

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

Expand All @@ -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"
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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.
Expand All @@ -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(
Expand All @@ -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
Expand All @@ -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]):
Expand Down
0