|
57 | 57 | # kind of outer loop. |
58 | 58 | protocols = range(pickle.HIGHEST_PROTOCOL + 1) |
59 | 59 |
|
| 60 | +FAST_NESTING_LIMIT = 50 |
| 61 | + |
60 | 62 |
|
61 | 63 | # Return True if opcode code appears in the pickle, else False. |
62 | 64 | def opcode_in_pickle(code, pickle): |
@@ -4273,6 +4275,98 @@ def __reduce__(self): |
4273 | 4275 | expected = "changed size during iteration" |
4274 | 4276 | self.assertIn(expected, str(e)) |
4275 | 4277 |
|
| 4278 | + def fast_save_enter(self, create_data, minprotocol=0): |
| 4279 | + # gh-146059: Check that fast_save() is called when |
| 4280 | + # fast_save_enter() is called. |
| 4281 | + if not hasattr(self, "pickler"): |
| 4282 | + self.skipTest("need Pickler class") |
| 4283 | + |
| 4284 | + data = [create_data(i) for i in range(FAST_NESTING_LIMIT * 2)] |
| 4285 | + data = {"key": data} |
| 4286 | + protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1) |
| 4287 | + for proto in protocols: |
| 4288 | + with self.subTest(proto=proto): |
| 4289 | + buf = io.BytesIO() |
| 4290 | + pickler = self.pickler(buf, protocol=proto) |
| 4291 | + # Enable fast mode (disables memo, enables cycle detection) |
| 4292 | + pickler.fast = 1 |
| 4293 | + pickler.dump(data) |
| 4294 | + |
| 4295 | + buf.seek(0) |
| 4296 | + data2 = self.unpickler(buf).load() |
| 4297 | + self.assertEqual(data2, data) |
| 4298 | + |
| 4299 | + def test_fast_save_enter_tuple(self): |
| 4300 | + self.fast_save_enter(lambda i: (i,)) |
| 4301 | + |
| 4302 | + def test_fast_save_enter_list(self): |
| 4303 | + self.fast_save_enter(lambda i: [i]) |
| 4304 | + |
| 4305 | + def test_fast_save_enter_frozenset(self): |
| 4306 | + self.fast_save_enter(lambda i: frozenset([i])) |
| 4307 | + |
| 4308 | + def test_fast_save_enter_set(self): |
| 4309 | + self.fast_save_enter(lambda i: set([i])) |
| 4310 | + |
| 4311 | + def test_fast_save_enter_frozendict(self): |
| 4312 | + if self.py_version < (3, 15): |
| 4313 | + self.skipTest('need frozendict') |
| 4314 | + self.fast_save_enter(lambda i: frozendict(key=i), minprotocol=2) |
| 4315 | + |
| 4316 | + def test_fast_save_enter_dict(self): |
| 4317 | + self.fast_save_enter(lambda i: {"key": i}) |
| 4318 | + |
| 4319 | + def deep_nested_struct(self, seed, create_nested, |
| 4320 | + minprotocol=0, compare_equal=True, |
| 4321 | + depth=FAST_NESTING_LIMIT * 2): |
| 4322 | + # gh-146059: Check that fast_save() is called when |
| 4323 | + # fast_save_enter() is called. |
| 4324 | + if not hasattr(self, "pickler"): |
| 4325 | + self.skipTest("need Pickler class") |
| 4326 | + |
| 4327 | + data = seed |
| 4328 | + for i in range(depth): |
| 4329 | + data = create_nested(data) |
| 4330 | + data = {"key": data} |
| 4331 | + protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1) |
| 4332 | + for proto in protocols: |
| 4333 | + with self.subTest(proto=proto): |
| 4334 | + buf = io.BytesIO() |
| 4335 | + pickler = self.pickler(buf, protocol=proto) |
| 4336 | + # Enable fast mode (disables memo, enables cycle detection) |
| 4337 | + pickler.fast = 1 |
| 4338 | + pickler.dump(data) |
| 4339 | + |
| 4340 | + buf.seek(0) |
| 4341 | + data2 = self.unpickler(buf).load() |
| 4342 | + if compare_equal: |
| 4343 | + self.assertEqual(data2, data) |
| 4344 | + |
| 4345 | + def test_deep_nested_struct_tuple(self): |
| 4346 | + self.deep_nested_struct((1,), lambda data: (data,)) |
| 4347 | + |
| 4348 | + def test_deep_nested_struct_list(self): |
| 4349 | + self.deep_nested_struct([1], lambda data: [data]) |
| 4350 | + |
| 4351 | + def test_deep_nested_struct_frozenset(self): |
| 4352 | + self.deep_nested_struct(frozenset((1,)), |
| 4353 | + lambda data: frozenset((1, data))) |
| 4354 | + |
| 4355 | + def test_deep_nested_struct_set(self): |
| 4356 | + self.deep_nested_struct({1}, lambda data: {K(data)}, |
| 4357 | + depth=FAST_NESTING_LIMIT+1, |
| 4358 | + compare_equal=False) |
| 4359 | + |
| 4360 | + def test_deep_nested_struct_frozendict(self): |
| 4361 | + if self.py_version < (3, 15): |
| 4362 | + self.skipTest('need frozendict') |
| 4363 | + self.deep_nested_struct(frozendict(x=1), |
| 4364 | + lambda data: frozendict(x=data), |
| 4365 | + minprotocol=2) |
| 4366 | + |
| 4367 | + def test_deep_nested_struct_dict(self): |
| 4368 | + self.deep_nested_struct({'x': 1}, lambda data: {'x': data}) |
| 4369 | + |
4276 | 4370 |
|
4277 | 4371 | class BigmemPickleTests: |
4278 | 4372 |
|
|
0 commit comments