5562: Use pycurl library (instead of requests) for Keep transactions.
[arvados.git] / sdk / python / arvados / errors.py
index 5ea54befdebcc735dabb3b13688f50743474f31e..bfd471ba52bee712a1e1768c91327ce28a9c6603 100644 (file)
@@ -1,14 +1,90 @@
 # errors.py - Arvados-specific exceptions.
 
+import json
+
+from apiclient import errors as apiclient_errors
+from collections import OrderedDict
+
+class ApiError(apiclient_errors.HttpError):
+    def _get_reason(self):
+        try:
+            return '; '.join(json.loads(self.content)['errors'])
+        except (KeyError, TypeError, ValueError):
+            return super(ApiError, self)._get_reason()
+
+
+class KeepRequestError(Exception):
+    """Base class for errors accessing Keep services."""
+    def __init__(self, message='', request_errors=(), label=""):
+        """KeepRequestError(message='', request_errors=(), label="")
+
+        :message:
+          A human-readable message describing what Keep operation
+          failed.
+
+        :request_errors:
+          An iterable that yields 2-tuples of keys (where the key refers to
+          some operation that was attempted) to the error encountered when
+          talking to it--either an exception, or an HTTP response object.
+          These will be packed into an OrderedDict, available through the
+          request_errors() method.
+
+        :label:
+          A label indicating the type of value in the 'key' position of request_errors.
+
+        """
+        self.label = label
+        self._request_errors = OrderedDict(request_errors)
+        if self._request_errors:
+            exc_reports = [self._format_error(*err_pair)
+                           for err_pair in self._request_errors.iteritems()]
+            base_msg = "{}: {}".format(message, "; ".join(exc_reports))
+        else:
+            base_msg = message
+        super(KeepRequestError, self).__init__(base_msg)
+        self.message = message
+
+    def _format_error(self, key, error):
+        if isinstance(error, HttpError):
+            err_fmt = "{} {} responded with {e.status_code} {e.reason}"
+        else:
+            err_fmt = "{} {} raised {e.__class__.__name__} ({e})"
+        return err_fmt.format(self.label, key, e=error)
+
+    def request_errors(self):
+        """request_errors() -> OrderedDict
+
+        The keys of the dictionary are described by `self.label`
+        The corresponding value is the exception raised when sending the
+        request to it."""
+        return self._request_errors
+
+
+class HttpError(Exception):
+    def __init__(self, status_code, reason):
+        self.status_code = status_code
+        self.reason = reason
+
+
+class ArgumentError(Exception):
+    pass
 class SyntaxError(Exception):
     pass
 class AssertionError(Exception):
     pass
-class NotFoundError(Exception):
-    pass
 class CommandFailedError(Exception):
     pass
-class KeepWriteError(Exception):
+class KeepReadError(KeepRequestError):
+    pass
+class KeepWriteError(KeepRequestError):
+    pass
+class NotFoundError(KeepReadError):
     pass
 class NotImplementedError(Exception):
     pass
+class NoKeepServersError(Exception):
+    pass
+class StaleWriterStateError(Exception):
+    pass
+class FeatureNotEnabledError(Exception):
+    pass