[Zope3-checkins] CVS: Zope3/lib/python/ZODB - FileStorage.py:1.96 POSException.py:1.15

Jeremy Hylton jeremy@zope.com
Thu, 25 Jul 2002 17:32:11 -0400


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

Modified Files:
	FileStorage.py POSException.py 
Log Message:
Rationalize use of UndoError().


=== Zope3/lib/python/ZODB/FileStorage.py 1.95 => 1.96 ===
 
 import ZODB.DB
 from ZODB import BaseStorage, ConflictResolution, POSException
-from ZODB.POSException import UndoError, POSKeyError
+from ZODB.POSException import UndoError, POSKeyError, MultipleUndoErrors
 from ZODB.TimeStamp import TimeStamp
 from ZODB.lock_file import lock_file
 from ZODB.utils import t32, p64, U64, cp
@@ -148,6 +148,8 @@
 DATA_HDR_LEN = 42
 DATA_VERSION_HDR_LEN = 58
 
+packed_version='FS21'
+
 def warn(message, *data):
     LOG('ZODB FS',WARNING, "%s  warn: %s\n"
         % (packed_version, (message % data)))
@@ -159,9 +161,8 @@
     LOG('ZODB FS',PANIC,"%s ERROR: %s\n" % (packed_version, (message % data)))
 
 def panic(message, *data):
-    message=message%data
-    LOG('ZODB FS',PANIC,"%s ERROR: %s\n" % (packed_version, message))
-    raise CorruptedTransactionError, message
+    LOG('ZODB FS',PANIC,"%s ERROR: %s\n" % (packed_version, (message % data)))
+    raise CorruptedTransactionError(message)
 
 class FileStorageError(POSException.StorageError):
     pass
@@ -174,11 +175,13 @@
 
 class CorruptedFileStorageError(FileStorageError,
                                 POSException.StorageSystemError):
-    """Corrupted file storage
-    """
+    """Corrupted file storage."""
+
+class CorruptedTransactionError(CorruptedFileStorageError):
+    pass
 
-class CorruptedTransactionError(CorruptedFileStorageError): pass
-class CorruptedDataError(CorruptedFileStorageError): pass
+class CorruptedDataError(CorruptedFileStorageError):
+    pass
 
 class FileStorageQuotaError(FileStorageError,
                             POSException.StorageSystemError):
@@ -196,8 +199,6 @@
                     version_cache_deactivate_after)
     return db
 
-packed_version='FS21'
-
 class FileStorage(BaseStorage.BaseStorage,
                   ConflictResolution.ConflictResolvingStorage):
     _packt=z64
@@ -548,7 +549,7 @@
             h = self._file.read(34)
             _oid = h[:8]
             if _oid != oid:
-                raise CorruptedData, h
+                raise CorruptedDataError(h)
             vlen = unpack(">H", h[-2:])[0]
             if vlen:
                 # If there is a version, find out its name and let
@@ -611,7 +612,7 @@
                 seek(pos)
                 h=read(DATA_HDR_LEN)
                 doid,dserial,prev,tloc,vlen,plen = unpack(">8s8s8s8sH8s", h)
-                if doid != oid: raise CorruptedDataError, h
+                if doid != oid: raise CorruptedDataError(h)
                 if dserial == serial: break # Yeee ha!
                 # Keep looking for serial
                 pos=U64(prev)
@@ -640,7 +641,7 @@
             seek(pos)
             doid,serial,prev,tloc,vlen = unpack(">8s8s8s8sH", file.read(34))
             if doid != oid:
-                raise CorruptedDataError, pos
+                raise CorruptedDataError(pos)
             if vlen:
                 seek(24,1) # skip plen, pnv, and pv
                 return file.read(vlen)
@@ -663,7 +664,7 @@
                 read=file.read
                 h=read(DATA_HDR_LEN)
                 doid,oserial,sprev,stloc,vlen,splen = unpack(">8s8s8s8sH8s", h)
