|
58 | 58 | # kind of outer loop. |
59 | 59 | protocols = range(pickle.HIGHEST_PROTOCOL + 1) |
60 | 60 |
|
| 61 | +FAST_NESTING_LIMIT = 50 |
| 62 | + |
61 | 63 |
|
62 | 64 | # Return True if opcode code appears in the pickle, else False. |
63 | 65 | def opcode_in_pickle(code, pickle): |
@@ -3944,6 +3946,99 @@ def __reduce__(self): |
3944 | 3946 | expected = "changed size during iteration" |
3945 | 3947 | self.assertIn(expected, str(e)) |
3946 | 3948 |
|
| 3949 | + def fast_save_enter(self, create_data, minprotocol=0): |
| 3950 | + # gh-146059: Check that fast_save() is called when |
| 3951 | + # fast_save_enter() is called. |
| 3952 | + if not hasattr(self, "pickler"): |
| 3953 | + self.skipTest("need Pickler class") |
| 3954 | + |
| 3955 | + data = [create_data(i) for i in range(FAST_NESTING_LIMIT * 2)] |
| 3956 | + data = {"key": data} |
| 3957 | + protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1) |
| 3958 | + for proto in protocols: |
| 3959 | + with self.subTest(proto=proto): |
| 3960 | + buf = io.BytesIO() |
| 3961 | + pickler = self.pickler(buf, protocol=proto) |
| 3962 | + # Enable fast mode (disables memo, enables cycle detection) |
| 3963 | + pickler.fast = 1 |
| 3964 | + pickler.dump(data) |
| 3965 | + |
| 3966 | + buf.seek(0) |
| 3967 | + data2 = self.unpickler(buf).load() |
| 3968 | + self.assertEqual(data2, data) |
| 3969 | + |
| 3970 | + def test_fast_save_enter_tuple(self): |
| 3971 | + self.fast_save_enter(lambda i: (i,)) |
| 3972 | + |
| 3973 | + def test_fast_save_enter_list(self): |
| 3974 | + self.fast_save_enter(lambda i: [i]) |
| 3975 | + |
| 3976 | + def test_fast_save_enter_frozenset(self): |
| 3977 | + self.fast_save_enter(lambda i: frozenset([i])) |
| 3978 | + |
| 3979 | + def test_fast_save_enter_set(self): |
| 3980 | + self.fast_save_enter(lambda i: set([i])) |
| 3981 | + |
| 3982 | + def test_fast_save_enter_frozendict(self): |
| 3983 | + if self.py_version < (3, 15): |
| 3984 | + self.skipTest('need frozendict') |
| 3985 | + self.fast_save_enter(lambda i: frozendict(key=i), minprotocol=2) |
| 3986 | + |
| 3987 | + def test_fast_save_enter_dict(self): |
| 3988 | + self.fast_save_enter(lambda i: {"key": i}) |
| 3989 | + |
| 3990 | + def deep_nested_struct(self, seed, create_nested, |
| 3991 | + minprotocol=0, compare_equal=True, |
| 3992 | + depth=FAST_NESTING_LIMIT * 2): |
| 3993 | + # gh-146059: Check that fast_save() is called when |
| 3994 | + # fast_save_enter() is called. |
| 3995 | + if not hasattr(self, "pickler"): |
| 3996 | + self.skipTest("need Pickler class") |
| 3997 | + |
| 3998 | + data = seed |
| 3999 | + for i in range(depth): |
| 4000 | + data = create_nested(data) |
| 4001 | + data = {"key": data} |
| 4002 | + protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1) |
| 4003 | + for proto in protocols: |
| 4004 | + with self.subTest(proto=proto): |
| 4005 | + buf = io.BytesIO() |
| 4006 | + pickler = self.pickler(buf, protocol=proto) |
| 4007 | + # Enable fast mode (disables memo, enables cycle detection) |
| 4008 | + pickler.fast = 1 |
| 4009 | + pickler.dump(data) |
| 4010 | + |
| 4011 | + buf.seek(0) |
| 4012 | + data2 = self.unpickler(buf).load() |
| 4013 | + if compare_equal: |
| 4014 | + self.assertEqual(data2, data) |
| 4015 | + |
| 4016 | + def test_deep_nested_struct_tuple(self): |
| 4017 | + self.deep_nested_struct((1,), lambda data: (data,)) |
| 4018 | + |
| 4019 | + def test_deep_nested_struct_list(self): |
| 4020 | + self.deep_nested_struct([1], lambda data: [data]) |
| 4021 | + |
| 4022 | + def test_deep_nested_struct_frozenset(self): |
| 4023 | + self.deep_nested_struct(frozenset((1,)), |
| 4024 | + lambda data: frozenset((1, data))) |
| 4025 | + |
| 4026 | + @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI") |
| 4027 | + def test_deep_nested_struct_set(self): |
| 4028 | + self.deep_nested_struct({1}, lambda data: {K(data)}, |
| 4029 | + depth=FAST_NESTING_LIMIT+1, |
| 4030 | + compare_equal=False) |
| 4031 | + |
| 4032 | + def test_deep_nested_struct_frozendict(self): |
| 4033 | + if self.py_version < (3, 15): |
| 4034 | + self.skipTest('need frozendict') |
| 4035 | + self.deep_nested_struct(frozendict(x=1), |
| 4036 | + lambda data: frozendict(x=data), |
| 4037 | + minprotocol=2) |
| 4038 | + |
| 4039 | + def test_deep_nested_struct_dict(self): |
| 4040 | + self.deep_nested_struct({'x': 1}, lambda data: {'x': data}) |
| 4041 | + |
3947 | 4042 |
|
3948 | 4043 | class BigmemPickleTests: |
3949 | 4044 |
|
|
0 commit comments