-
Notifications
You must be signed in to change notification settings - Fork 407
/
Copy pathexceptions.py
329 lines (206 loc) · 7.49 KB
/
exceptions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
"""All exceptions for the github3 library."""
class GitHubException(Exception):
"""The base exception class.
.. versionadded:: 1.0.0
Necessary to handle pre-response exceptions
"""
pass
class GeneratedTokenExpired(GitHubException):
"""This exception is used to prevent an expired token from being used.
.. versionadded:: 1.2.0
"""
pass
class AppInstallationTokenExpired(GeneratedTokenExpired):
"""This exception is used to prevent an expired token from being used.
.. versionadded:: 1.2.0
"""
pass
class AppTokenExpired(GeneratedTokenExpired):
"""This exception is used to prevent an expired token from being used.
.. versionadded:: 1.2.0
"""
pass
class MissingAppAuthentication(GitHubException):
"""Raised when user tries to call a method that requires app auth.
.. versionadded:: 1.2.0
"""
pass
class MissingAppBearerAuthentication(MissingAppAuthentication):
"""Raised when user tries to call a method that requires app auth.
.. versionadded:: 1.2.0
"""
pass
class MissingAppInstallationAuthentication(MissingAppAuthentication):
"""Raised when user tries to call a method that requires app installation.
.. versionadded:: 1.2.0
"""
pass
class CardHasNoContentUrl(GitHubException):
"""Raised when attempting a card has no ``content_url``.
We use this in methods to retrieve the underlying issue or pull request
based on the ``content_url``.
.. versionadded:: 1.3.0
"""
pass
class GitHubError(GitHubException):
"""The base exception class for all response-related exceptions.
.. versionchanged:: 1.0.0
This now inherits from :class:`~github3.exceptions.GitHubException`
.. attribute:: response
The response object that triggered the exception
.. attribute:: code
The response's status code
.. attribute:: errors
The list of errors (if present) returned by GitHub's API
"""
def __init__(self, resp):
"""Initialize our exception class."""
super().__init__(resp)
#: Response code that triggered the error
self.response = resp
self.code = resp.status_code
self.errors = []
try:
error = resp.json()
#: Message associated with the error
self.msg = error.get("message")
#: List of errors provided by GitHub
if error.get("errors"):
self.errors = error.get("errors")
except Exception: # Amazon S3 error
self.msg = resp.content or "[No message]"
def __repr__(self):
return "<{} [{}]>".format(
self.__class__.__name__, self.msg or self.code
)
def __str__(self):
return f"{self.code} {self.msg}"
@property
def message(self):
"""The actual message returned by the API."""
return self.msg
class IncompleteResponse(GitHubError):
"""Exception for a response that doesn't have everything it should.
This has the same attributes as :class:`~github3.exceptions.GitHubError`
as well as
.. attribute:: exception
The original exception causing the IncompleteResponse exception
"""
def __init__(self, json, exception):
"""Initialize our IncompleteResponse."""
self.response = None
self.code = None
self.json = json
self.errors = []
self.exception = exception
self.msg = (
"The library was expecting more data in the response (%r)."
" Either GitHub modified it's response body, or your token"
" is not properly scoped to retrieve this information."
) % (exception,)
class NotRefreshable(GitHubException):
"""Exception to indicate that an object is not refreshable."""
message_format = (
'"{}" is not refreshable because the GitHub API does '
"not provide a URL to retrieve its contents from."
)
def __init__(self, object_name):
"""Initialize our NotRefreshable exception."""
super().__init__(self.message_format.format(object_name))
class ResponseError(GitHubError):
"""The base exception for errors stemming from GitHub responses."""
pass
class TransportError(GitHubException):
"""Catch-all exception for errors coming from Requests.
.. versionchanged:: 1.0.0
Now inherits from :class:`~github3.exceptions.GitHubException`.
"""
msg_format = "An error occurred while making a request to GitHub: {0}"
def __init__(self, exception):
"""Initialize TransportError exception."""
self.msg = self.msg_format.format(str(exception))
super().__init__(self, self.msg, exception)
self.exception = exception
def __str__(self):
return f"{type(self.exception)}: {self.msg}"
class ConnectionError(TransportError):
"""Exception for errors in connecting to or reading data from GitHub."""
msg_format = "A connection-level exception occurred: {0}"
class UnexpectedResponse(ResponseError):
"""Exception class for responses that were unexpected."""
pass
class UnprocessableResponseBody(ResponseError):
"""Exception class for response objects that cannot be handled."""
def __init__(self, message, body):
"""Initialize UnprocessableResponseBody."""
Exception.__init__(self, message)
self.body = body
self.msg = message
def __repr__(self):
return "<{} [{}]>".format("UnprocessableResponseBody", self.body)
def __str__(self):
return self.message
class BadRequest(ResponseError):
"""Exception class for 400 responses."""
pass
class AuthenticationFailed(ResponseError):
"""Exception class for 401 responses.
Possible reasons:
- Need one time password (for two-factor authentication)
- You are not authorized to access the resource
"""
pass
class ForbiddenError(ResponseError):
"""Exception class for 403 responses.
Possible reasons:
- Too many requests (you've exceeded the ratelimit)
- Too many login failures
"""
pass
class NotFoundError(ResponseError):
"""Exception class for 404 responses."""
pass
class MethodNotAllowed(ResponseError):
"""Exception class for 405 responses."""
pass
class NotAcceptable(ResponseError):
"""Exception class for 406 responses."""
pass
class Conflict(ResponseError):
"""Exception class for 409 responses.
Possible reasons:
- Head branch was modified (SHA sums do not match)
"""
pass
class UnprocessableEntity(ResponseError):
"""Exception class for 422 responses."""
pass
class ClientError(ResponseError):
"""Catch-all for 400 responses that aren't specific errors."""
pass
class ServerError(ResponseError):
"""Exception class for 5xx responses."""
pass
class UnavailableForLegalReasons(ResponseError):
"""Exception class for 451 responses."""
pass
error_classes = {
400: BadRequest,
401: AuthenticationFailed,
403: ForbiddenError,
404: NotFoundError,
405: MethodNotAllowed,
406: NotAcceptable,
409: Conflict,
422: UnprocessableEntity,
451: UnavailableForLegalReasons,
}
def error_for(response):
"""Return the appropriate initialized exception class for a response."""
klass = error_classes.get(response.status_code)
if klass is None:
if 400 <= response.status_code < 500:
klass = ClientError
if 500 <= response.status_code < 600:
klass = ServerError
return klass(response)