[Zodb-checkins] CVS: Zope/lib/python/ZODB - BaseStorage.py:1.20.4.3 Connection.py:1.72.4.1 POSException.py:1.12.4.4

Chris McDonough chrism@zope.com
Tue, 10 Sep 2002 23:37:09 -0400


Update of /cvs-repository/Zope/lib/python/ZODB
In directory cvs.zope.org:/tmp/cvs-serv22446/lib/python/ZODB

Modified Files:
      Tag: chrism-install-branch
	BaseStorage.py Connection.py POSException.py 
Log Message:
Merging chrism-install-branch with HEAD changes.


=== Zope/lib/python/ZODB/BaseStorage.py 1.20.4.2 => 1.20.4.3 ===
--- Zope/lib/python/ZODB/BaseStorage.py:1.20.4.2	Tue Sep  3 03:43:47 2002
+++ Zope/lib/python/ZODB/BaseStorage.py	Tue Sep 10 23:36:38 2002
@@ -108,12 +108,14 @@
     def tpc_abort(self, transaction):
         self._lock_acquire()
         try:
-            if transaction is not self._transaction: return
+            if transaction is not self._transaction:
+                return
             self._abort()
             self._clear_temp()
-            self._transaction=None
+            self._transaction = None
             self._commit_lock_release()
-        finally: self._lock_release()
+        finally:
+            self._lock_release()
 
     def _abort(self):
         """Subclasses should redefine this to supply abort actions"""
@@ -122,34 +124,36 @@
     def tpc_begin(self, transaction, tid=None, status=' '):
         self._lock_acquire()
         try:
-            if self._transaction is transaction: return
+            if self._transaction is transaction:
+                return
             self._lock_release()
             self._commit_lock_acquire()
             self._lock_acquire()
-            self._transaction=transaction
+            self._transaction = transaction
             self._clear_temp()
 
-            user=transaction.user
-            desc=transaction.description
-            ext=transaction._extension
-            if ext: ext=dumps(ext,1)
-            else: ext=""
-            self._ude=user, desc, ext
+            user = transaction.user
+            desc = transaction.description
+            ext = transaction._extension
+            if ext:
+                ext = dumps(ext,1)
+            else:
+                ext=""
+            self._ude = user, desc, ext
 
             if tid is None:
-                t=time.time()
-                t=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,)))
-                self._ts=t=t.laterThan(self._ts)
-                self._serial=`t`
+                now = time.time()
+                t = TimeStamp(*(time.gmtime(now)[:5] + (now % 60,)))
+                self._ts = t = t.laterThan(self._ts)
+                self._serial = `t`
             else:
-                self._ts=TimeStamp(tid)
-                self._serial=tid
-
-            self._tstatus=status
+                self._ts = TimeStamp(tid)
+                self._serial = tid
 
+            self._tstatus = status
             self._begin(self._serial, user, desc, ext)
