18700: Merge branch 'main'
[arvados.git] / sdk / python / arvados / errors.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: Apache-2.0
4
5 # errors.py - Arvados-specific exceptions.
6
7 import json
8
9 from apiclient import errors as apiclient_errors
10 from collections import OrderedDict
11
12 class ApiError(apiclient_errors.HttpError):
13     def _get_reason(self):
14         try:
15             return '; '.join(json.loads(self.content.decode('utf-8'))['errors'])
16         except (KeyError, TypeError, ValueError):
17             return super(ApiError, self)._get_reason()
18
19
20 class KeepRequestError(Exception):
21     """Base class for errors accessing Keep services."""
22     def __init__(self, message='', request_errors=(), label=""):
23         """KeepRequestError(message='', request_errors=(), label="")
24
25         :message:
26           A human-readable message describing what Keep operation
27           failed.
28
29         :request_errors:
30           An iterable that yields 2-tuples of keys (where the key refers to
31           some operation that was attempted) to the error encountered when
32           talking to it--either an exception, or an HTTP response object.
33           These will be packed into an OrderedDict, available through the
34           request_errors() method.
35
36         :label:
37           A label indicating the type of value in the 'key' position of request_errors.
38
39         """
40         self.label = label
41         self._request_errors = OrderedDict(request_errors)
42         if self._request_errors:
43             exc_reports = [self._format_error(*err_pair)
44                            for err_pair in self._request_errors.items()]
45             base_msg = "{}: {}".format(message, "; ".join(exc_reports))
46         else:
47             base_msg = message
48         super(KeepRequestError, self).__init__(base_msg)
49         self.message = message
50
51     def _format_error(self, key, error):
52         if isinstance(error, HttpError):
53             err_fmt = "{} {} responded with {e.status_code} {e.reason}"
54         else:
55             err_fmt = "{} {} raised {e.__class__.__name__} ({e})"
56         return err_fmt.format(self.label, key, e=error)
57
58     def request_errors(self):
59         """request_errors() -> OrderedDict
60
61         The keys of the dictionary are described by `self.label`
62         The corresponding value is the exception raised when sending the
63         request to it."""
64         return self._request_errors
65
66
67 class HttpError(Exception):
68     def __init__(self, status_code, reason):
69         self.status_code = status_code
70         self.reason = reason
71
72
73 class ArgumentError(Exception):
74     pass
75 class SyntaxError(Exception):
76     pass
77 class AssertionError(Exception):
78     pass
79 class CommandFailedError(Exception):
80     pass
81 class KeepReadError(KeepRequestError):
82     pass
83 class KeepWriteError(KeepRequestError):
84     pass
85 class NotFoundError(KeepReadError):
86     pass
87 class NotImplementedError(Exception):
88     pass
89 class NoKeepServersError(Exception):
90     pass
91 class StaleWriterStateError(Exception):
92     pass
93 class FeatureNotEnabledError(Exception):
94     pass