Skip to content

Commit cbda40d

Browse files
bpo-37210: Fix pure Python pickle when _pickle is unavailable (GH-14016)
Allow pure Python implementation of pickle to work even when the C _pickle module is unavailable. Fix test_pickle when _pickle is missing: declare PyPicklerHookTests outside "if has_c_implementation:" block. (cherry picked from commit 63ab4ba) Co-authored-by: Victor Stinner <[email protected]>
1 parent e40a97a commit cbda40d

File tree

3 files changed

+41
-32
lines changed

3 files changed

+41
-32
lines changed

Lib/pickle.py

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@
3636
import codecs
3737
import _compat_pickle
3838

39-
from _pickle import PickleBuffer
40-
4139
__all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler",
42-
"Unpickler", "dump", "dumps", "load", "loads", "PickleBuffer"]
40+
"Unpickler", "dump", "dumps", "load", "loads"]
41+
42+
try:
43+
from _pickle import PickleBuffer
44+
__all__.append("PickleBuffer")
45+
_HAVE_PICKLE_BUFFER = True
46+
except ImportError:
47+
_HAVE_PICKLE_BUFFER = False
48+
4349

4450
# Shortcut for use in isinstance testing
4551
bytes_types = (bytes, bytearray)
@@ -812,31 +818,32 @@ def save_bytearray(self, obj):
812818
self.write(BYTEARRAY8 + pack("<Q", n) + obj)
813819
dispatch[bytearray] = save_bytearray
814820

815-
def save_picklebuffer(self, obj):
816-
if self.proto < 5:
817-
raise PicklingError("PickleBuffer can only pickled with "
818-
"protocol >= 5")
819-
with obj.raw() as m:
820-
if not m.contiguous:
821-
raise PicklingError("PickleBuffer can not be pickled when "
822-
"pointing to a non-contiguous buffer")
823-
in_band = True
824-
if self._buffer_callback is not None:
825-
in_band = bool(self._buffer_callback(obj))
826-
if in_band:
827-
# Write data in-band
828-
# XXX The C implementation avoids a copy here
829-
if m.readonly:
830-
self.save_bytes(m.tobytes())
821+
if _HAVE_PICKLE_BUFFER:
822+
def save_picklebuffer(self, obj):
823+
if self.proto < 5:
824+
raise PicklingError("PickleBuffer can only pickled with "
825+
"protocol >= 5")
826+
with obj.raw() as m:
827+
if not m.contiguous:
828+
raise PicklingError("PickleBuffer can not be pickled when "
829+
"pointing to a non-contiguous buffer")
830+
in_band = True
831+
if self._buffer_callback is not None:
832+
in_band = bool(self._buffer_callback(obj))
833+
if in_band:
834+
# Write data in-band
835+
# XXX The C implementation avoids a copy here
836+
if m.readonly:
837+
self.save_bytes(m.tobytes())
838+
else:
839+
self.save_bytearray(m.tobytes())
831840
else:
832-
self.save_bytearray(m.tobytes())
833-
else:
834-
# Write data out-of-band
835-
self.write(NEXT_BUFFER)
836-
if m.readonly:
837-
self.write(READONLY_BUFFER)
841+
# Write data out-of-band
842+
self.write(NEXT_BUFFER)
843+
if m.readonly:
844+
self.write(READONLY_BUFFER)
838845

839-
dispatch[PickleBuffer] = save_picklebuffer
846+
dispatch[PickleBuffer] = save_picklebuffer
840847

841848
def save_str(self, obj):
842849
if self.bin:

Lib/test/test_pickle.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,13 @@ def get_dispatch_table(self):
203203
return collections.ChainMap({}, pickle.dispatch_table)
204204

205205

206+
class PyPicklerHookTests(AbstractHookTests):
207+
class CustomPyPicklerClass(pickle._Pickler,
208+
AbstractCustomPicklerClass):
209+
pass
210+
pickler_class = CustomPyPicklerClass
211+
212+
206213
if has_c_implementation:
207214
class CPickleTests(AbstractPickleModuleTests):
208215
from _pickle import dump, dumps, load, loads, Pickler, Unpickler
@@ -255,12 +262,6 @@ class CChainDispatchTableTests(AbstractDispatchTableTests):
255262
def get_dispatch_table(self):
256263
return collections.ChainMap({}, pickle.dispatch_table)
257264

258-
class PyPicklerHookTests(AbstractHookTests):
259-
class CustomPyPicklerClass(pickle._Pickler,
260-
AbstractCustomPicklerClass):
261-
pass
262-
pickler_class = CustomPyPicklerClass
263-
264265
class CPicklerHookTests(AbstractHookTests):
265266
class CustomCPicklerClass(_pickle.Pickler, AbstractCustomPicklerClass):
266267
pass
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow pure Python implementation of :mod:`pickle` to work even when the C :mod:`_pickle` module is unavailable.

0 commit comments

Comments
 (0)