[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)