Skip to content

Commit c86e3d0

Browse files
committed
gh-146059: Call fast_save_leave() in pickle save_frozenset()
1 parent 52c0186 commit c86e3d0

2 files changed

Lines changed: 46 additions & 4 deletions

File tree

Lib/test/pickletester.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
# kind of outer loop.
5858
protocols = range(pickle.HIGHEST_PROTOCOL + 1)
5959

60+
FAST_NESTING_LIMIT = 50
61+
6062

6163
# Return True if opcode code appears in the pickle, else False.
6264
def opcode_in_pickle(code, pickle):
@@ -4552,6 +4554,36 @@ def __reduce__(self):
45524554
expected = "changed size during iteration"
45534555
self.assertIn(expected, str(e))
45544556

4557+
def test_fast_save_enter_leave(self):
4558+
# gh-146059: Check that fast_save_leave() is called when
4559+
# fast_save_enter() is called.
4560+
if not hasattr(self, "pickler"):
4561+
self.skipTest("need Pickler class")
4562+
4563+
limit = FAST_NESTING_LIMIT * 2
4564+
for proto in protocols:
4565+
for tested_type in (frozenset, list, dict):
4566+
with self.subTest(proto=proto, tested_type=tested_type):
4567+
buf = io.BytesIO()
4568+
pickler = self.pickler(buf, protocol=proto)
4569+
# Enable fast mode (disables memo, enables cycle detection)
4570+
pickler.fast = 1
4571+
4572+
if tested_type == frozenset:
4573+
data = [frozenset([i]) for i in range(limit)]
4574+
elif tested_type == list:
4575+
data = [[i] for i in range(limit)]
4576+
elif tested_type == dict:
4577+
data = [{"key": 123} for i in range(limit)]
4578+
else:
4579+
self.fail("unknown tested_type")
4580+
data = {"key": data}
4581+
pickler.dump(data)
4582+
4583+
buf.seek(0)
4584+
data2 = self.unpickler(buf).load()
4585+
self.assertEqual(data2, data)
4586+
45554587

45564588
class BigmemPickleTests:
45574589

Modules/_pickle.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3671,16 +3671,13 @@ save_set(PickleState *state, PicklerObject *self, PyObject *obj)
36713671
}
36723672

36733673
static int
3674-
save_frozenset(PickleState *state, PicklerObject *self, PyObject *obj)
3674+
save_frozenset_impl(PickleState *state, PicklerObject *self, PyObject *obj)
36753675
{
36763676
PyObject *iter;
36773677

36783678
const char mark_op = MARK;
36793679
const char frozenset_op = FROZENSET;
36803680

3681-
if (self->fast && !fast_save_enter(self, obj))
3682-
return -1;
3683-
36843681
if (self->proto < 4) {
36853682
PyObject *items;
36863683
PyObject *reduce_value;
@@ -3751,6 +3748,19 @@ save_frozenset(PickleState *state, PicklerObject *self, PyObject *obj)
37513748
return 0;
37523749
}
37533750

3751+
static int
3752+
save_frozenset(PickleState *state, PicklerObject *self, PyObject *obj)
3753+
{
3754+
if (self->fast && !fast_save_enter(self, obj)) {
3755+
return -1;
3756+
}
3757+
int status = save_frozenset_impl(state, self, obj);
3758+
if (self->fast && !fast_save_leave(self, obj)) {
3759+
return -1;
3760+
}
3761+
return status;
3762+
}
3763+
37543764
static int
37553765
fix_imports(PickleState *st, PyObject **module_name, PyObject **global_name)
37563766
{

0 commit comments

Comments
 (0)