File: exceptions.py

package info (click to toggle)
python-asyncprawcore 3.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,328 kB
  • sloc: python: 2,224; makefile: 4
file content (183 lines) | stat: -rw-r--r-- 6,182 bytes parent folder | download
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
"""Provide exception classes for the asyncprawcore package."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any
from urllib.parse import urlparse

if TYPE_CHECKING:
    from aiohttp import ClientResponse


class AsyncPrawcoreException(Exception):  # noqa: N818
    """Base exception class for exceptions that occur within this package."""


class InvalidInvocation(AsyncPrawcoreException):
    """Indicate that the code to execute cannot be completed."""


class OAuthException(AsyncPrawcoreException):
    """Indicate that there was an OAuth2 related error with the request."""

    def __init__(self, response: ClientResponse, error: str, description: str | None = None) -> None:
        """Initialize a OAuthException instance.

        :param response: A ``aiohttp.ClientResponse`` instance.
        :param error: The error type returned by Reddit.
        :param description: A description of the error when provided.

        """
        self.error = error
        self.description = description
        self.response = response
        message = f"{error} error processing request"
        if description:
            message += f" ({description})"
        AsyncPrawcoreException.__init__(self, message)


class RequestException(AsyncPrawcoreException):
    """Indicate that there was an error with the incomplete HTTP request."""

    def __init__(
        self,
        original_exception: Exception,
        request_args: tuple[Any, ...],
        request_kwargs: dict[str, bool | (dict[str, int] | (dict[str, str] | str)) | None],
    ) -> None:
        """Initialize a RequestException instance.

        :param original_exception: The original exception that occurred.
        :param request_args: The arguments to the request function.
        :param request_kwargs: The keyword arguments to the request function.

        """
        self.original_exception = original_exception
        self.request_args = request_args
        self.request_kwargs = request_kwargs
        super().__init__(f"error with request {original_exception}")


class ResponseException(AsyncPrawcoreException):
    """Indicate that there was an error with the completed HTTP request."""

    def __init__(self, response: ClientResponse) -> None:
        """Initialize a ResponseException instance.

        :param response: A ``aiohttp.ClientResponse`` instance.

        """
        self.response = response
        super().__init__(f"received {response.status} HTTP response")


class BadJSON(ResponseException):
    """Indicate the response did not contain valid JSON."""


class BadRequest(ResponseException):
    """Indicate invalid parameters for the request."""


class Conflict(ResponseException):
    """Indicate a conflicting change in the target resource."""


class Forbidden(ResponseException):
    """Indicate the authentication is not permitted for the request."""


class InsufficientScope(ResponseException):
    """Indicate that the request requires a different scope."""


class InvalidToken(ResponseException):
    """Indicate that the request used an invalid access token."""


class NotFound(ResponseException):
    """Indicate that the requested URL was not found."""


class Redirect(ResponseException):
    """Indicate the request resulted in a redirect.

    This class adds the attribute ``path``, which is the path to which the response
    redirects.

    """

    def __init__(self, response: ClientResponse) -> None:
        """Initialize a Redirect exception instance.

        :param response: A ``aiohttp.ClientResponse`` instance containing a location
            header.

        """
        path = urlparse(str(response.headers.get("location"))).path
        self.path = path[:-5] if path.endswith(".json") else path
        self.response = response
        msg = f"Redirect to {self.path}"
        msg += (
            " (You may be trying to perform a non-read-only action via a read-only instance.)"
            if "/login/" in self.path
            else ""
        )
        AsyncPrawcoreException.__init__(self, msg)


class ServerError(ResponseException):
    """Indicate issues on the server end preventing request fulfillment."""


class SpecialError(ResponseException):
    """Indicate syntax or spam-prevention issues."""

    def __init__(self, response: ClientResponse, resp_dict: dict[str, object]) -> None:
        """Initialize a SpecialError exception instance.

        :param response: A ``aiohttp.ClientResponse`` instance containing a message and
            a list of special errors.
        :param resp_dict: A dictionary containing the response data, which should
            include keys like "message", "reason", and "special_errors".

        """
        self.response = response

        self.message = resp_dict.get("message", "")
        self.reason = resp_dict.get("reason", "")
        self.special_errors = resp_dict.get("special_errors", [])
        AsyncPrawcoreException.__init__(self, f"Special error {self.message!r}")


class TooLarge(ResponseException):
    """Indicate that the request data exceeds the allowed limit."""


class TooManyRequests(ResponseException):
    """Indicate that the user has sent too many requests in a given amount of time."""

    def __init__(self, response: ClientResponse) -> None:
        """Initialize a TooManyRequests exception instance.

        :param response: A ``aiohttp.ClientResponse`` instance that may contain a
            retry-after header and a message.

        """
        self.response = response
        self.retry_after = response.headers.get("retry-after")
        self.message = response.text  # Not all response bodies are valid JSON

        msg = f"received {response.status} HTTP response"
        if self.retry_after:
            msg += f". Please wait at least {float(self.retry_after)} seconds before re-trying this request."
        AsyncPrawcoreException.__init__(self, msg)


class URITooLong(ResponseException):
    """Indicate that the length of the request URI exceeds the allowed limit."""


class UnavailableForLegalReasons(ResponseException):
    """Indicate that the requested URL is unavailable due to legal reasons."""