Skip to content

Commit

Permalink
Switch to using non-backward-compatible pickles
Browse files Browse the repository at this point in the history
- Allow protocol 3 under Python 3.

- Do not stori bytes as strings under Python 3.

Fixes issue #4.
  • Loading branch information
tseaver committed Jun 7, 2013
1 parent 58b0554 commit 9542630
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
Unreleased
==========

- Switch to using non-backward-compatible pickles (protocol 3, without
storing bytes as strings) under Python 3.

- Fixed: A ``UnicodeDecodeError`` could happen for non-ASCII OIDs
when using bushy blob layout.

Expand Down
15 changes: 3 additions & 12 deletions src/ZODB/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@

class Pickler(zodbpickle.pickle.Pickler):
def __init__(self, f, protocol=None):
if protocol:
# we want to be backwards-compatible with Python 2
assert 0 <= protocol < 3
super(Pickler, self).__init__(f, protocol, bytes_as_strings=True)
super(Pickler, self).__init__(f, protocol)

class Unpickler(zodbpickle.pickle.Unpickler):
def __init__(self, f):
Expand All @@ -45,16 +42,10 @@ def find_class(self, modulename, name):
return self.find_global(modulename, name)

def dump(o, f, protocol=None):
if protocol:
# we want to be backwards-compatible with Python 2
assert 0 <= protocol < 3
return zodbpickle.pickle.dump(o, f, protocol, bytes_as_strings=True)
return zodbpickle.pickle.dump(o, f, protocol)

def dumps(o, protocol=None):
if protocol:
# we want to be backwards-compatible with Python 2
assert 0 <= protocol < 3
return zodbpickle.pickle.dumps(o, protocol, bytes_as_strings=True)
return zodbpickle.pickle.dumps(o, protocol)

def loads(s):
return zodbpickle.pickle.loads(s, encoding='ASCII', errors='bytes')
Expand Down
81 changes: 74 additions & 7 deletions src/ZODB/tests/testSerialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def make_pickle(ob):
return sio.getvalue()


def test_factory(conn, module_name, name):
def _factory(conn, module_name, name):
return globals()[name]

class SerializerTestCase(unittest.TestCase):
Expand All @@ -60,7 +60,7 @@ class SerializerTestCase(unittest.TestCase):
(ClassWithNewargs, (1,)))

def test_getClassName(self):
r = serialize.ObjectReader(factory=test_factory)
r = serialize.ObjectReader(factory=_factory)
eq = self.assertEqual
eq(r.getClassName(self.old_style_with_newargs),
__name__ + ".ClassWithNewargs")
Expand All @@ -82,7 +82,7 @@ def _get_class(self, module, name):
__import__(module)
return getattr(sys.modules[module], name)

r = TestObjectReader(factory=test_factory)
r = TestObjectReader(factory=_factory)
g = r.getGhost(self.old_style_with_newargs)
self.assertTrue(isinstance(g, ClassWithNewargs))
self.assertEqual(g, 1)
Expand Down Expand Up @@ -119,9 +119,76 @@ def _raise(self):
self.assertTrue(not serialize.myhasattr(NewStyle(), "rat"))


class SerializerFunctestCase(unittest.TestCase):

def setUp(self):
import tempfile
self._tempdir = tempfile.mkdtemp(suffix='serializerfunc')

def tearDown(self):
import shutil
shutil.rmtree(self._tempdir)

def test_funky_datetime_serialization(self):
import os
import subprocess

This comment has been minimized.

Copy link
@jimfulton

jimfulton Apr 8, 2017

Member

Why does this have to run in a separate process?

This comment has been minimized.

Copy link
@tseaver

tseaver Apr 18, 2017

Author Member

I don't recall at four years' distance.

fqn = os.path.join(self._tempdir, 'Data.fs')
prep_args = [sys.executable, '-c',
'from ZODB.tests.testSerialize import _functest_prep; '
'_functest_prep("%s")' % fqn]
subprocess.check_call(prep_args)
load_args = [sys.executable, '-c',
'from ZODB.tests.testSerialize import _functest_load; '
'_functest_load("%s")' % fqn]
subprocess.call(load_args)

def _working_failing_datetimes():
import datetime
WORKING = datetime.datetime(5375, 12, 31, 23, 59, 59)
# Any date after 5375 A.D. appears to trigger this bug.
FAILING = datetime.datetime(5376, 12, 31, 23, 59, 59)
return WORKING, FAILING

def _functest_prep(fqn):
# Prepare the database with a BTree which won't deserialize
# if the bug is present.
# run in separate process)
import transaction
from BTrees.OOBTree import OOBTree
from ZODB import DB
WORKING, FAILING = _working_failing_datetimes()
db = DB(fqn)
conn = db.open()
try:
root = conn.root()
tree = root['tree'] = OOBTree()
tree[WORKING] = 'working'
tree[FAILING] = 'failing'
transaction.commit()
finally: # Windoze
conn.close()
db.close()

def _functest_load(fqn):
# Open the database and attempt to deserialize the tree
# (run in separate process)
from ZODB import DB
WORKING, FAILING = _working_failing_datetimes()
db = DB(fqn)
conn = db.open()
try:
root = conn.root()
tree = root['tree']
assert tree[WORKING] == 'working'
assert tree[FAILING] == 'failing'
finally: # Windoze
conn.close()
db.close()

def test_suite():
suite = unittest.makeSuite(SerializerTestCase)
suite.addTest(
return unittest.TestSuite((
unittest.makeSuite(SerializerTestCase),
unittest.makeSuite(SerializerFunctestCase),
doctest.DocTestSuite("ZODB.serialize",
checker=ZODB.tests.util.checker))
return suite
checker=ZODB.tests.util.checker),
))

0 comments on commit 9542630

Please sign in to comment.