123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- from __future__ import annotations
- import typing
- import urllib.error
- from ..utils import YoutubeDLError, deprecation_warning
- if typing.TYPE_CHECKING:
- from .common import RequestHandler, Response
- class RequestError(YoutubeDLError):
- def __init__(
- self,
- msg: str | None = None,
- cause: Exception | str | None = None,
- handler: RequestHandler = None
- ):
- self.handler = handler
- self.cause = cause
- if not msg and cause:
- msg = str(cause)
- super().__init__(msg)
- class UnsupportedRequest(RequestError):
- """raised when a handler cannot handle a request"""
- pass
- class NoSupportingHandlers(RequestError):
- """raised when no handlers can support a request for various reasons"""
- def __init__(self, unsupported_errors: list[UnsupportedRequest], unexpected_errors: list[Exception]):
- self.unsupported_errors = unsupported_errors or []
- self.unexpected_errors = unexpected_errors or []
- # Print a quick summary of the errors
- err_handler_map = {}
- for err in unsupported_errors:
- err_handler_map.setdefault(err.msg, []).append(err.handler.RH_NAME)
- reason_str = ', '.join([f'{msg} ({", ".join(handlers)})' for msg, handlers in err_handler_map.items()])
- if unexpected_errors:
- reason_str = ' + '.join(filter(None, [reason_str, f'{len(unexpected_errors)} unexpected error(s)']))
- err_str = 'Unable to handle request'
- if reason_str:
- err_str += f': {reason_str}'
- super().__init__(msg=err_str)
- class TransportError(RequestError):
- """Network related errors"""
- class HTTPError(RequestError):
- def __init__(self, response: Response, redirect_loop=False):
- self.response = response
- self.status = response.status
- self.reason = response.reason
- self.redirect_loop = redirect_loop
- msg = f'HTTP Error {response.status}: {response.reason}'
- if redirect_loop:
- msg += ' (redirect loop detected)'
- super().__init__(msg=msg)
- def close(self):
- self.response.close()
- def __repr__(self):
- return f'<HTTPError {self.status}: {self.reason}>'
- class IncompleteRead(TransportError):
- def __init__(self, partial, expected=None, **kwargs):
- self.partial = partial
- self.expected = expected
- msg = f'{len(partial)} bytes read'
- if expected is not None:
- msg += f', {expected} more expected'
- super().__init__(msg=msg, **kwargs)
- def __repr__(self):
- return f'<IncompleteRead: {self.msg}>'
- class SSLError(TransportError):
- pass
- class CertificateVerifyError(SSLError):
- """Raised when certificate validated has failed"""
- pass
- class ProxyError(TransportError):
- pass
- class _CompatHTTPError(urllib.error.HTTPError, HTTPError):
- """
- Provides backwards compatibility with urllib.error.HTTPError.
- Do not use this class directly, use HTTPError instead.
- """
- def __init__(self, http_error: HTTPError):
- super().__init__(
- url=http_error.response.url,
- code=http_error.status,
- msg=http_error.msg,
- hdrs=http_error.response.headers,
- fp=http_error.response
- )
- self._closer.file = None # Disable auto close
- self._http_error = http_error
- HTTPError.__init__(self, http_error.response, redirect_loop=http_error.redirect_loop)
- @property
- def status(self):
- return self._http_error.status
- @status.setter
- def status(self, value):
- return
- @property
- def reason(self):
- return self._http_error.reason
- @reason.setter
- def reason(self, value):
- return
- @property
- def headers(self):
- deprecation_warning('HTTPError.headers is deprecated, use HTTPError.response.headers instead')
- return self._http_error.response.headers
- @headers.setter
- def headers(self, value):
- return
- def info(self):
- deprecation_warning('HTTPError.info() is deprecated, use HTTPError.response.headers instead')
- return self.response.headers
- def getcode(self):
- deprecation_warning('HTTPError.getcode is deprecated, use HTTPError.status instead')
- return self.status
- def geturl(self):
- deprecation_warning('HTTPError.geturl is deprecated, use HTTPError.response.url instead')
- return self.response.url
- @property
- def code(self):
- deprecation_warning('HTTPError.code is deprecated, use HTTPError.status instead')
- return self.status
- @code.setter
- def code(self, value):
- return
- @property
- def url(self):
- deprecation_warning('HTTPError.url is deprecated, use HTTPError.response.url instead')
- return self.response.url
- @url.setter
- def url(self, value):
- return
- @property
- def hdrs(self):
- deprecation_warning('HTTPError.hdrs is deprecated, use HTTPError.response.headers instead')
- return self.response.headers
- @hdrs.setter
- def hdrs(self, value):
- return
- @property
- def filename(self):
- deprecation_warning('HTTPError.filename is deprecated, use HTTPError.response.url instead')
- return self.response.url
- @filename.setter
- def filename(self, value):
- return
- def __getattr__(self, name):
- # File operations are passed through the response.
- # Warn for some commonly used ones
- passthrough_warnings = {
- 'read': 'response.read()',
- # technically possibly due to passthrough, but we should discourage this
- 'get_header': 'response.get_header()',
- 'readable': 'response.readable()',
- 'closed': 'response.closed',
- 'tell': 'response.tell()',
- }
- if name in passthrough_warnings:
- deprecation_warning(f'HTTPError.{name} is deprecated, use HTTPError.{passthrough_warnings[name]} instead')
- return super().__getattr__(name)
- def __str__(self):
- return str(self._http_error)
- def __repr__(self):
- return repr(self._http_error)
- network_exceptions = (HTTPError, TransportError)
|