Coverage for /Users/buh/.pyenv/versions/3.12.2/envs/pii/lib/python3.12/site-packages/es_pii_tool/exceptions.py: 41%
68 statements
« prev ^ index » next coverage.py v7.5.0, created at 2025-03-17 21:29 -0600
« prev ^ index » next coverage.py v7.5.0, created at 2025-03-17 21:29 -0600
1"""PII Tool Exceptions"""
3import typing as t
4from datetime import datetime, timedelta, timezone
7class PiiToolError(Exception): # Parent exception
8 """
9 Base class for all exceptions raised by the tool that are not base/native
10 Exceptions
11 """
14class ClientError(PiiToolError):
15 """
16 Exception raised when the Elasticsearch client and/or connection is the source of
17 the problem.
19 :param message: The error message
20 :param upstream: The upstream exception
21 """
23 def __init__(self, message: str, upstream: Exception):
24 super().__init__(message)
25 self.message = message
26 self.upstream = upstream
28 # Possibly include code here to extract any extra details, append them to message
31class BadClientResult(ClientError):
32 """
33 Exception raised when return value from Elasticsearch API call is not or does not
34 contain the expected result.
36 :param message: The error message
37 :param upstream: The upstream exception
38 """
41class MissingError(ClientError):
42 """
43 Exception raised when an item is expected but not found
45 :param message: The error message
46 :param upstream: The upstream exception
47 :param missing: The missing item
48 """
50 def __init__(self, message: str, upstream: Exception, missing: str):
51 super().__init__(message, upstream)
52 #: The name of the missing item
53 self.missing = missing
56class MissingIndex(MissingError):
57 """
58 Exception raised when an index is expected but not found
60 :param message: The error message
61 :param upstream: The upstream exception
62 :param missing: The missing index
63 """
66class MissingDocument(MissingError):
67 """
68 Exception raised when a document in an index is expected but not found
70 :param message: The error message
71 :param upstream: The upstream exception
72 :param missing: The missing document
73 """
76class ConfigError(PiiToolError):
77 """
78 Exception raised when there is a configuration error
80 :param message: The error message
81 :param what: What or why the ConfigError happened
82 """
84 def __init__(self, message: str, what: t.Any):
85 super().__init__(message)
86 self.what = what
89class MissingArgument(ConfigError):
90 """
91 Exception raised when a required argument or parameter is missing
93 :param message: The error message
94 :param what: What or why the ConfigError happened
95 :param names: The name or names of the missing arguments. Can pass in a single
96 name or a list.
97 """
99 def __init__(self, message: str, what: t.Any, names: t.Union[str, t.Sequence[str]]):
100 super().__init__(message, what)
101 if not isinstance(names, list):
102 mylist = []
103 mylist.append(names)
104 #: The names of the missing argument
105 self.names = names
108class ValueMismatch(ConfigError):
109 """
110 Exception raised when a received value does not match what was expected.
112 This is particularly used when ``expected_docs`` is specified but a different value
113 is returned at query time.
115 :param message: The error message
116 :param what: What or why the ConfigError happened
117 :param expected: What the expected value was
118 """
120 def __init__(self, message: str, what: t.Any, expected: t.Any):
121 super().__init__(message, what)
122 self.expected = expected
125class PiiTimeout(PiiToolError):
126 """
127 Exception raised when a task has failed because the allotted time ran out
129 :param message: The error message
130 :param timeout: The timeout value
131 :param seconds: Number of seconds
132 """
134 Num = t.Union[int, float]
136 def __init__(
137 self,
138 message: str,
139 seconds: t.Union[float, None] = None,
140 elapsed: t.Union[float, None] = None,
141 start: t.Union[datetime, None] = None,
142 end: t.Union[datetime, None] = None,
143 ):
144 super().__init__(message)
145 self.seconds = seconds
146 self.elapsed = elapsed
147 self.start = start
148 self.end = end
149 self.human = 'not calculated'
150 self.parse()
152 def get_human(self, value: Num) -> str:
153 """
154 Return human readable version of elapsed time
155 Output is in days|hours|minutes|seconds.milliseconds
156 """
157 td = timedelta(seconds=value)
158 seconds = td.seconds # No microseconds
159 h_num = seconds // 3600
160 m_num = (seconds % 3600) // 60
161 s_num = seconds % 60
162 days = f'{td.days} days, ' if td.days else ''
163 hours = f'{h_num} hours, ' if h_num > 0 else ''
164 minutes = f'{m_num} minutes, ' if m_num > 0 else ''
165 float_sec = float(s_num) + td.microseconds / 1000000
166 return f'Elapsed time: {days}{hours}{minutes}{float_sec:.3f}'
168 def parse(self) -> None:
169 """Parse args and determine which value to use"""
170 val = 'not calculated'
171 if self.seconds:
172 val = self.get_human(self.seconds)
173 if self.elapsed:
174 val = self.get_human(self.elapsed)
175 if self.start and self.end:
176 val = self.get_human((self.end - self.start).total_seconds())
177 elif self.start and not self.end:
178 # Going to use now as the end time
179 end = datetime.now(timezone.utc)
180 val = self.get_human((end - self.start).total_seconds())
181 self.human = val
184class FatalError(PiiToolError):
185 """
186 Exception raised when the program should be halted.
188 :param message: The error message
189 :param upstream: The upstream exception
190 """
192 def __init__(self, message: str, upstream: Exception):
193 super().__init__(message)
194 self.message = message
195 self.upstream = upstream
198# Possibly include more code here specific to the index and/or failure