Coverage for /Users/buh/.pyenv/versions/3.12.2/envs/pii/lib/python3.12/site-packages/es_pii_tool/exceptions.py: 41%

1"""PII Tool Exceptions"""

2

3 import typing as t

4 from datetime import datetime, timedelta, timezone

5

6

7 class PiiToolError(Exception): # Parent exception

8 """

9 Base class for all exceptions raised by the tool that are not base/native

10 Exceptions

11 """

12

13

14 class ClientError(PiiToolError):

15 """

16 Exception raised when the Elasticsearch client and/or connection is the source of

17 the problem.

18

19 :param message: The error message

20 :param upstream: The upstream exception

21 """

22

23 def __init__(self, message: str, upstream: Exception):

24 super().__init__(message)

25 self.message = message

26 self.upstream = upstream

27

28 # Possibly include code here to extract any extra details, append them to message

29

30

31 class BadClientResult(ClientError):

32 """

33 Exception raised when return value from Elasticsearch API call is not or does not

34 contain the expected result.

35

36 :param message: The error message

37 :param upstream: The upstream exception

38 """

39

40

41 class MissingError(ClientError):

42 """

43 Exception raised when an item is expected but not found

44

45 :param message: The error message

46 :param upstream: The upstream exception

47 :param missing: The missing item

48 """

49

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

54

55

56 class MissingIndex(MissingError):

57 """

58 Exception raised when an index is expected but not found

59

60 :param message: The error message

61 :param upstream: The upstream exception

62 :param missing: The missing index

63 """

64

65

66 class MissingDocument(MissingError):

67 """

68 Exception raised when a document in an index is expected but not found

69

70 :param message: The error message

71 :param upstream: The upstream exception

72 :param missing: The missing document

73 """

74

75

76 class ConfigError(PiiToolError):

77 """

78 Exception raised when there is a configuration error

79

80 :param message: The error message

81 :param what: What or why the ConfigError happened

82 """

83

84 def __init__(self, message: str, what: t.Any):

85 super().__init__(message)

86 self.what = what

87

88

89 class MissingArgument(ConfigError):

90 """

91 Exception raised when a required argument or parameter is missing

92

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 """

98

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

106

107

108 class ValueMismatch(ConfigError):

109 """

110 Exception raised when a received value does not match what was expected.

111

112 This is particularly used when ``expected_docs`` is specified but a different value

113 is returned at query time.

114

115 :param message: The error message

116 :param what: What or why the ConfigError happened

117 :param expected: What the expected value was

118 """

119

120 def __init__(self, message: str, what: t.Any, expected: t.Any):

121 super().__init__(message, what)

122 self.expected = expected

123

124

125 class PiiTimeout(PiiToolError):

126 """

127 Exception raised when a task has failed because the allotted time ran out

128

129 :param message: The error message

130 :param timeout: The timeout value

131 :param seconds: Number of seconds

132 """

133

134 Num = t.Union[int, float]

135

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()

151

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}'

167

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

182

183

184 class FatalError(PiiToolError):

185 """

186 Exception raised when the program should be halted.

187

188 :param message: The error message

189 :param upstream: The upstream exception

190 """

191

192 def __init__(self, message: str, upstream: Exception):

193 super().__init__(message)

194 self.message = message

195 self.upstream = upstream

196

197

198# Possibly include more code here specific to the index and/or failure