[Zope3-checkins] CVS: ZODB4/ZODB - ConflictResolution.py:1.13 Connection.py:1.78 DB.py:1.52 FileStorage.py:1.98

Jeremy Hylton jeremy@zope.com
Thu, 19 Sep 2002 14:22:05 -0400


Update of /cvs-repository/ZODB4/ZODB
In directory cvs.zope.org:/tmp/cvs-serv22608/ZODB

Modified Files:
	ConflictResolution.py Connection.py DB.py FileStorage.py 
Log Message:
Use new Serialize module for unpickling.

ConflictResolution:
    Simplify PersistentReferenceFactory.
    Use the ResolveUnpickler to get a resolve method and get state
    from pickles.

Connection:
    Each Connection gets a ConnectionUnpickler instance.
    Delete all unpickling code.
    Change failure return value of tryToResolveConflict() to None.

DB:
    Delete _classFactory method.  It's now a function in Serialize.
    Hack: Change the way the root is created.  This needs to be
    cleaner.



=== ZODB4/ZODB/ConflictResolution.py 1.12 => 1.13 ===
--- ZODB4/ZODB/ConflictResolution.py:1.12	Thu Sep  5 16:20:01 2002
+++ ZODB4/ZODB/ConflictResolution.py	Thu Sep 19 14:22:04 2002
@@ -15,20 +15,9 @@
 from cPickle import Unpickler, Pickler
 
 from Transaction.Exceptions import ConflictError
+from ZODB.Serialize import ResolveUnpickler
 
-bad_classes={}
-def bad_class(class_tuple):
-    if (class_tuple in bad_classes) or class_tuple[0][0] == '*':
-        # if we've seen the class before or if it's a ZClass, we know that
-        # we can't resolve the conflict
-        return 1
-
-ResolvedSerial='rs'
-
-def _classFactory(location, name,
-                  _silly=('__doc__',), _globals={}):
-    return getattr(__import__(location, _globals, _globals, _silly),
-                   name)
+ResolvedSerial = 'rs'
 
 def state(storage, oid, serial, prfactory, p=''):
     p=p or storage.loadSerial(oid, serial)
@@ -39,7 +28,6 @@
     state=unpickler.load()
     return state
 
-
 class PersistentReference:
 
     def __repr__(self):
@@ -53,22 +41,18 @@
     data = None
     
     def __call__(self, oid):
-        data = self.data
-        if not data:
-            data = self.data = {}
+        if self.data is None:
+            self.data = {}
 
-        r = data.get(oid, None)
+        r = self.data.get(oid)
         if r is None:
             r = PersistentReference()
             r.data = oid
-            data[oid] = r
+            self.data[oid] = r
 
         return r
 
-def persistent_id(object,
-                  PersistentReference=PersistentReference,
-                  getattr=getattr
-                  ):
+def persistent_id(object):
     if getattr(object, '__class__', 0) is not PersistentReference:
         return None
     return object.data
@@ -78,36 +62,23 @@
 
     def tryToResolveConflict(self, oid, committedSerial, oldSerial, newpickle,
                              committedData=''):
-        try:
-            file = StringIO(newpickle)
-            unpickler = Unpickler(file)
-            prfactory = PersistentReferenceFactory()
-            unpickler.persistent_load = prfactory
-            class_tuple = unpickler.load()[0]
-            if bad_class(class_tuple):
-                return 0
-
-            newstate = unpickler.load()
-            klass = _classFactory(class_tuple[0], class_tuple[1])
-            inst = klass.__new__(klass)
-
+        unpickler = ResolveUnpickler(PersistentReferenceFactory(),
+                                     can_resolve_class)
+        resolve = unpickler.getResolveMethod(newpickle)
+        if resolve is None:
+            return None
+        else:
             try:
-                resolve = inst._p_resolveConflict
-            except AttributeError:
-                bad_classes[class_tuple] = 1
-                return 0
-
-            old = state(self, oid, oldSerial, prfactory)
-            committed = state(self, oid, committedSerial, prfactory,
-                              committedData)
-
-            resolved = resolve(old, committed, newstate)
-
-            file = StringIO()
-            pickler = Pickler(file,1)
-            pickler.persistent_id = persistent_id
-            pickler.dump(class_tuple)
-            pickler.dump(resolved)
-            return file.getvalue(1)
-        except ConflictError:
-            return 0
+                p = self.loadSerial(oid, oldSerial)
+                old = unpickler.getState(p)
+                committed = unpickler.getState(committedData)
+                resolved = resolve(old, committed, newstate)
+
+                file = StringIO()
+                pickler = Pickler(file,1)
+                pickler.persistent_id = persistent_id
+                pickler.dump(class_tuple)
+                pickler.dump(resolved)
+                return file.getvalue(1)
+            except ConflictError:
+                return None


