Skip to content

Commit

Permalink
Support for passing in known classes for given oids.
Browse files Browse the repository at this point in the history
Can be used if there is no 1-to-1 mapping from old to new classes.
  • Loading branch information
reinhardt committed Oct 25, 2018
1 parent 24b8772 commit fbd537c
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 16 deletions.
4 changes: 3 additions & 1 deletion CHANGES.txt
Expand Up @@ -4,7 +4,9 @@ Changes
0.6 (unreleased) 0.6 (unreleased)
---------------- ----------------


- ... - Support for passing in known classes for given oids.
Can be used if there is no 1-to-1 mapping from old to new classes.



0.5 (2010-10-07) 0.5 (2010-10-07)
---------------- ----------------
Expand Down
14 changes: 13 additions & 1 deletion src/zodbupdate/main.py
Expand Up @@ -15,6 +15,8 @@
import ZODB.FileStorage import ZODB.FileStorage
import ZODB.config import ZODB.config
import ZODB.serialize import ZODB.serialize
import ZODB.utils
import json
import logging import logging
import optparse import optparse
import pkg_resources import pkg_resources
Expand Down Expand Up @@ -57,7 +59,9 @@
parser.add_option( parser.add_option(
"--pack", action="store_true", dest="pack", "--pack", action="store_true", dest="pack",
help="pack the storage when done. use in conjunction of -c if you have blobs storage") help="pack the storage when done. use in conjunction of -c if you have blobs storage")

parser.add_option(
"-k", "--known-classes",
help="file containing a mapping from oids to module/class tuples in JSON format")




class DuplicateFilter(object): class DuplicateFilter(object):
Expand Down Expand Up @@ -108,6 +112,13 @@ def main():
if options.oid: if options.oid:
start_at = options.oid start_at = options.oid


class_by_oid = {}
if options.known_classes:
classes_file = open(options.known_classes, 'r')
class_by_oid = {ZODB.utils.repr_to_oid(key): value
for key, value in json.load(classes_file).items()}
classes_file.close()

rename_rules = {} rename_rules = {}
for entry_point in pkg_resources.iter_entry_points('zodbupdate'): for entry_point in pkg_resources.iter_entry_points('zodbupdate'):
rules = entry_point.load() rules = entry_point.load()
Expand All @@ -119,6 +130,7 @@ def main():
storage, storage,
dry=options.dry_run, dry=options.dry_run,
renames=rename_rules, renames=rename_rules,
class_by_oid=class_by_oid,
start_at=start_at, start_at=start_at,
debug=options.debug, debug=options.debug,
pickler_name=options.pickler) pickler_name=options.pickler)
Expand Down
32 changes: 21 additions & 11 deletions src/zodbupdate/serialize.py
Expand Up @@ -144,21 +144,25 @@ class ObjectRenamer(object):
- in class information (first pickle of the record). - in class information (first pickle of the record).
""" """


def __init__(self, changes, pickler_name='C'): def __init__(self, changes, class_by_oid={}, pickler_name='C'):
self.__added = dict() self.__added = dict()
self.__changes = dict() self.__changes = dict()
for old, new in changes.iteritems(): for old, new in changes.iteritems():
self.__changes[tuple(old.split(' '))] = tuple(new.split(' ')) self.__changes[tuple(old.split(' '))] = tuple(new.split(' '))
self.__class_by_oid = class_by_oid
self.__changed = False self.__changed = False
self.__pickler_name = pickler_name self.__pickler_name = pickler_name


def __update_symb(self, symb_info): def __update_symb(self, symb_info, oid=None):
"""This method look in a klass or symbol have been renamed or """This method look in a klass or symbol have been renamed or
not. If the symbol have not been renamed explicitly, it's not. If the symbol have not been renamed explicitly, it's
loaded and its location is checked to see if it have moved as loaded and its location is checked to see if it have moved as
well. well.
""" """
if symb_info in self.__changes: if oid in self.__class_by_oid:
self.__changed = True
return self.__class_by_oid[oid]
elif symb_info in self.__changes:
self.__changed = True self.__changed = True
return self.__changes[symb_info] return self.__changes[symb_info]
else: else:
Expand Down Expand Up @@ -195,15 +199,21 @@ def __persistent_load(self, reference):
""" """
if isinstance(reference, tuple): if isinstance(reference, tuple):
oid, klass_info = reference oid, klass_info = reference
if isinstance(klass_info, tuple): if oid in self.__class_by_oid:
klass_info = self.__update_symb(klass_info) self.__changed = True
klass_info = find_global(*self.__class_by_oid[oid], Broken=ZODBBroken)
elif isinstance(klass_info, tuple):
klass_info = self.__update_symb(klass_info, oid=oid)
return ZODBReference((oid, klass_info)) return ZODBReference((oid, klass_info))
if isinstance(reference, list): if isinstance(reference, list):
mode, information = reference mode, information = reference
if mode == 'm': if mode == 'm':
database_name, oid, klass_info = information database_name, oid, klass_info = information
if isinstance(klass_info, tuple): if oid in self.__class_by_oid:
klass_info = self.__update_symb(klass_info) self.__changed = True
klass_info = find_global(*self.__class_by_oid[oid], Broken=ZODBBroken)
elif isinstance(klass_info, tuple):
klass_info = self.__update_symb(klass_info, oid=oid)
return ZODBReference(['m', (database_name, oid, klass_info)]) return ZODBReference(['m', (database_name, oid, klass_info)])
return ZODBReference(reference) return ZODBReference(reference)


Expand All @@ -230,7 +240,7 @@ def __pickler(self, output_file):
pickler.persistent_id = self.__persistent_id pickler.persistent_id = self.__persistent_id
return pickler return pickler


def __update_class_meta(self, class_meta): def __update_class_meta(self, class_meta, oid):
"""Update class information, which can contain information """Update class information, which can contain information
about a renamed class. about a renamed class.
""" """
Expand All @@ -242,10 +252,10 @@ def __update_class_meta(self, class_meta):
u'Warning: Missing factory for %s' % u' '.join(symb_info)) u'Warning: Missing factory for %s' % u' '.join(symb_info))
return (symb_info, args) return (symb_info, args)
elif isinstance(symb, tuple): elif isinstance(symb, tuple):
return self.__update_symb(symb), args return self.__update_symb(symb, oid=oid), args
return class_meta return class_meta


def rename(self, input_file): def rename(self, input_file, oid):
"""Take a ZODB record (as a file object) as input. We load it, """Take a ZODB record (as a file object) as input. We load it,
replace any reference to renamed class we know of. If any replace any reference to renamed class we know of. If any
modification are done, we save the record again and return it, modification are done, we save the record again and return it,
Expand All @@ -257,7 +267,7 @@ def rename(self, input_file):
class_meta = unpickler.load() class_meta = unpickler.load()
data = unpickler.load() data = unpickler.load()


class_meta = self.__update_class_meta(class_meta) class_meta = self.__update_class_meta(class_meta, oid)


if not (self.__changed or if not (self.__changed or
(hasattr(unpickler, 'need_repickle') and (hasattr(unpickler, 'need_repickle') and
Expand Down
6 changes: 3 additions & 3 deletions src/zodbupdate/update.py
Expand Up @@ -30,12 +30,12 @@
class Updater(object): class Updater(object):
"""Update class references for all current objects in a storage.""" """Update class references for all current objects in a storage."""


def __init__(self, storage, dry=False, renames=None, def __init__(self, storage, dry=False, renames=None, class_by_oid={},
start_at='0x00', debug=False, pickler_name='C'): start_at='0x00', debug=False, pickler_name='C'):
self.dry = dry self.dry = dry
self.storage = storage self.storage = storage
self.processor = zodbupdate.serialize.ObjectRenamer( self.processor = zodbupdate.serialize.ObjectRenamer(
renames or {}, pickler_name) renames or {}, class_by_oid=class_by_oid, pickler_name=pickler_name)
self.start_at = start_at self.start_at = start_at
self.debug = debug self.debug = debug


Expand All @@ -62,7 +62,7 @@ def __call__(self):
for oid, serial, current in self.records: for oid, serial, current in self.records:
logger.debug('Processing OID %s' % ZODB.utils.oid_repr(oid)) logger.debug('Processing OID %s' % ZODB.utils.oid_repr(oid))


new = self.processor.rename(current) new = self.processor.rename(current, oid)
if new is None: if new is None:
continue continue


Expand Down

0 comments on commit fbd537c

Please sign in to comment.