_exceptions.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. """
  2. Our exception hierarchy:
  3. * HTTPError
  4. x RequestError
  5. + TransportError
  6. - TimeoutException
  7. · ConnectTimeout
  8. · ReadTimeout
  9. · WriteTimeout
  10. · PoolTimeout
  11. - NetworkError
  12. · ConnectError
  13. · ReadError
  14. · WriteError
  15. · CloseError
  16. - ProtocolError
  17. · LocalProtocolError
  18. · RemoteProtocolError
  19. - ProxyError
  20. - UnsupportedProtocol
  21. + DecodingError
  22. + TooManyRedirects
  23. x HTTPStatusError
  24. * InvalidURL
  25. * CookieConflict
  26. * StreamError
  27. x StreamConsumed
  28. x StreamClosed
  29. x ResponseNotRead
  30. x RequestNotRead
  31. """
  32. from __future__ import annotations
  33. import contextlib
  34. import typing
  35. if typing.TYPE_CHECKING:
  36. from ._models import Request, Response # pragma: no cover
  37. __all__ = [
  38. "CloseError",
  39. "ConnectError",
  40. "ConnectTimeout",
  41. "CookieConflict",
  42. "DecodingError",
  43. "HTTPError",
  44. "HTTPStatusError",
  45. "InvalidURL",
  46. "LocalProtocolError",
  47. "NetworkError",
  48. "PoolTimeout",
  49. "ProtocolError",
  50. "ProxyError",
  51. "ReadError",
  52. "ReadTimeout",
  53. "RemoteProtocolError",
  54. "RequestError",
  55. "RequestNotRead",
  56. "ResponseNotRead",
  57. "StreamClosed",
  58. "StreamConsumed",
  59. "StreamError",
  60. "TimeoutException",
  61. "TooManyRedirects",
  62. "TransportError",
  63. "UnsupportedProtocol",
  64. "WriteError",
  65. "WriteTimeout",
  66. ]
  67. class HTTPError(Exception):
  68. """
  69. Base class for `RequestError` and `HTTPStatusError`.
  70. Useful for `try...except` blocks when issuing a request,
  71. and then calling `.raise_for_status()`.
  72. For example:
  73. ```
  74. try:
  75. response = httpx.get("https://www.example.com")
  76. response.raise_for_status()
  77. except httpx.HTTPError as exc:
  78. print(f"HTTP Exception for {exc.request.url} - {exc}")
  79. ```
  80. """
  81. def __init__(self, message: str) -> None:
  82. super().__init__(message)
  83. self._request: Request | None = None
  84. @property
  85. def request(self) -> Request:
  86. if self._request is None:
  87. raise RuntimeError("The .request property has not been set.")
  88. return self._request
  89. @request.setter
  90. def request(self, request: Request) -> None:
  91. self._request = request
  92. class RequestError(HTTPError):
  93. """
  94. Base class for all exceptions that may occur when issuing a `.request()`.
  95. """
  96. def __init__(self, message: str, *, request: Request | None = None) -> None:
  97. super().__init__(message)
  98. # At the point an exception is raised we won't typically have a request
  99. # instance to associate it with.
  100. #
  101. # The 'request_context' context manager is used within the Client and
  102. # Response methods in order to ensure that any raised exceptions
  103. # have a `.request` property set on them.
  104. self._request = request
  105. class TransportError(RequestError):
  106. """
  107. Base class for all exceptions that occur at the level of the Transport API.
  108. """
  109. # Timeout exceptions...
  110. class TimeoutException(TransportError):
  111. """
  112. The base class for timeout errors.
  113. An operation has timed out.
  114. """
  115. class ConnectTimeout(TimeoutException):
  116. """
  117. Timed out while connecting to the host.
  118. """
  119. class ReadTimeout(TimeoutException):
  120. """
  121. Timed out while receiving data from the host.
  122. """
  123. class WriteTimeout(TimeoutException):
  124. """
  125. Timed out while sending data to the host.
  126. """
  127. class PoolTimeout(TimeoutException):
  128. """
  129. Timed out waiting to acquire a connection from the pool.
  130. """
  131. # Core networking exceptions...
  132. class NetworkError(TransportError):
  133. """
  134. The base class for network-related errors.
  135. An error occurred while interacting with the network.
  136. """
  137. class ReadError(NetworkError):
  138. """
  139. Failed to receive data from the network.
  140. """
  141. class WriteError(NetworkError):
  142. """
  143. Failed to send data through the network.
  144. """
  145. class ConnectError(NetworkError):
  146. """
  147. Failed to establish a connection.
  148. """
  149. class CloseError(NetworkError):
  150. """
  151. Failed to close a connection.
  152. """
  153. # Other transport exceptions...
  154. class ProxyError(TransportError):
  155. """
  156. An error occurred while establishing a proxy connection.
  157. """
  158. class UnsupportedProtocol(TransportError):
  159. """
  160. Attempted to make a request to an unsupported protocol.
  161. For example issuing a request to `ftp://www.example.com`.
  162. """
  163. class ProtocolError(TransportError):
  164. """
  165. The protocol was violated.
  166. """
  167. class LocalProtocolError(ProtocolError):
  168. """
  169. A protocol was violated by the client.
  170. For example if the user instantiated a `Request` instance explicitly,
  171. failed to include the mandatory `Host:` header, and then issued it directly
  172. using `client.send()`.
  173. """
  174. class RemoteProtocolError(ProtocolError):
  175. """
  176. The protocol was violated by the server.
  177. For example, returning malformed HTTP.
  178. """
  179. # Other request exceptions...
  180. class DecodingError(RequestError):
  181. """
  182. Decoding of the response failed, due to a malformed encoding.
  183. """
  184. class TooManyRedirects(RequestError):
  185. """
  186. Too many redirects.
  187. """
  188. # Client errors
  189. class HTTPStatusError(HTTPError):
  190. """
  191. The response had an error HTTP status of 4xx or 5xx.
  192. May be raised when calling `response.raise_for_status()`
  193. """
  194. def __init__(self, message: str, *, request: Request, response: Response) -> None:
  195. super().__init__(message)
  196. self.request = request
  197. self.response = response
  198. class InvalidURL(Exception):
  199. """
  200. URL is improperly formed or cannot be parsed.
  201. """
  202. def __init__(self, message: str) -> None:
  203. super().__init__(message)
  204. class CookieConflict(Exception):
  205. """
  206. Attempted to lookup a cookie by name, but multiple cookies existed.
  207. Can occur when calling `response.cookies.get(...)`.
  208. """
  209. def __init__(self, message: str) -> None:
  210. super().__init__(message)
  211. # Stream exceptions...
  212. # These may occur as the result of a programming error, by accessing
  213. # the request/response stream in an invalid manner.
  214. class StreamError(RuntimeError):
  215. """
  216. The base class for stream exceptions.
  217. The developer made an error in accessing the request stream in
  218. an invalid way.
  219. """
  220. def __init__(self, message: str) -> None:
  221. super().__init__(message)
  222. class StreamConsumed(StreamError):
  223. """
  224. Attempted to read or stream content, but the content has already
  225. been streamed.
  226. """
  227. def __init__(self) -> None:
  228. message = (
  229. "Attempted to read or stream some content, but the content has "
  230. "already been streamed. For requests, this could be due to passing "
  231. "a generator as request content, and then receiving a redirect "
  232. "response or a secondary request as part of an authentication flow."
  233. "For responses, this could be due to attempting to stream the response "
  234. "content more than once."
  235. )
  236. super().__init__(message)
  237. class StreamClosed(StreamError):
  238. """
  239. Attempted to read or stream response content, but the request has been
  240. closed.
  241. """
  242. def __init__(self) -> None:
  243. message = (
  244. "Attempted to read or stream content, but the stream has " "been closed."
  245. )
  246. super().__init__(message)
  247. class ResponseNotRead(StreamError):
  248. """
  249. Attempted to access streaming response content, without having called `read()`.
  250. """
  251. def __init__(self) -> None:
  252. message = (
  253. "Attempted to access streaming response content,"
  254. " without having called `read()`."
  255. )
  256. super().__init__(message)
  257. class RequestNotRead(StreamError):
  258. """
  259. Attempted to access streaming request content, without having called `read()`.
  260. """
  261. def __init__(self) -> None:
  262. message = (
  263. "Attempted to access streaming request content,"
  264. " without having called `read()`."
  265. )
  266. super().__init__(message)
  267. @contextlib.contextmanager
  268. def request_context(
  269. request: Request | None = None,
  270. ) -> typing.Iterator[None]:
  271. """
  272. A context manager that can be used to attach the given request context
  273. to any `RequestError` exceptions that are raised within the block.
  274. """
  275. try:
  276. yield
  277. except RequestError as exc:
  278. if request is not None:
  279. exc.request = request
  280. raise exc