=== ZODB4/ZODB/Connection.py 1.77 => 1.78 ===
--- ZODB4/ZODB/Connection.py:1.77	Tue Sep 17 17:44:25 2002
+++ ZODB4/ZODB/Connection.py	Thu Sep 19 14:22:04 2002
@@ -48,12 +48,14 @@
 from ZODB.ConflictResolution import ResolvedSerial
 from ZODB.IConnection import IConnection
 from ZODB.POSException import ConflictError
+from ZODB.Serialize import ConnectionUnpickler, \
+     new_persistent_id, getClassMetadata
 from Transaction import get_transaction
 
 from Persistence.Cache import Cache
 from zLOG import LOG, ERROR, BLATHER, INFO
 
-from cPickle import Unpickler, Pickler
+import cPickle
 from cStringIO import StringIO
 import sys
 import time
@@ -96,6 +98,7 @@
         self.new_oid = db._storage.new_oid
         self._version = version
         self._cache = cache = Cache(cache_size, cache_deactivate_after)
+        self._unpickler = ConnectionUnpickler(self, self._cache)
 
         # _invalidated queues invalidate messages delivered from the DB
         self._invalidated = Set()
@@ -116,79 +119,6 @@
     def root(self):
         return self['\0\0\0\0\0\0\0\0']
 
-    def _persistent_load(self, oid):
-        # persistent_load function to pass to Unpickler
-        if isinstance(oid, TupleType):
-            # Quick instance reference.  We know all we need to know
-            # to create the instance w/o hitting the db, so go for it!
-            oid, klass = oid
-            object = self._cache.get(oid)
-            if object is not None:
-                return object
-
-            if isinstance(klass, TupleType):
-                module, name = klass
-                try:
-                    klass = self._db._classFactory(self, module, name)
-                except: # XXX except what?
-                    LOG('ZODB', INFO, "Couldn't load class %s.%s" % (
-                        module, name), error=sys.exc_info())
-                    # Couldn't get the class.  More current data may
-                    # be in the object's actual record.
-                    return self[oid]
-            
-            # CXX This isn't quite right, but I'm not sure what is.
-            # The contacts are a bit unclear.
-            object = klass.__new__(klass)
-            
-            object._p_oid = oid
-            object._p_jar = self
-            object._p_changed = None
-            
-            self._cache[oid] = object
-
-            return object
-
-        object = self._cache.get(oid)
-        if object is not None:
-            return object
-        return self[oid]
-
-    def _get_unpickler(self, p):
-        file = StringIO(p)
-        unpickler = Unpickler(file)
-        unpickler.persistent_load = self._persistent_load
-        return unpickler
-
-    def _unpickle_object(self, p):
-        unpickler = self._get_unpickler(p)
-        klass, args = unpickler.load()
-
-        if isinstance(klass, TupleType):
-            module, name = klass
-            klass = self._db._classFactory(self, module, name)
-        
-        if (args is None or
-            not args and not hasattr(klass, '__getinitargs__')):
-            # XXX (jeremy) Not sure what the comment below means.
-            # CXX This isn't quite right, but I'm not sure what is.
-            # The contacts are a bit unclear.
-            object = klass.__new__(klass)
-        else:
-            object = klass(*args)
-            # XXX What does is the following test supposed to
-            # accomplish? 
-            if klass is not type:
-                object.__dict__.clear()
-                
-        return object
-
-    def _unpickle_state(self, p):
-        unpickler = self._get_unpickler(p)
-        unpickler.load() # returns klass, args
-        state = unpickler.load()
-        return state
-    
     def __getitem__(self, oid):
         # assume that a cache cannot store None as a valid object
         object = self._cache.get(oid)
@@ -196,7 +126,7 @@
             return object
 
         p, serial = self._storage.load(oid, self._version)
-        object = self._unpickle_object(p)
+        object = self._unpickler.getGhost(p)
 
         object._p_oid = oid
         object._p_jar = self
@@ -240,15 +170,7 @@
             else:
                 invalid = 0
 
-            state = self._unpickle_state(p)
-
-            if hasattr(object, '__setstate__'):
-                object.__setstate__(state)
-            else:
-                d = object.__dict__
-                for k, v in state.iteritems():
-                    d[k] = v
-
+            self._unpickler.setGhostState(object, p)
             object._p_serial = serial
 
             if invalid:
