Commit 63ab4ba0 authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

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.
parent 6f75c873
...@@ -36,10 +36,16 @@ import io ...@@ -36,10 +36,16 @@ import io
import codecs import codecs
import _compat_pickle import _compat_pickle
from _pickle import PickleBuffer
__all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler", __all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler",
"Unpickler", "dump", "dumps", "load", "loads", "PickleBuffer"] "Unpickler", "dump", "dumps", "load", "loads"]
try:
from _pickle import PickleBuffer
__all__.append("PickleBuffer")
_HAVE_PICKLE_BUFFER = True
except ImportError:
_HAVE_PICKLE_BUFFER = False
# Shortcut for use in isinstance testing # Shortcut for use in isinstance testing
bytes_types = (bytes, bytearray) bytes_types = (bytes, bytearray)
...@@ -812,31 +818,32 @@ class _Pickler: ...@@ -812,31 +818,32 @@ class _Pickler:
self.write(BYTEARRAY8 + pack("<Q", n) + obj) self.write(BYTEARRAY8 + pack("<Q", n) + obj)
dispatch[bytearray] = save_bytearray dispatch[bytearray] = save_bytearray
def save_picklebuffer(self, obj): if _HAVE_PICKLE_BUFFER:
if self.proto < 5: def save_picklebuffer(self, obj):
raise PicklingError("PickleBuffer can only pickled with " if self.proto < 5:
"protocol >= 5") raise PicklingError("PickleBuffer can only pickled with "
with obj.raw() as m: "protocol >= 5")
if not m.contiguous: with obj.raw() as m:
raise PicklingError("PickleBuffer can not be pickled when " if not m.contiguous:
"pointing to a non-contiguous buffer") raise PicklingError("PickleBuffer can not be pickled when "
in_band = True "pointing to a non-contiguous buffer")
if self._buffer_callback is not None: in_band = True
in_band = bool(self._buffer_callback(obj)) if self._buffer_callback is not None:
if in_band: in_band = bool(self._buffer_callback(obj))
# Write data in-band if in_band:
# XXX The C implementation avoids a copy here # Write data in-band
if m.readonly: # XXX The C implementation avoids a copy here
self.save_bytes(m.tobytes()) if m.readonly:
self.save_bytes(m.tobytes())
else:
self.save_bytearray(m.tobytes())
else: else:
self.save_bytearray(m.tobytes()) # Write data out-of-band
else: self.write(NEXT_BUFFER)
# Write data out-of-band if m.readonly:
self.write(NEXT_BUFFER) self.write(READONLY_BUFFER)
if m.readonly:
self.write(READONLY_BUFFER)
dispatch[PickleBuffer] = save_picklebuffer dispatch[PickleBuffer] = save_picklebuffer
def save_str(self, obj): def save_str(self, obj):
if self.bin: if self.bin:
......
...@@ -203,6 +203,13 @@ class PyChainDispatchTableTests(AbstractDispatchTableTests): ...@@ -203,6 +203,13 @@ class PyChainDispatchTableTests(AbstractDispatchTableTests):
return collections.ChainMap({}, pickle.dispatch_table) return collections.ChainMap({}, pickle.dispatch_table)
class PyPicklerHookTests(AbstractHookTests):
class CustomPyPicklerClass(pickle._Pickler,
AbstractCustomPicklerClass):
pass
pickler_class = CustomPyPicklerClass
if has_c_implementation: if has_c_implementation:
class CPickleTests(AbstractPickleModuleTests): class CPickleTests(AbstractPickleModuleTests):
from _pickle import dump, dumps, load, loads, Pickler, Unpickler from _pickle import dump, dumps, load, loads, Pickler, Unpickler
...@@ -255,12 +262,6 @@ if has_c_implementation: ...@@ -255,12 +262,6 @@ if has_c_implementation:
def get_dispatch_table(self): def get_dispatch_table(self):
return collections.ChainMap({}, pickle.dispatch_table) return collections.ChainMap({}, pickle.dispatch_table)
class PyPicklerHookTests(AbstractHookTests):
class CustomPyPicklerClass(pickle._Pickler,
AbstractCustomPicklerClass):
pass
pickler_class = CustomPyPicklerClass
class CPicklerHookTests(AbstractHookTests): class CPicklerHookTests(AbstractHookTests):
class CustomCPicklerClass(_pickle.Pickler, AbstractCustomPicklerClass): class CustomCPicklerClass(_pickle.Pickler, AbstractCustomPicklerClass):
pass pass
......
Allow pure Python implementation of :mod:`pickle` to work even when the C :mod:`_pickle` module is unavailable.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment