[Zope3-checkins] CVS: Zope3/lib/python/ZODB - ConflictResolution.py:1.16 Serialize.py:1.6
Jeremy Hylton
jeremy@zope.com
Mon, 2 Dec 2002 13:10:39 -0500
Update of /cvs-repository/Zope3/lib/python/ZODB
In directory cvs.zope.org:/tmp/cvs-serv2990/lib/python/ZODB
Modified Files:
ConflictResolution.py Serialize.py
Log Message:
Revise separation of concerns between ConflictResolution and Serialize.
There is a ResolvedObjectAdapter that makes the state created by
_p_resolveConflict() look like a regular object using
__getattribute__() to override __class__.
Move the ResolveObjectReader to ConflictResolution from Serialize,
because it no longer knows much about how Serialize works. XXX It's
still hard to draw a clear separation between these two modules,
because conflict resolution depends (for efficiency and safety) on
working with the raw object state instead of instantiated objects.
=== Zope3/lib/python/ZODB/ConflictResolution.py 1.15 => 1.16 ===
--- Zope3/lib/python/ZODB/ConflictResolution.py:1.15 Tue Nov 26 12:41:21 2002
+++ Zope3/lib/python/ZODB/ConflictResolution.py Mon Dec 2 13:10:37 2002
@@ -11,49 +11,97 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
+
+# It's hard to draw a clear separation between these two modules,
+# because conflict resolution depends (for efficiency and safety) on
+# working with the raw object state instead of instantiated objects.
+
from cStringIO import StringIO
from cPickle import Pickler, PicklingError
from Transaction.Exceptions import ConflictError
-from ZODB.Serialize import ResolveObjectReader, getClassMetadata
+from ZODB.Serialize import BaseObjectReader, ObjectWriter
ResolvedSerial = "rs"
-class PersistentReference:
+__metaclass__ = type
+
+class ResolvedObjectAdapter:
+ """Adapt an object's raw state to the ObjectWriter protocol.
- def __repr__(self):
- return "PR(%s %s)" % (id(self), self.data)
+ ObjectWriter uses an object's __class__ and __getstate__() method
+ to determine how to pickle it. When conflict resolution occurs,
+ there is no instantiated object; the code deals with the concrete
+ state as returned by __getstate__(). This adapter allows the
+ state to be passed to ObjectWriter without instantiating the
+ object.
+
+ This object should only be used in conjunction with the ObjectWriter.
+ """
+
+ def __init__(self, ghost, state):
+ self._class = ghost.__class__
+ self._state = state
+
+ def __getattribute__(self, name):
+ if name == "__class__":
+ return self._class
+ else:
+ _super = super(ResolvedObjectAdapter, self).__getattribute__
+ return _super(name)
def __getstate__(self):
- raise "Can't pickle PersistentReference"
+ return self._state
+
+class PersistentReference:
+
+ __slots__ = "oid",
+
+ def __init__(self, oid):
+ self.oid = oid
-class PersistentReferenceFactory:
+class ResolveObjectReader(BaseObjectReader):
- data = None
-
- def __call__(self, oid):
- if self.data is None:
- self.data = {}
-
- r = self.data.get(oid)
- if r is None:
- r = PersistentReference()
- r.data = oid
- self.data[oid] = r
-
- return r
-
-def persistent_id(object):
- if getattr(object, '__class__', 0) is not PersistentReference:
- return None
- return object.data
+ bad_classes = {}
+
+ def __init__(self):
+ self._refs = {}
+
+ def _persistent_load(self, oid):
+ ref = self._refs.get(oid)
+ if ref is None:
+ ref = self._refs[oid] = PersistentReference(oid)
+ return ref
+
+ def getGhost(self, pickle):
+ unpickler = self._get_unpickler(pickle)
+ classmeta = unpickler.load()
+ if classmeta in self.bad_classes:
+ return None
+ else:
+ return self._new_object(*classmeta)
+
+ def getResolver(self, pickle):
+ # Get the conflict resolution method from a ghost rather
+ # than actually instantiating the object. _p_resolveConflict()
+ # is really a static method.
+ ghost = self.getGhost(pickle)
+ if ghost is None:
+ return None
+ resolve = getattr(ghost, "_p_resolveConflict", None)
+ if resolve is None:
+ klass = ghost.__class__
+ self.bad_classes[klass] = True
+ return None
+ else:
+ return resolve
class ConflictResolvingStorage:
"Mix-in class that provides conflict resolution handling for storages"
def tryToResolveConflict(self, oid, committedSerial, oldSerial, newpickle,
committedData=None):
- reader = ResolveObjectReader(PersistentReferenceFactory())
+ reader = ResolveObjectReader()
resolve = reader.getResolver(newpickle)
if resolve is None:
return None
@@ -73,11 +121,8 @@
return None
resolved = resolve(old, committed, newstate)
- file = StringIO()
- pickler = Pickler(file,1)
- pickler.persistent_id = persistent_id
- pickler.dump(getClassMetadata(resolve.im_self))
- pickler.dump(resolved)
- return file.getvalue(1)
+ writer = ObjectWriter()
+ obj = ResolvedObjectAdapter(resolve.im_self, resolved)
+ return writer.getState(obj)
except ConflictError:
return None
=== Zope3/lib/python/ZODB/Serialize.py 1.5 => 1.6 ===
--- Zope3/lib/python/ZODB/Serialize.py:1.5 Tue Nov 26 12:41:21 2002
+++ Zope3/lib/python/ZODB/Serialize.py Mon Dec 2 13:10:37 2002
@@ -21,6 +21,13 @@
ghost allows many persistent objects to be loaded while minimizing the
memory consumption of referenced but otherwise unused objects.
+Object introspection
+--------------------
+
+XXX Need to define what properties an object must have to be usable
+with the ObjectWriter. Should document how it determines what the
+class and state of an object are.
+
Pickle format
-------------
@@ -225,34 +232,6 @@
if object is not None:
return object
return self._conn[oid]
-
-class ResolveObjectReader(BaseObjectReader):
-
- bad_classes = {}
-
- def __init__(self, persistent_load):
- self._persistent_load = persistent_load
-
- def getGhost(self, pickle):
- unpickler = self._get_unpickler(pickle)
- classmeta = unpickler.load()
- if classmeta in self.bad_classes:
- return None
- else:
- return self._new_object(*classmeta)
-
- def getResolver(self, pickle):
- ghost = self.getGhost(pickle)
- if ghost is None:
- return None
- resolve = getattr(ghost, "_p_resolveConflict", None)
- if resolve is None:
- # XXX too bad. we just had classmeta.
- classmeta = getClassMetadata(ghost)
- self.bad_classes[classmeta] = True
- return None
- else:
- return resolve
def findrefs(p):
f = StringIO(p)