Skip to content

Commit

Permalink
Skip blob objects during Python 3 migration. (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
frisi authored and Michael Howitz committed Oct 5, 2018
1 parent e7ccaf4 commit 54233b1
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Changes
1.1 (unreleased)
----------------

- Skip records for ZODB.blob when migrating database to python3 to not break
references to blobfiles.

- When migrating databases to python3, do not fail when converting
attributes containing None.

Expand Down
15 changes: 13 additions & 2 deletions src/zodbupdate/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
logger = logging.getLogger('zodbupdate.serialize')
known_broken_modules = {}

# types to skip when renaming/migrating databases
SKIP_SYMBS = [('ZODB.blob', 'Blob')]


def create_broken_module_for(symb):
"""If your pickle refer a broken class (not an instance of it, a
Expand All @@ -37,7 +40,7 @@ def create_broken_module_for(symb):
parts = symb.__module__.split('.')
previous = None
for fullname, name in reversed(
[('.'.join(parts[0:p+1]), parts[p])
[('.'.join(parts[0:p + 1]), parts[p])
for p in range(1, len(parts))]):
if fullname not in sys.modules:
if fullname not in known_broken_modules:
Expand Down Expand Up @@ -155,7 +158,10 @@ def __update_symb(self, symb_info):
loaded and its location is checked to see if it have moved as
well.
"""
if symb_info in self.__renames:
if symb_info in SKIP_SYMBS:
self.__skipped = True
return symb_info
elif symb_info in self.__renames:
self.__changed = True
return self.__renames[symb_info]
else:
Expand Down Expand Up @@ -292,13 +298,18 @@ def rename(self, input_file):
return None otherwise.
"""
self.__changed = False
self.__skipped = False

unpickler = self.__unpickler(input_file)
class_meta = unpickler.load()
data = unpickler.load()

class_meta = self.__update_class_meta(class_meta)

if self.__skipped:
# do not do renames/conversions on blob records
return None

self.__decode_data(class_meta, data)

if not (self.__changed or self.__repickle_all):
Expand Down
44 changes: 44 additions & 0 deletions src/zodbupdate/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import six
import zope.interface
import zodbupdate.main
import zodbupdate.serialize


class TestLogHandler(object):
Expand Down Expand Up @@ -94,6 +95,8 @@ class OtherFactory(persistent.Persistent):
self.conn = self.db.open()
self.root = self.conn.root()

self._skipped_symbs = zodbupdate.serialize.SKIP_SYMBS

def update(self, **args):
self.conn.close()
self.db.close()
Expand Down Expand Up @@ -132,6 +135,8 @@ def tearDown(self):
os.unlink(self.dbfile + '.tmp')
os.unlink(self.dbfile + '.lock')

zodbupdate.serialize.SKIP_SYMBS = self._skipped_symbs

def test_no_transaction_if_no_changes(self):
# If an update run doesn't produce any changes it won't commit the
# transaction to avoid superfluous clutter in the DB.
Expand Down Expand Up @@ -175,6 +180,45 @@ def getName(self):
renames = updater.processor.get_rules(implicit=True)
self.assertEqual({}, renames)

def test_skipped_types_are_left_untouched(self):
skipped = sys.modules['module1'].Factory()
self.root['skipped'] = skipped
transaction.commit()

self.assertIn(('ZODB.blob', 'Blob'), zodbupdate.serialize.SKIP_SYMBS)
zodbupdate.serialize.SKIP_SYMBS += [('module1', 'Factory')]

oid = self.root['skipped']._p_oid
old_pickle, old_serial = self.storage.load(oid)

self.update(
default_renames={
('module1', 'Factory'): ('module2', 'OtherFactory')})

pickle, serial = self.storage.load(oid)
self.assertEqual(old_pickle, pickle)
self.assertEqual(old_serial, serial)

def test_not_skipped_types_are_touched(self):
skipped = sys.modules['module1'].Factory()
self.root['skipped'] = skipped
transaction.commit()

self.assertNotIn(
('module1', 'Factory'),
zodbupdate.serialize.SKIP_SYMBS)

oid = self.root['skipped']._p_oid
old_pickle, old_serial = self.storage.load(oid)

self.update(
default_renames={
('module1', 'Factory'): ('module2', 'OtherFactory')})

pickle, serial = self.storage.load(oid)
self.assertNotEqual(old_pickle, pickle)
self.assertNotEqual(old_serial, serial)


class Python2Tests(Tests):

Expand Down

0 comments on commit 54233b1

Please sign in to comment.