bpo-35900: Add a state_setter arg to save_reduce (GH-12588) · python/cpython@65d98d0 (original) (raw)
`@@ -3662,6 +3662,7 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
`
3662
3662
`PyObject *state = NULL;
`
3663
3663
`PyObject *listitems = Py_None;
`
3664
3664
`PyObject *dictitems = Py_None;
`
``
3665
`+
PyObject *state_setter = Py_None;
`
3665
3666
`PickleState *st = _Pickle_GetGlobalState();
`
3666
3667
`Py_ssize_t size;
`
3667
3668
`int use_newobj = 0, use_newobj_ex = 0;
`
`@@ -3672,14 +3673,15 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
`
3672
3673
`const char newobj_ex_op = NEWOBJ_EX;
`
3673
3674
``
3674
3675
`size = PyTuple_Size(args);
`
3675
``
`-
if (size < 2 || size > 5) {
`
``
3676
`+
if (size < 2 || size > 6) {
`
3676
3677
`PyErr_SetString(st->PicklingError, "tuple returned by "
`
3677
``
`-
"reduce must contain 2 through 5 elements");
`
``
3678
`+
"reduce must contain 2 through 6 elements");
`
3678
3679
`return -1;
`
3679
3680
` }
`
3680
3681
``
3681
``
`-
if (!PyArg_UnpackTuple(args, "save_reduce", 2, 5,
`
3682
``
`-
&callable, &argtup, &state, &listitems, &dictitems))
`
``
3682
`+
if (!PyArg_UnpackTuple(args, "save_reduce", 2, 6,
`
``
3683
`+
&callable, &argtup, &state, &listitems, &dictitems,
`
``
3684
`+
&state_setter))
`
3683
3685
`return -1;
`
3684
3686
``
3685
3687
`if (!PyCallable_Check(callable)) {
`
`@@ -3714,6 +3716,15 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
`
3714
3716
`return -1;
`
3715
3717
` }
`
3716
3718
``
``
3719
`+
if (state_setter == Py_None)
`
``
3720
`+
state_setter = NULL;
`
``
3721
`+
else if (!PyCallable_Check(state_setter)) {
`
``
3722
`+
PyErr_Format(st->PicklingError, "sixth element of the tuple "
`
``
3723
`+
"returned by reduce must be a function, not %s",
`
``
3724
`+
Py_TYPE(state_setter)->tp_name);
`
``
3725
`+
return -1;
`
``
3726
`+
}
`
``
3727
+
3717
3728
`if (self->proto >= 2) {
`
3718
3729
`PyObject *name;
`
3719
3730
`_Py_IDENTIFIER(name);
`
`@@ -3933,11 +3944,32 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
`
3933
3944
`return -1;
`
3934
3945
``
3935
3946
`if (state) {
`
3936
``
`-
if (save(self, state, 0) < 0 ||
`
3937
``
`-
_Pickler_Write(self, &build_op, 1) < 0)
`
3938
``
`-
return -1;
`
3939
``
`-
}
`
``
3947
`+
if (state_setter == NULL) {
`
``
3948
`+
if (save(self, state, 0) < 0 ||
`
``
3949
`+
_Pickler_Write(self, &build_op, 1) < 0)
`
``
3950
`+
return -1;
`
``
3951
`+
}
`
``
3952
`+
else {
`
``
3953
+
``
3954
`+
/* If a state_setter is specified, call it instead of load_build to
`
``
3955
`+
- update obj's with its previous state.
`
``
3956
`+
- The first 4 save/write instructions push state_setter and its
`
``
3957
`+
- tuple of expected arguments (obj, state) onto the stack. The
`
``
3958
`+
- REDUCE opcode triggers the state_setter(obj, state) function
`
``
3959
`+
- call. Finally, because state-updating routines only do in-place
`
``
3960
`+
- modification, the whole operation has to be stack-transparent.
`
``
3961
`+
- Thus, we finally pop the call's output from the stack.*/
`
3940
3962
``
``
3963
`+
const char tupletwo_op = TUPLE2;
`
``
3964
`+
const char pop_op = POP;
`
``
3965
`+
if (save(self, state_setter, 0) < 0 ||
`
``
3966
`+
save(self, obj, 0) < 0 || save(self, state, 0) < 0 ||
`
``
3967
`+
_Pickler_Write(self, &tupletwo_op, 1) < 0 ||
`
``
3968
`+
_Pickler_Write(self, &reduce_op, 1) < 0 ||
`
``
3969
`+
_Pickler_Write(self, &pop_op, 1) < 0)
`
``
3970
`+
return -1;
`
``
3971
`+
}
`
``
3972
`+
}
`
3941
3973
`return 0;
`
3942
3974
`}
`
3943
3975
``