Commit 12ee41c4 authored by Jim Fulton's avatar Jim Fulton Committed by GitHub

- ZODB now uses pickle protocol 3 for both Python 2 and Python 3. (#194)

(Previously, protocol 2 was used for Python 2.)

  The zodbpickle package provides a `zodbpickle.binary` string type
  that should be used in Python 2 to cause binary strings to be saved
  in a pickle binary format, so they can be loaded correctly in
  Python 3.  Pickle protocol 3 is needed for this to work correctly.

- Object identifiers in persistent references are saved as
  `zodbpickle.binary` strings in Python 2, so that they are loaded
  correctly in Python 3.
parent dba374d4
...@@ -5,6 +5,19 @@ ...@@ -5,6 +5,19 @@
5.4.0 (unreleased) 5.4.0 (unreleased)
================== ==================
- ZODB now uses pickle protocol 3 for both Python 2 and Python 3.
(Previously, protocol 2 was used for Python 2.)
The zodbpickle package provides a `zodbpickle.binary` string type
that should be used in Python 2 to cause binary strings to be saved
in a pickle binary format, so they can be loaded correctly in
Python 3. Pickle protocol 3 is needed for this to work correctly.
- Object identifiers in persistent references are saved as
`zodbpickle.binary` strings in Python 2, so that they are loaded
correctly in Python 3.
- If an object is missing from the index while packing a ``FileStorage``, - If an object is missing from the index while packing a ``FileStorage``,
report its full ``oid``. report its full ``oid``.
......
...@@ -16,6 +16,8 @@ from six import PY3 ...@@ -16,6 +16,8 @@ from six import PY3
IS_JYTHON = sys.platform.startswith('java') IS_JYTHON = sys.platform.startswith('java')
_protocol = 3
from zodbpickle import binary
if not PY3: if not PY3:
# Python 2.x # Python 2.x
...@@ -34,7 +36,6 @@ if not PY3: ...@@ -34,7 +36,6 @@ if not PY3:
HIGHEST_PROTOCOL = cPickle.HIGHEST_PROTOCOL HIGHEST_PROTOCOL = cPickle.HIGHEST_PROTOCOL
IMPORT_MAPPING = {} IMPORT_MAPPING = {}
NAME_MAPPING = {} NAME_MAPPING = {}
_protocol = 2
FILESTORAGE_MAGIC = b"FS21" FILESTORAGE_MAGIC = b"FS21"
else: else:
# Python 3.x: can't use stdlib's pickle because # Python 3.x: can't use stdlib's pickle because
...@@ -69,7 +70,6 @@ else: ...@@ -69,7 +70,6 @@ else:
def loads(s): def loads(s):
return zodbpickle.pickle.loads(s, encoding='ASCII', errors='bytes') return zodbpickle.pickle.loads(s, encoding='ASCII', errors='bytes')
_protocol = 3
FILESTORAGE_MAGIC = b"FS30" FILESTORAGE_MAGIC = b"FS30"
......
...@@ -139,7 +139,8 @@ from persistent import Persistent ...@@ -139,7 +139,8 @@ from persistent import Persistent
from persistent.wref import WeakRefMarker, WeakRef from persistent.wref import WeakRefMarker, WeakRef
from ZODB import broken from ZODB import broken
from ZODB.POSException import InvalidObjectReference from ZODB.POSException import InvalidObjectReference
from ZODB._compat import PersistentPickler, PersistentUnpickler, BytesIO, _protocol from ZODB._compat import PersistentPickler, PersistentUnpickler, BytesIO
from ZODB._compat import _protocol, binary
_oidtypes = bytes, type(None) _oidtypes = bytes, type(None)
...@@ -186,7 +187,7 @@ class ObjectWriter(object): ...@@ -186,7 +187,7 @@ class ObjectWriter(object):
>>> class DummyJar(object): >>> class DummyJar(object):
... xrefs = True ... xrefs = True
... def new_oid(self): ... def new_oid(self):
... return 42 ... return b'42'
... def db(self): ... def db(self):
... return self ... return self
... databases = {} ... databases = {}
...@@ -204,24 +205,31 @@ class ObjectWriter(object): ...@@ -204,24 +205,31 @@ class ObjectWriter(object):
>>> bob = P('bob') >>> bob = P('bob')
>>> oid, cls = writer.persistent_id(bob) >>> oid, cls = writer.persistent_id(bob)
>>> oid >>> oid
42 '42'
>>> cls is P >>> cls is P
True True
To work with Python 3, the oid in the persistent id is of the
zodbpickle binary type:
>>> oid.__class__ is binary
True
If a persistent object does not already have an oid and jar, If a persistent object does not already have an oid and jar,
these will be assigned by persistent_id(): these will be assigned by persistent_id():
>>> bob._p_oid >>> bob._p_oid
42 '42'
>>> bob._p_jar is jar >>> bob._p_jar is jar
True True
If the object already has a persistent id, the id is not changed: If the object already has a persistent id, the id is not changed:
>>> bob._p_oid = 24 >>> bob._p_oid = b'24'
>>> oid, cls = writer.persistent_id(bob) >>> oid, cls = writer.persistent_id(bob)
>>> oid >>> oid
24 '24'
>>> cls is P >>> cls is P
True True
...@@ -247,9 +255,9 @@ class ObjectWriter(object): ...@@ -247,9 +255,9 @@ class ObjectWriter(object):
>>> sam = PNewArgs('sam') >>> sam = PNewArgs('sam')
>>> writer.persistent_id(sam) >>> writer.persistent_id(sam)
42 '42'
>>> sam._p_oid >>> sam._p_oid
42 '42'
>>> sam._p_jar is jar >>> sam._p_jar is jar
True True
...@@ -312,6 +320,8 @@ class ObjectWriter(object): ...@@ -312,6 +320,8 @@ class ObjectWriter(object):
obj.oid = oid obj.oid = oid
obj.dm = target._p_jar obj.dm = target._p_jar
obj.database_name = obj.dm.db().database_name obj.database_name = obj.dm.db().database_name
oid = binary(oid)
if obj.dm is self._jar: if obj.dm is self._jar:
return ['w', (oid, )] return ['w', (oid, )]
else: else:
...@@ -366,6 +376,7 @@ class ObjectWriter(object): ...@@ -366,6 +376,7 @@ class ObjectWriter(object):
self._jar, obj, self._jar, obj,
) )
oid = binary(oid)
klass = type(obj) klass = type(obj)
if hasattr(klass, '__getnewargs__'): if hasattr(klass, '__getnewargs__'):
# We don't want to save newargs in object refs. # We don't want to save newargs in object refs.
......
...@@ -137,6 +137,9 @@ class SerializerTestCase(unittest.TestCase): ...@@ -137,6 +137,9 @@ class SerializerTestCase(unittest.TestCase):
top.ref = WeakRef(o) top.ref = WeakRef(o)
pickle = serialize.ObjectWriter().serialize(top) pickle = serialize.ObjectWriter().serialize(top)
# Make sure the persistent id is pickled using the 'C',
# SHORT_BINBYTES opcode:
self.assertTrue(b'C\x04abcd' in pickle)
refs = [] refs = []
u = PersistentUnpickler(None, refs.append, BytesIO(pickle)) u = PersistentUnpickler(None, refs.append, BytesIO(pickle))
...@@ -145,6 +148,18 @@ class SerializerTestCase(unittest.TestCase): ...@@ -145,6 +148,18 @@ class SerializerTestCase(unittest.TestCase):
self.assertEqual(refs, [['w', (b'abcd',)]]) self.assertEqual(refs, [['w', (b'abcd',)]])
def test_protocol_3_binary_handling(self):
from ZODB.serialize import _protocol
self.assertEqual(3, _protocol) # Yeah, whitebox
o = PersistentObject()
o._p_oid = b'o'
o.o = PersistentObject()
o.o._p_oid = b'o.o'
pickle = serialize.ObjectWriter().serialize(o)
# Make sure the persistent id is pickled using the 'C',
# SHORT_BINBYTES opcode:
self.assertTrue(b'C\x03o.o' in pickle)
class SerializerFunctestCase(unittest.TestCase): class SerializerFunctestCase(unittest.TestCase):
......
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