308 lines
10 KiB
Python
308 lines
10 KiB
Python
"""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:`.PRAWException`.
|
|
|
|
All other exceptions are subclassed from :class:`.ClientException`.
|
|
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from warnings import warn
|
|
|
|
from .util import _deprecate_args
|
|
|
|
|
|
class PRAWException(Exception):
|
|
"""The base PRAW Exception that all other exception classes extend."""
|
|
|
|
|
|
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
|
|
|
|
def __eq__(self, other: RedditErrorItem | list[str]) -> bool:
|
|
"""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)
|
|
|
|
@_deprecate_args("error_type", "message", "field")
|
|
def __init__(
|
|
self,
|
|
error_type: str,
|
|
*,
|
|
field: str | None = None,
|
|
message: str | None = 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 __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) -> str:
|
|
"""Get the message returned from str(self)."""
|
|
return self.error_message
|
|
|
|
|
|
class ClientException(PRAWException):
|
|
"""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 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: Exception | None):
|
|
"""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 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,
|
|
)
|
|
|
|
|
|
class APIException(PRAWException):
|
|
"""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 PRAW 8.0.
|
|
|
|
"""
|
|
|
|
@staticmethod
|
|
def parse_exception_list(
|
|
exceptions: list[RedditErrorItem | list[str]],
|
|
) -> list[RedditErrorItem]:
|
|
"""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 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 field(self) -> str:
|
|
"""Get field.
|
|
|
|
.. deprecated:: 7.0
|
|
|
|
Accessing attributes through instances of :class:`.RedditAPIException` is
|
|
deprecated. This behavior will be removed in 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")
|
|
|
|
@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 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("message")
|
|
|
|
def __init__(
|
|
self,
|
|
items: list[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)
|
|
|
|
def _get_old_attr(self, attrname: str) -> Any:
|
|
warn(
|
|
f"Accessing attribute '{attrname}' through APIException is deprecated."
|
|
" This behavior will be removed in 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)
|
|
|
|
|
|
class RedditAPIException(APIException):
|
|
"""Container for error messages from Reddit's API."""
|