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