-
-        finally: self._lock_release()
+        finally:
+            self._lock_release()
 
     def _begin(self, tid, u, d, e):
         """Subclasses should redefine this to supply transaction start actions.
@@ -159,7 +163,8 @@
     def tpc_vote(self, transaction):
         self._lock_acquire()
         try:
-            if transaction is not self._transaction: return
+            if transaction is not self._transaction:
+                return
             self._vote()
         finally:
             self._lock_release()
@@ -172,16 +177,17 @@
     def tpc_finish(self, transaction, f=None):
         self._lock_acquire()
         try:
-            if transaction is not self._transaction: return
+            if transaction is not self._transaction:
+                return
             try:
-                if f is not None: f()
-
-                u,d,e=self._ude
+                if f is not None:
+                    f()
+                u, d, e = self._ude
                 self._finish(self._serial, u, d, e)
                 self._clear_temp()
             finally:
-                self._ude=None
-                self._transaction=None
+                self._ude = None
+                self._transaction = None
                 self._commit_lock_release()
         finally:
             self._lock_release()


=== Zope/lib/python/ZODB/Connection.py 1.72 => 1.72.4.1 ===
--- Zope/lib/python/ZODB/Connection.py:1.72	Wed Aug 14 18:07:09 2002
+++ Zope/lib/python/ZODB/Connection.py	Tue Sep 10 23:36:38 2002
@@ -268,28 +268,44 @@
         self.__onCommitActions.append((method_name, args, kw))
         get_transaction().register(self)
 
+    # NB: commit() is responsible for calling tpc_begin() on the storage.
+    # It uses self._begun to track whether it has been called.  When
+    # self._begun is None, it has not been called.
+
+    # This arrangement allows us to handle the special case of a
+    # transaction with no modified objects.  It is possible for
+    # registration to be occur unintentionally and for a persistent
+    # object to compensate by making itself as unchanged.  When this
+    # happens, it's possible to commit a transaction with no modified
+    # objects.
+
+    # Since tpc_begin() may raise a ReadOnlyError, don't call it if there
+    # are no objects.  This avoids spurious (?) errors when working with
+    # a read-only storage.
+
     def commit(self, object, transaction):
         if object is self:
+            if self._begun is None:
+                self._storage.tpc_begin(transaction)
+                self._begun = 1
+
             # We registered ourself.  Execute a commit action, if any.
             if self.__onCommitActions is not None:
                 method_name, args, kw = self.__onCommitActions.pop(0)
                 apply(getattr(self, method_name), (transaction,) + args, kw)
             return
-        oid=object._p_oid
-        invalid=self._invalid
+        
+        oid = object._p_oid
+        invalid = self._invalid
         if oid is None or object._p_jar is not self:
             # new object
             oid = self.new_oid()
-            object._p_jar=self
-            object._p_oid=oid
+            object._p_jar = self
+            object._p_oid = oid
             self._creating.append(oid)
 
         elif object._p_changed:
-            if (
-                (invalid(oid) and not hasattr(object, '_p_resolveConflict'))
-                or
-                invalid(None)
-                ):
+            if invalid(oid) and not hasattr(object, '_p_resolveConflict'):
                 raise ConflictError(object=object)
             self._invalidating.append(oid)
 
@@ -297,7 +313,11 @@
             # Nothing to do
             return
 
-        stack=[object]
+        if self._begun is None:
+            self._storage.tpc_begin(transaction)
+            self._begun = 1
+
+        stack = [object]
 
         # Create a special persistent_id that passes T and the subobject
         # stack along:
@@ -351,12 +371,7 @@
                 self._creating.append(oid)
             else:
                 #XXX We should never get here
-                if (
-                    (invalid(oid) and
-                     not hasattr(object, '_p_resolveConflict'))
-                    or
-                    invalid(None)
-                    ):
+                if invalid(oid) and not hasattr(object, '_p_resolveConflict'):
                     raise ConflictError(object=object)
                 self._invalidating.append(oid)
 
@@ -517,8 +532,7 @@
             # storage to make sure that we don't miss an invaildation
             # notifications between the time we check and the time we
             # read.
-            invalid = self._invalid
-            if invalid(oid) or invalid(None):
+            if self._invalid(oid):
                 if not hasattr(object.__class__, '_p_independent'):
                     get_transaction().register(self)
                     raise ReadConflictError(object=object)
@@ -602,33 +616,33 @@
         if self.__onCommitActions is not None:
             del self.__onCommitActions
         self._storage.tpc_abort(transaction)
-        cache=self._cache
-        cache.invalidate(self._invalidated)
-        cache.invalidate(self._invalidating)
+        self._cache.invalidate(self._invalidated)
+        self._cache.invalidate(self._invalidating)
         self._invalidate_creating()
 
     def tpc_begin(self, transaction, sub=None):
-        if self._invalid(None): # Some nitwit invalidated everything!
-            raise ConflictError("transaction already invalidated")
-        self._invalidating=[]
-        self._creating=[]
+        self._invalidating = []
+        self._creating = []
+        self._begun = None
 
         if sub:
             # Sub-transaction!
-            _tmp=self._tmp
-            if _tmp is None:
-                _tmp=TmpStore.TmpStore(self._version)
-                self._tmp=self._storage
-                self._storage=_tmp
+            if self._tmp is None:
+                _tmp = TmpStore.TmpStore(self._version)
+                self._tmp = self._storage
+                self._storage = _tmp
                 _tmp.registerDB(self._db, 0)
 
-        self._storage.tpc_begin(transaction)
+            # It's okay to always call tpc_begin() for a sub-transaction
+            # because this isn't the real storage.
+            self._storage.tpc_begin(transaction)
+            self._begun = 1
 
     def tpc_vote(self, transaction):
         if self.__onCommitActions is not None:
             del self.__onCommitActions
         try:
-            vote=self._storage.tpc_vote
+            vote = self._storage.tpc_vote
         except AttributeError:
             return
         s = vote(transaction)


=== Zope/lib/python/ZODB/POSException.py 1.12.4.3 => 1.12.4.4 ===
--- Zope/lib/python/ZODB/POSException.py:1.12.4.3	Tue Sep  3 03:43:47 2002
+++ Zope/lib/python/ZODB/POSException.py	Tue Sep 10 23:36:38 2002
@@ -104,6 +104,24 @@
     def get_serials(self):
         return self.serials
 
+class DanglingReferenceError(TransactionError):
+    """The transaction stored an object A containing a reference to another
+    object B, but B does not exist
+
+    Instance attributes:
+
+    Aoid: oid of the object being written
+
+    Boid: referenced oid that does not have a corresponding object
+    """
+
+    def __init__(self,Aoid,Boid):
+        self.Aoid = Aoid
+        self.Boid = Boid
+
+    def __str__(self):
+        return "from %r to %r" % (self.Aoid,self.Boid)
+
 
 class ReadConflictError(ConflictError):
     """A conflict was detected at read time.