-
-
Notifications
You must be signed in to change notification settings - Fork 278
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement requests-like exception hierarchy (#250)
- Loading branch information
Showing
7 changed files
with
254 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,7 @@ | ||
from .. import CurlError | ||
|
||
|
||
class RequestsError(CurlError): | ||
"""Base exception for curl_cffi.requests package""" | ||
|
||
def __init__(self, msg, code=0, response=None, *args, **kwargs): | ||
super().__init__(msg, code, *args, **kwargs) | ||
self.response = response | ||
# for compatibility with 0.5.x | ||
|
||
__all__ = ["CurlError", "RequestsError", "CookieConflict", "SessionClosed"] | ||
|
||
class CookieConflict(RequestsError): | ||
pass | ||
|
||
|
||
class SessionClosed(RequestsError): | ||
pass | ||
from .. import CurlError | ||
from .exceptions import CookieConflict, SessionClosed | ||
from .exceptions import RequestException as RequestsError |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,217 @@ | ||
from .errors import RequestsError | ||
# Apache 2.0 License | ||
# Vendored from https://github.com/psf/requests/blob/main/src/requests/exceptions.py | ||
# With our own addtions | ||
|
||
RequestsException = RequestsError | ||
import json | ||
from typing import Literal, Union | ||
|
||
from .. import CurlError | ||
from ..const import CurlECode | ||
|
||
|
||
# Note IOError is an alias of OSError in Python 3.x | ||
class RequestException(CurlError, OSError): | ||
"""Base exception for curl_cffi.requests package""" | ||
|
||
def __init__(self, msg, code: Union[CurlECode, Literal[0]] = 0, response=None, *args, **kwargs): | ||
super().__init__(msg, code, *args, **kwargs) | ||
self.response = response | ||
|
||
|
||
class CookieConflict(RequestException): | ||
"""Same cookie exists for different domains.""" | ||
|
||
|
||
class SessionClosed(RequestException): | ||
"""The session has already been closed.""" | ||
|
||
|
||
class ImpersonateError(RequestException): | ||
"""The impersonate config was wrong or impersonate failed.""" | ||
|
||
|
||
# not used | ||
class InvalidJSONError(RequestException): | ||
"""A JSON error occurred.""" | ||
|
||
|
||
# not used | ||
class JSONDecodeError(InvalidJSONError, json.JSONDecodeError): | ||
"""Couldn't decode the text into json""" | ||
|
||
|
||
class HTTPError(RequestException): | ||
"""An HTTP error occurred.""" | ||
|
||
|
||
class IncompleteRead(HTTPError): | ||
"""Incomplete read of content""" | ||
|
||
|
||
class ConnectionError(RequestException): | ||
"""A Connection error occurred.""" | ||
|
||
|
||
class DNSError(ConnectionError): | ||
"""Could not resolve""" | ||
|
||
|
||
class ProxyError(RequestException): | ||
"""A proxy error occurred.""" | ||
|
||
|
||
class SSLError(ConnectionError): | ||
"""An SSL error occurred.""" | ||
|
||
|
||
class CertificateVerifyError(SSLError): | ||
"""Raised when certificate validated has failed""" | ||
|
||
|
||
class Timeout(RequestException): | ||
"""The request timed out.""" | ||
|
||
|
||
# not used | ||
class ConnectTimeout(ConnectionError, Timeout): | ||
"""The request timed out while trying to connect to the remote server. | ||
Requests that produced this error are safe to retry. | ||
""" | ||
|
||
|
||
# not used | ||
class ReadTimeout(Timeout): | ||
"""The server did not send any data in the allotted amount of time.""" | ||
|
||
|
||
# not used | ||
class URLRequired(RequestException): | ||
"""A valid URL is required to make a request.""" | ||
|
||
|
||
class TooManyRedirects(RequestException): | ||
"""Too many redirects.""" | ||
|
||
|
||
# not used | ||
class MissingSchema(RequestException, ValueError): | ||
"""The URL scheme (e.g. http or https) is missing.""" | ||
|
||
|
||
class InvalidSchema(RequestException, ValueError): | ||
"""The URL scheme provided is either invalid or unsupported.""" | ||
|
||
|
||
class InvalidURL(RequestException, ValueError): | ||
"""The URL provided was somehow invalid.""" | ||
|
||
|
||
# not used | ||
class InvalidHeader(RequestException, ValueError): | ||
"""The header value provided was somehow invalid.""" | ||
|
||
|
||
# not used | ||
class InvalidProxyURL(InvalidURL): | ||
"""The proxy URL provided is invalid.""" | ||
|
||
|
||
# not used | ||
class ChunkedEncodingError(RequestException): | ||
"""The server declared chunked encoding but sent an invalid chunk.""" | ||
|
||
|
||
# not used | ||
class ContentDecodingError(RequestException): | ||
"""Failed to decode response content.""" | ||
|
||
|
||
# not used | ||
class StreamConsumedError(RequestException, TypeError): | ||
"""The content for this response was already consumed.""" | ||
|
||
|
||
# does not support | ||
class RetryError(RequestException): | ||
"""Custom retries logic failed""" | ||
|
||
|
||
# not used | ||
class UnrewindableBodyError(RequestException): | ||
"""Requests encountered an error when trying to rewind a body.""" | ||
|
||
|
||
class InterfaceError(RequestException): | ||
"""A specified outgoing interface could not be used.""" | ||
|
||
|
||
# Warnings | ||
|
||
|
||
# not used | ||
class RequestsWarning(Warning): | ||
"""Base warning for Requests.""" | ||
|
||
|
||
# not used | ||
class FileModeWarning(RequestsWarning, DeprecationWarning): | ||
"""A file was opened in text mode, but Requests determined its binary length.""" | ||
|
||
|
||
# not used | ||
class RequestsDependencyWarning(RequestsWarning): | ||
"""An imported dependency doesn't match the expected version range.""" | ||
|
||
|
||
CODE2ERROR = { | ||
0: RequestException, | ||
CurlECode.UNSUPPORTED_PROTOCOL: InvalidSchema, | ||
CurlECode.URL_MALFORMAT: InvalidURL, | ||
CurlECode.COULDNT_RESOLVE_PROXY: ProxyError, | ||
CurlECode.COULDNT_RESOLVE_HOST: DNSError, | ||
CurlECode.COULDNT_CONNECT: ConnectionError, | ||
CurlECode.WEIRD_SERVER_REPLY: ConnectionError, | ||
CurlECode.REMOTE_ACCESS_DENIED: ConnectionError, | ||
CurlECode.HTTP2: HTTPError, | ||
CurlECode.HTTP_RETURNED_ERROR: HTTPError, | ||
CurlECode.WRITE_ERROR: RequestException, | ||
CurlECode.READ_ERROR: RequestException, | ||
CurlECode.OUT_OF_MEMORY: RequestException, | ||
CurlECode.OPERATION_TIMEDOUT: Timeout, | ||
CurlECode.SSL_CONNECT_ERROR: SSLError, | ||
CurlECode.INTERFACE_FAILED: InterfaceError, | ||
CurlECode.TOO_MANY_REDIRECTS: TooManyRedirects, | ||
CurlECode.UNKNOWN_OPTION: RequestException, | ||
CurlECode.SETOPT_OPTION_SYNTAX: RequestException, | ||
CurlECode.GOT_NOTHING: ConnectionError, | ||
CurlECode.SSL_ENGINE_NOTFOUND: SSLError, | ||
CurlECode.SSL_ENGINE_SETFAILED: SSLError, | ||
CurlECode.SEND_ERROR: ConnectionError, | ||
CurlECode.RECV_ERROR: ConnectionError, | ||
CurlECode.SSL_CERTPROBLEM: SSLError, | ||
CurlECode.SSL_CIPHER: SSLError, | ||
CurlECode.PEER_FAILED_VERIFICATION: CertificateVerifyError, | ||
CurlECode.BAD_CONTENT_ENCODING: HTTPError, | ||
CurlECode.SSL_ENGINE_INITFAILED: SSLError, | ||
CurlECode.SSL_CACERT_BADFILE: SSLError, | ||
CurlECode.SSL_CRL_BADFILE: SSLError, | ||
CurlECode.SSL_ISSUER_ERROR: SSLError, | ||
CurlECode.SSL_PINNEDPUBKEYNOTMATCH: SSLError, | ||
CurlECode.SSL_INVALIDCERTSTATUS: SSLError, | ||
CurlECode.HTTP2_STREAM: HTTPError, | ||
CurlECode.HTTP3: HTTPError, | ||
CurlECode.QUIC_CONNECT_ERROR: ConnectionError, | ||
CurlECode.PROXY: ProxyError, | ||
CurlECode.SSL_CLIENTCERT: SSLError, | ||
CurlECode.ECH_REQUIRED: SSLError, | ||
CurlECode.PARTIAL_FILE: IncompleteRead, | ||
} | ||
|
||
|
||
# credits: https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/networking/_curlcffi.py#L241 | ||
# Unlicense | ||
def code2error(code: Union[CurlECode, Literal[0]], msg: str): | ||
if code == CurlECode.RECV_ERROR and "CONNECT" in msg: | ||
return ProxyError | ||
return CODE2ERROR.get(code, RequestException) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.