-                if doid != oid: raise CorruptedDataError, h
+                if doid != oid: raise CorruptedDataError(h)
                 if vlen:
                     pnv=read(8) # non-version data pointer
                     read(8) # skip past version link
@@ -749,7 +750,7 @@
                 h = self._file.read(42)
                 doid,oserial,sprev,stloc,vlen,splen = unpack(">8s8s8s8sH8s", h)
                 if doid != oid:
-                    raise CorruptedDataError, h
+                    raise CorruptedDataError(h)
             # Calculate the file position in the temporary file
             here = self._pos + self._tfile.tell() + self._thl
             # And update the temp file index
@@ -899,7 +900,8 @@
         file.seek(pos)
         h=read(DATA_HDR_LEN)
         roid,serial,sprev,stloc,vlen,splen = unpack(">8s8s8s8sH8s", h)
-        if roid != oid: raise UndoError('Invalid undo transaction id')
+        if roid != oid:
+            raise UndoError(oid, 'Invalid undo transaction id')
         if vlen:
             read(16) # skip nv pointer and version previous pointer
             version=read(vlen)
@@ -954,29 +956,27 @@
                 oid, ipos, tpos)
             # Versions of undone record and current record *must* match!
             if cver != version:
-                raise UndoError('Current and undone versions differ')
+                raise UndoError(oid, 'Current and undone versions differ')
 
             if cdataptr != pos:
                 # We aren't sure if we are talking about the same data
                 try:
-                    if (
-                        # The current record wrote a new pickle
-                        cdataptr == tipos
-                        or
+                    if (# The current record wrote a new pickle
+                        cdataptr == tipos or
                         # Backpointers are different
                         _loadBackPOS(self._file, oid, p64(pos)) !=
                         _loadBackPOS(self._file, oid, p64(cdataptr))
                         ):
                         if pre and not tpos:
-                            copy=0 # we'll try to do conflict resolution
+                            copy = 0 # we'll try to do conflict resolution
                         else:
                             # We bail if:
                             # - We don't have a previous record, which should
                             #   be impossible.
-                            raise UndoError
+                            raise UndoError(oid, "No previous record")
                 except KeyError:
                     # LoadBack gave us a key error. Bail.
-                    raise UndoError
+                    raise UndoError(oid, "_loadBack() failed")
 
         version, snv = self._getVersion(oid, pre)
         if copy:
@@ -988,13 +988,13 @@
             bdata = _loadBack(self._file, oid, p64(pre))[0]
         except KeyError:
             # couldn't find oid; what's the real explanation for this?
-            raise UndoError("_loadBack() failed for %s" % repr(oid))
+            raise UndoError(oid, "_loadBack() failed")
         data=self.tryToResolveConflict(oid, cserial, serial, bdata, cdata)  
 
         if data:
             return data, 0, version, snv, ipos
 
-        raise UndoError('Some data were modified by a later transaction')
+        raise UndoError(oid, 'Some data were modified by a later transaction')
 
     def transactionalUndo(self, transaction_id, transaction):
         """Undo a transaction, given by transaction_id.
@@ -1024,11 +1024,11 @@
             self._file.seek(tpos)
             h = self._file.read(TRANS_HDR_LEN)
             if len(h) != TRANS_HDR_LEN or h[:8] != tid: 
-                raise UndoError, 'Invalid undo transaction id'
+                raise UndoError(None, 'Invalid undo transaction id')
             if h[16] == 'u':
                 return
             if h[16] != ' ':
-                raise UndoError, 'non-undoable transaction'
+                raise UndoError(None, 'non-undoable transaction')
             tl = U64(h[8:16])
             ul, dl, el = struct.unpack(">HHH", h[17:TRANS_HDR_LEN])
             tend = tpos + tl
@@ -1082,9 +1082,15 @@
 
                 pos=pos+dlen
                 if pos > tend:
-                    raise UndoError, 'non-undoable transaction'
+                    raise UndoError(None, 'non-undoable transaction')
 
-            if failures: raise UndoError(failures)
+            if failures:
+                vals = failures.values()
+                if len(vals) == 1:
+                    raise vals[0]
+                else:
+                    raise MultipleUndoErrors([(e._oid, e._reason)
+                                              for e in vals])
             self._tindex.update(tindex)
             return tindex.keys()            
 
@@ -1096,8 +1102,8 @@
         try:
             packt=self._packt
             if packt is None:
-                raise UndoError(
-                    'Undo is currently disabled for database maintenance.<p>')
+                raise UndoError(None,
+                    'Undo is currently disabled for database maintenance.')
             pos=self._pos
             if pos < 39: return []
             file=self._file
@@ -1652,10 +1658,10 @@
         # first 8 bytes are oid, second 8 bytes are serialno
         h = self._file.read(16)
         if len(h) < 16:
-            raise CorruptedDataError, h
+            raise CorruptedDataError(h)
         if h[:8] != oid:
             h = h + self._file.read(26) # get rest of header
-            raise CorruptedDataError, h
+            raise CorruptedDataError(h)
         return h[8:]
 
 def shift_transactions_forward(index, vindex, tindex, file, pos, opos):


=== Zope3/lib/python/ZODB/POSException.py 1.14 => 1.15 ===
 from types import StringType, DictType
 from ZODB import utils
 
+def _fmt_oid(oid):
+    return "%016x" % utils.U64(oid)
+
+def _fmt_undo(oid, reason):
+    s = reason and (": %s" % reason) or ""
+    return "Undo error %s%s" % (_fmt_oid(oid), s)
+
 class POSError(StandardError):
     """Persistent object system error."""
 
@@ -29,7 +36,7 @@
     """Key not found in database."""
 
     def __str__(self):
-        return "%016x" % utils.U64(self.args[0])
+        return _fmt_oid(self.args[0])
 
 class ConflictError(_ConflictError):
     """Two transactions tried to modify the same object at once.
@@ -77,12 +84,12 @@
     def __str__(self):
         extras = []
         if self.oid:
-            extras.append("oid %016x" % utils.U64(self.oid))
+            extras.append("oid %016x" % _fmt_oid(self.oid))
         if self.class_name:
             extras.append("class %s" % self.class_name)
         if self.serials:
             extras.append("serial was %016x, now %016x" %
-                          tuple(map(utils.U64, self.serials)))
+                          tuple(map(_fmt_oid, self.serials)))
         if extras:
             return "%s (%s)" % (self.message, ", ".join(extras))
         else:
@@ -103,7 +110,6 @@
     def get_serials(self):
         return self.serials
 
-
 class ReadConflictError(ConflictError):
     """Conflict detected when object was loaded.
 
@@ -127,28 +133,24 @@
 
 class UndoError(POSError):
     """An attempt was made to undo a non-undoable transaction."""
-    
-    def __init__(self, *reason):
-        if len(reason) == 1:
-            reason = reason[0]
-        self.__reason = reason
-
-    def __repr__(self):
-        reason=self.__reason
-        if type(reason) is not DictType:
-            if reason: return str(reason)
-            return "non-undoable transaction"
-        r=[]
-        for oid, reason in reason.items():
-            if reason:
-                r.append("Couldn't undo change to %s because %s"
-                         % (`oid`, reason))
-            else:
-                r.append("Couldn't undo change to %s" % (`oid`))
 
-        return "\n".join(r)
+    def __init__(self, oid, reason=None):
+        self._oid = oid
+        self._reason = reason
 
-    __str__=__repr__
+    def __str__(self):
+        return _fmt_undo(self._oid, self._reason)
+
+class MultipleUndoErrors(UndoError):
+    """Several undo errors occured during a single transaction."""
+    
+    def __init__(self, errs):
+        # provide an oid and reason for clients that only look at that
+        UndoError.__init__(self, *errs[0])
+        self._errs = errs
+
+    def __str__(self):
+        return "\n".join([_fmt_undo(*pair) for pair in self._errs])
 
 class StorageError(POSError):
     pass