src/modules/misc.py
changeset 576 6ee6de304041
parent 565 1fb4f05220b9
child 621 6c144915eed1
--- a/src/modules/misc.py	Wed Oct 08 14:07:27 2008 -0700
+++ b/src/modules/misc.py	Thu Oct 09 02:02:50 2008 -0700
@@ -419,36 +419,117 @@
 
         return int(size)
 
-# Set the maximum number of timeouts before we giveup.  This can
-# be adjusted by setting the environment variable PKG_TIMEOUT_MAX
-MAX_TIMEOUT_COUNT = 4
+class TransportException(Exception):
+        """ Abstract base class for various transport exceptions """
+        def __init__(self):
+                self.count = 1
+
+class TransportFailures(TransportException):
+        """ This exception encapsulates multiple transport exceptions """
+
+        #
+        # This class is a subclass of TransportException so that calling
+        # code can reasonably 'except TransportException' and get either
+        # a single-valued or in this case a multi-valued instance.
+        #
+        def __init__(self):
+                TransportException.__init__(self)
+                self.exceptions = []
+
+        def append(self, exc):
+                assert isinstance(exc, TransportException)
+                for x in self.exceptions:
+                        if cmp(x, exc) == 0:
+                                x.count += 1
+                                return
+
+                self.exceptions.append(exc)
+
+        def __str__(self):
+                if len(self.exceptions) == 0:
+                        return "[no errors accumulated]"
 
-class TransferTimedOutException(Exception):
-        def __init__(self, args = None):
-                self.args = args
+                s = ""
+                for i, x in enumerate(self.exceptions):
+                        if len(self.exceptions) > 1:
+                                s += "%d: " % (i + 1)
+                        s += str(x)
+                        if x.count > 1:
+                                s += " (happened %d times)" % x.count
+                        s += "\n"
+                return s
+
+
+class TransferTimedOutException(TransportException):
+        def __init__(self, url, reason=None):
+                TransportException.__init__(self)
+                self.url = url
+                self.reason = reason
+
+        def __str__(self):
+                s = "Transfer from '%s' timed out" % self.url
+                if self.reason:
+                        s += ": %s" % self.reason
+                s += "."
+                return s
+
+        def __cmp__(self, other):
+                if not isinstance(other, TransferTimedOutException):
+                        return -1        
+                r = cmp(self.url, other.url)
+                if r != 0:
+                        return r
+                return cmp(self.reason, other.reason)
+
 
 # Retryable http errors.  These are the HTTP errors that we'll catch.  When we
 # catch them, we throw a TransferTimedOutException instead re-raising the
 # HTTPError and letting some other handler catch it.
 
+# XXX consider moving to pkg.client module
 retryable_http_errors = set((httplib.REQUEST_TIMEOUT, httplib.BAD_GATEWAY,
         httplib.GATEWAY_TIMEOUT))
 
 
-class TransferContentException(Exception):
-        def __init__(self, args = None):
-                self.args = args
+class TransferContentException(TransportException):
+        def __init__(self, url, reason=None):
+                TransportException.__init__(self)
+                self.url = url
+                self.reason = reason
+
+        def __str__(self):
+                s = "Transfer from '%s' failed" % self.url
+                if self.reason:
+                        s += ": %s" % self.reason
+                s += "."
+                return s
 
-class InvalidContentException(Exception):
+        def __cmp__(self, other):
+                if not isinstance(other, TransferContentException):
+                        return -1        
+                r = cmp(self.url, other.url)
+                if r != 0:
+                        return r
+                return cmp(self.reason, other.reason)
+
+
+class InvalidContentException(TransportException):
         def __init__(self, action, hashval):
+                TransportException.__init__(self)
                 self.action = action
                 self.hashval = hashval
 
         def __str__(self):
-                str = "Action with path %s should have hash %s. Computed " \
+                s = "Action with path %s should have hash %s. Computed " \
                     "hash %s instead." % (self.action.attrs["path"],
                     self.action.attrs["chash"], self.hashval)
-                return str
+                return s
+
+        def __cmp__(self, other):
+                if not isinstance(other, InvalidContentException):
+                        return -1        
+                return cmp(self.hashval, other.hashval)
+
 
 # Default maximum memory useage during indexing
 # This is a soft cap since memory usage is estimated.