Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.4k
bpo-35900: Add a state_setter arg to save_reduce#12588
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
pitrou
merged 29 commits into
python:master
from
pierreglaser:give-a-state-setter-to-save-reduceMay 8, 2019
Uh oh!
There was an error while loading. Please reload this page.
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
ddce7cb
ENH add a state_setter arg to save_reduce
pierreglaser cf86a19
MNT News section
pierreglaser 9077859
TST test custom reducer using with state_setter
pierreglaser b388aa0
DOC mention state_setter in pickle docs
pierreglaser 1f286c1
CLN do not expose slotstate in state_setter
pierreglaser 8abbef1
DOC update the doc according to the last commit
pierreglaser 4b46841
CLN load_build cleanups
pierreglaser 6c2c957
CLN do not mention protocol 5
pierreglaser d7f8714
CLN style
pierreglaser d253441
CLN style and comments
pierreglaser 48e4686
DOC update doc
pierreglaser e83f940
CLN typo
pierreglaser 83d3a2b
MNT dont pervert BUILD if state_setter is specified
pierreglaser 68b3a5d
CLN style
pierreglaser f311ef6
ENH pop state_setter's output of the pickler stack
pierreglaser 744525e
DOC write code explanation comments
pierreglaser a2687d3
CLN spurious line deletion
pierreglaser 1dadb11
CLN remove stale state_setter handling in load_build
pierreglaser ab3cef2
ENH implement the same mechanism in pickle.py
pierreglaser 3fd958b
CLN little refactoring of pickle.py (comment + style)
pierreglaser 785c067
MNT versionadded directive
pierreglaser bb2882a
Fix typo
ncoghlan c0ea035
Fix copy-and-paste issue in error message
ncoghlan 4450b7b
Fix reduce result length checking error message
ncoghlan c781103
DOC rephrasings
pierreglaser 5404bcd
DOC comments
pierreglaser 88d19c0
TST test priority of state_setter over __setstate__
pierreglaser 183f2b0
TST, CLN remove unnecessary instance recreation
pierreglaser 3f2fdb2
FIX allow state_setter to be an arbitrary callable
pierreglaser File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -2992,7 +2992,26 @@ def __reduce__(self): | ||
| return str, (REDUCE_A,) | ||
| class BBB(object): | ||
| pass | ||
| def __init__(self): | ||
| # Add an instance attribute to enable state-saving routines at pickling | ||
| # time. | ||
| self.a = "some attribute" | ||
| def __setstate__(self, state): | ||
| self.a = "BBB.__setstate__" | ||
| def setstate_bbb(obj, state): | ||
| """Custom state setter for BBB objects | ||
| Such callable may be created by other persons than the ones who created the | ||
| BBB class. If passed as the state_setter item of a custom reducer, this | ||
| allows for custom state setting behavior of BBB objects. One can think of | ||
| it as the analogous of list_setitems or dict_setitems but for foreign | ||
| classes/functions. | ||
| """ | ||
| obj.a = "custom state_setter" | ||
| class AbstractDispatchTableTests(unittest.TestCase): | ||
| @@ -3081,6 +3100,25 @@ def reduce_2(obj): | ||
| self.assertEqual(default_load_dump(a), REDUCE_A) | ||
| self.assertIsInstance(default_load_dump(b), BBB) | ||
| # End-to-end testing of save_reduce with the state_setter keyword | ||
| # argument. This is a dispatch_table test as the primary goal of | ||
| # state_setter is to tweak objects reduction behavior. | ||
| # In particular, state_setter is useful when the default __setstate__ | ||
| # behavior is not flexible enough. | ||
| # No custom reducer for b has been registered for now, so | ||
| # BBB.__setstate__ should be used at unpickling time | ||
| self.assertEqual(default_load_dump(b).a, "BBB.__setstate__") | ||
| def reduce_bbb(obj): | ||
| return BBB, (), obj.__dict__, None, None, setstate_bbb | ||
pierreglaser marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| dispatch_table[BBB] = reduce_bbb | ||
| # The custom reducer reduce_bbb includes a state setter, that should | ||
| # have priority over BBB.__setstate__ | ||
| self.assertEqual(custom_load_dump(b).a, "custom state_setter") | ||
| if __name__ == "__main__": | ||
| # Print some stuff that can be used to rewrite DATA{0,1,2} | ||
3 changes: 3 additions & 0 deletions
3 Misc/NEWS.d/next/Library/2019-03-27-15-09-00.bpo-35900.fh56UU.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| Allow reduction methods to return a 6-item tuple where the 6th item specifies a | ||
| custom state-setting method that's called instead of the regular | ||
| ``__setstate__`` method. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -3662,6 +3662,7 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) | ||
| PyObject *state = NULL; | ||
| PyObject *listitems = Py_None; | ||
| PyObject *dictitems = Py_None; | ||
| PyObject *state_setter = Py_None; | ||
pitrou marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| PickleState *st = _Pickle_GetGlobalState(); | ||
| Py_ssize_t size; | ||
| int use_newobj = 0, use_newobj_ex = 0; | ||
| @@ -3672,14 +3673,15 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) | ||
| const char newobj_ex_op = NEWOBJ_EX; | ||
| size = PyTuple_Size(args); | ||
| if (size < 2 || size > 5) { | ||
| if (size < 2 || size > 6) { | ||
| PyErr_SetString(st->PicklingError, "tuple returned by " | ||
| "__reduce__ must contain 2 through 5 elements"); | ||
| "__reduce__ must contain 2 through 6 elements"); | ||
| return -1; | ||
| } | ||
| if (!PyArg_UnpackTuple(args, "save_reduce", 2, 5, | ||
| &callable, &argtup, &state, &listitems, &dictitems)) | ||
| if (!PyArg_UnpackTuple(args, "save_reduce", 2, 6, | ||
| &callable, &argtup, &state, &listitems, &dictitems, | ||
| &state_setter)) | ||
| return -1; | ||
| if (!PyCallable_Check(callable)) { | ||
| @@ -3714,6 +3716,15 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) | ||
| return -1; | ||
| } | ||
| if (state_setter == Py_None) | ||
| state_setter = NULL; | ||
| else if (!PyCallable_Check(state_setter)) { | ||
| PyErr_Format(st->PicklingError, "sixth element of the tuple " | ||
| "returned by __reduce__ must be a function, not %s", | ||
| Py_TYPE(state_setter)->tp_name); | ||
| return -1; | ||
| } | ||
| if (self->proto >= 2) { | ||
| PyObject *name; | ||
| _Py_IDENTIFIER(__name__); | ||
| @@ -3933,11 +3944,32 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) | ||
| return -1; | ||
| if (state) { | ||
| if (save(self, state, 0) < 0 || | ||
| _Pickler_Write(self, &build_op, 1) < 0) | ||
| return -1; | ||
| } | ||
| if (state_setter == NULL) { | ||
| if (save(self, state, 0) < 0 || | ||
| _Pickler_Write(self, &build_op, 1) < 0) | ||
| return -1; | ||
| } | ||
| else { | ||
| /* If a state_setter is specified, call it instead of load_build to | ||
| * update obj's with its previous state. | ||
| * The first 4 save/write instructions push state_setter and its | ||
| * tuple of expected arguments (obj, state) onto the stack. The | ||
| * REDUCE opcode triggers the state_setter(obj, state) function | ||
| * call. Finally, because state-updating routines only do in-place | ||
| * modification, the whole operation has to be stack-transparent. | ||
| * Thus, we finally pop the call's output from the stack.*/ | ||
| const char tupletwo_op = TUPLE2; | ||
| const char pop_op = POP; | ||
| if (save(self, state_setter, 0) < 0 || | ||
| save(self, obj, 0) < 0 || save(self, state, 0) < 0 || | ||
| _Pickler_Write(self, &tupletwo_op, 1) < 0 || | ||
| _Pickler_Write(self, &reduce_op, 1) < 0 || | ||
| _Pickler_Write(self, &pop_op, 1) < 0) | ||
| return -1; | ||
| } | ||
| } | ||
| return 0; | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.