@@ -359,7 +281,7 @@
         stack = [object]
         
         file = StringIO()
-        pickler = Pickler(file, 1)
+        pickler = cPickle.Pickler(file, 1)
         pickler.persistent_id = new_persistent_id(self, stack)
 
         while stack:
@@ -380,22 +302,12 @@
                 raise ConflictError(oid=oid)
             self._modified.add(oid)
 
-        klass = pobject.__class__
-
-        if hasattr(klass, '__getinitargs__'):
-            args = pobject.__getinitargs__()
-            len(args) # XXX Assert it's a sequence
-        else:
-            args = None # New no-constructor protocol!
-
-        module = getattr(klass,'__module__','')
-        if module:
-            klass = module, klass.__name__
+        module, classname, newargs = getClassMetadata(pobject)
         state = pobject.__getstate__()
 
         file.seek(0)
         pickler.clear_memo()
-        pickler.dump((klass, args))
+        pickler.dump((module, classname, newargs))
         pickler.dump(state)
         p = file.getvalue(1)
         s = self._storage.store(oid, serial, p, self._version, transaction)
@@ -586,58 +498,6 @@
         self._invalidated.clear()
         self._cache.incrgc() # This is a good time to do some GC
         
-def new_persistent_id(self, stack):
-    # XXX need a doc string.  not sure if the one for persistent_id()
-    # below is correct.
-
-    # Create a special persistent_id that captures T and the subobject
-    # stack in a closure.
-
-    def persistent_id(object):
-        """Test if an object is persistent, returning an oid if it is.
-
-        This function is used by the pickler to test whether an object
-        is persistent.  If it isn't, the function returns None and the
-        object is included in the pickle for the current persistent
-        object.
-
-        If it is persistent, it returns the oid and sometimes a tuple
-        with other stuff.
-        """
-        
-        if (not hasattr(object, '_p_oid') or
-            isinstance(object, ClassType)):
-            return None
-        
-        oid = object._p_oid
-
-        # I'd like to write something like this --
-        # if isinstance(oid, types.MemberDescriptor):
-        # -- but I can't because the type doesn't have a canonical name.
-        # Instead, we'll assert that an oid must always be a string
-        if not (isinstance(oid, StringType) or oid is None):
-            return None
-        
-        if oid is None or object._p_jar is not self:
-            oid = self.new_oid()
-            object._p_jar = self
-            object._p_oid = oid
-            stack.append(object)
-
-        klass = object.__class__
-
-        if klass is type:
-            return oid
-        if hasattr(klass, '__getinitargs__'):
-            return oid
-        module = getattr(klass,'__module__', '')
-        if module:
-            klass = module, klass.__name__
-
-        return oid, klass
-    
-    return persistent_id
-
 class Rollback:
     """Rollback changes associated with savepoint"""
 


=== ZODB4/ZODB/DB.py 1.51 => 1.52 ===
--- ZODB4/ZODB/DB.py:1.51	Mon Aug  5 16:08:38 2002
+++ ZODB4/ZODB/DB.py	Thu Sep 19 14:22:04 2002
@@ -84,7 +84,7 @@
             # The pickle must be in the special ZODB format.
             file = cStringIO.StringIO()
             p = cPickle.Pickler(file, 1)
-            p.dump((root.__class__, None))
+            p.dump((root.__class__.__module__, root.__class__.__name__, None))
             p.dump(root.__getstate__())
             t = Transaction()
             t.note("initial database creation")
@@ -110,11 +110,6 @@
         if m[1]: m=m[0]/m[1]
         else: m=None
         return m
-
-    def _classFactory(self, connection, location, name,
-                      _silly=('__doc__',), _globals={}):
-        return getattr(__import__(location, _globals, _globals, _silly),
-                       name)
             
     def _closeConnection(self, connection):
         """Return a connection to the pool"""


=== ZODB4/ZODB/FileStorage.py 1.97 => 1.98 ===
--- ZODB4/ZODB/FileStorage.py:1.97	Thu Aug  1 12:23:53 2002
+++ ZODB4/ZODB/FileStorage.py	Thu Sep 19 14:22:04 2002
@@ -680,7 +680,7 @@
 
                 if serial != oserial:
                     data=self.tryToResolveConflict(oid, oserial, serial, data)
-                    if not data:
+                    if data is None:
                         raise POSException.ConflictError(oid=oid,
                                                 serials=(oserial, serial))
             else: