-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
exceptions.py
328 lines (243 loc) · 10.9 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
"""Async PRAW exception classes.
Includes two main exceptions: :class:`.RedditAPIException` for when something goes wrong
on the server side, and :class:`.ClientException` when something goes wrong on the
client side. Both of these classes extend :class:`.AsyncPRAWException`.
All other exceptions are subclassed from :class:`.ClientException`.
"""
import sys
from typing import List, Optional, Union
from warnings import warn
from .util import _deprecate_args
class AsyncPRAWException(Exception):
"""The base Async PRAW Exception that all other exception classes extend."""
PRAWException = AsyncPRAWException
class RedditErrorItem:
"""Represents a single error returned from Reddit's API."""
@property
def error_message(self) -> str:
"""Get the completed error message string."""
error_str = self.error_type
if self.message:
error_str += f": {self.message!r}"
if self.field:
error_str += f" on field {self.field!r}"
return error_str
@_deprecate_args("error_type", "message", "field")
def __init__(
self,
error_type: str,
*,
field: Optional[str] = None,
message: Optional[str] = None,
):
"""Initialize a :class:`.RedditErrorItem` instance.
:param error_type: The error type set on Reddit's end.
:param field: The input field associated with the error, if available.
:param message: The associated message for the error.
"""
self.error_type = error_type
self.message = message
self.field = field
def __eq__(self, other: Union["RedditErrorItem", List[str]]):
"""Check for equality."""
if isinstance(other, RedditErrorItem):
return (self.error_type, self.message, self.field) == (
other.error_type,
other.message,
other.field,
)
return super().__eq__(other)
def __repr__(self) -> str:
"""Return an object initialization representation of the instance."""
return (
f"{self.__class__.__name__}(error_type={self.error_type!r},"
f" message={self.message!r}, field={self.field!r})"
)
def __str__(self):
"""Get the message returned from str(self)."""
return self.error_message
class APIException(AsyncPRAWException):
"""Old class preserved for alias purposes.
.. deprecated:: 7.0
Class :class:`.APIException` has been deprecated in favor of
:class:`.RedditAPIException`. This class will be removed in Async PRAW 8.0.
"""
@staticmethod
def parse_exception_list(exceptions: List[Union[RedditErrorItem, List[str]]]):
"""Covert an exception list into a :class:`.RedditErrorItem` list."""
return [
exception
if isinstance(exception, RedditErrorItem)
else RedditErrorItem(
error_type=exception[0],
field=exception[2] if bool(exception[2]) else "",
message=exception[1] if bool(exception[1]) else "",
)
for exception in exceptions
]
@property
def error_type(self) -> str:
"""Get error_type.
.. deprecated:: 7.0
Accessing attributes through instances of :class:`.RedditAPIException` is
deprecated. This behavior will be removed in Async PRAW 8.0. Check out the
:ref:`PRAW 7 Migration tutorial <Exception_Handling>` on how to migrate code
from this behavior.
"""
return self._get_old_attr("error_type")
@property
def message(self) -> str:
"""Get message.
.. deprecated:: 7.0
Accessing attributes through instances of :class:`.RedditAPIException` is
deprecated. This behavior will be removed in Async PRAW 8.0. Check out the
:ref:`Async PRAW 7 Migration tutorial <Exception_Handling>` on how to
migrate code from this behavior.
"""
return self._get_old_attr("message")
@property
def field(self) -> str:
"""Get field.
.. deprecated:: 7.0
Accessing attributes through instances of :class:`.RedditAPIException` is
deprecated. This behavior will be removed in Async PRAW 8.0. Check out the
:ref:`PRAW 7 Migration tutorial <Exception_Handling>` on how to migrate code
from this behavior.
"""
return self._get_old_attr("field")
def _get_old_attr(self, attrname):
warn(
f"Accessing attribute ``{attrname}`` through APIException is deprecated."
" This behavior will be removed in Async PRAW 8.0. Check out"
" https://praw.readthedocs.io/en/latest/package_info/praw7_migration.html"
" to learn how to migrate your code.",
category=DeprecationWarning,
stacklevel=3,
)
return getattr(self.items[0], attrname)
def __init__(
self,
items: Union[List[Union[RedditErrorItem, List[str], str]], str],
*optional_args: str,
):
"""Initialize a :class:`.RedditAPIException` instance.
:param items: Either a list of instances of :class:`.RedditErrorItem` or a list
containing lists of unformed errors.
:param optional_args: Takes the second and third arguments that
:class:`.APIException` used to take.
"""
if isinstance(items, str):
items = [[items, *optional_args]]
elif isinstance(items, list) and isinstance(items[0], str):
items = [items]
self.items = self.parse_exception_list(items)
super().__init__(*self.items)
class RedditAPIException(APIException):
"""Container for error messages from Reddit's API."""
class ClientException(AsyncPRAWException):
"""Indicate exceptions that don't involve interaction with Reddit's API."""
class DuplicateReplaceException(ClientException):
"""Indicate exceptions that involve the replacement of :class:`.MoreComments`."""
def __init__(self):
"""Initialize a :class:`.DuplicateReplaceException` instance."""
super().__init__(
"A duplicate comment has been detected. Are you attempting to call"
" ``replace_more_comments`` more than once?"
)
class InvalidFlairTemplateID(ClientException):
"""Indicate exceptions where an invalid flair template ID is given."""
def __init__(self, template_id: str):
"""Initialize an :class:`.InvalidFlairTemplateID` instance."""
super().__init__(
f"The flair template ID ``{template_id}`` is invalid. If you are trying to"
" create a flair, please use the ``add`` method."
)
class InvalidImplicitAuth(ClientException):
"""Indicate exceptions where an implicit auth type is used incorrectly."""
def __init__(self):
"""Initialize an :class:`.InvalidImplicitAuth` instance."""
super().__init__("Implicit authorization can only be used with installed apps.")
class InvalidURL(ClientException):
"""Indicate exceptions where an invalid URL is entered."""
@_deprecate_args("url", "message")
def __init__(self, url: str, *, message: str = "Invalid URL: {}"):
"""Initialize an :class:`.InvalidURL` instance.
:param url: The invalid URL.
:param message: The message to display. Must contain a format identifier (``{}``
or ``{0}``) (default: ``"Invalid URL: {}"``).
"""
super().__init__(message.format(url))
class MissingRequiredAttributeException(ClientException):
"""Indicate exceptions caused by not including a required attribute."""
class ReadOnlyException(ClientException):
"""Raised when a method call requires :attr:`.read_only` mode to be disabled."""
class TooLargeMediaException(ClientException):
"""Indicate exceptions from uploading media that's too large."""
@_deprecate_args("maximum_size", "actual")
def __init__(self, *, actual: int, maximum_size: int):
"""Initialize a :class:`.TooLargeMediaException` instance.
:param actual: The actual size of the uploaded media.
:param maximum_size: The maximum size of the uploaded media.
"""
self.maximum_size = maximum_size
self.actual = actual
super().__init__(
f"The media that you uploaded was too large (maximum size is {maximum_size}"
f" bytes, uploaded {actual} bytes)"
)
class WebSocketException(ClientException):
"""Indicate exceptions caused by use of WebSockets."""
@property
def original_exception(self) -> Exception:
"""Access the original_exception attribute (now deprecated)."""
warn(
"Accessing the attribute original_exception is deprecated. Please rewrite"
" your code in such a way that this attribute does not need to be used. It"
" will be removed in Async PRAW 8.0.",
category=DeprecationWarning,
stacklevel=2,
)
return self._original_exception
@original_exception.setter
def original_exception(self, value: Exception):
self._original_exception = value
@original_exception.deleter
def original_exception(self):
del self._original_exception
def __init__(self, message: str, exception: Optional[Exception]):
"""Initialize a :class:`.WebSocketException` instance.
:param message: The exception message.
:param exception: The exception thrown by the websocket library.
.. note::
This parameter is deprecated. It will be removed in Async PRAW 8.0.
"""
super().__init__(message)
self._original_exception = exception
class MediaPostFailed(WebSocketException):
"""Indicate exceptions where media uploads failed.."""
def __init__(self):
"""Initialize a :class:`.MediaPostFailed` instance."""
super().__init__(
"The attempted media upload action has failed. Possible causes include the"
" corruption of media files. Check that the media file can be opened on"
" your local machine.",
None,
)
# Adapted from https://stackoverflow.com/a/40546615
class ExceptionWrapper(object):
"""Wrapper to facilitate showing depreciation for PRAWException class rename."""
def __init__(self, wrapped):
"""Initialize Wrapper instance."""
self.wrapped = wrapped
def __getattr__(self, attribute):
"""Return the value of `attribute`."""
if attribute == "PRAWException":
warn(
"PRAWException as been renamed to AsyncPRAWException. PRAWException "
" will be removed in the next version Async PRAW.",
category=DeprecationWarning,
stacklevel=3,
)
return getattr(self.wrapped, attribute)
if "sphinx" not in sys.modules:
sys.modules[__name__] = ExceptionWrapper(sys.modules[__name__])