]> git.ipfire.org Git - thirdparty/httpx.git/commitdiff
Allow default+override timeout style (#593)
authorTom Christie <tom@tomchristie.com>
Thu, 5 Dec 2019 09:38:48 +0000 (09:38 +0000)
committerGitHub <noreply@github.com>
Thu, 5 Dec 2019 09:38:48 +0000 (09:38 +0000)
* Allow styles like: httpx.Timeout(5.0, pool_timeout=None)

* Update timeout docs

* Minor tweaks to sub headings in timeout docs

* Fixing up Timeout docs

* RequestTimeout -> TimeoutException

* Tweak timeout docs

docs/advanced.md
docs/quickstart.md
httpx/__init__.py
httpx/client.py
httpx/config.py
httpx/exceptions.py
tests/test_config.py

index e13ea452f441e835f18c01be74b6afe4cdd728fa..42346d50391ba870a7efd8ecdae582042d6684a9 100644 (file)
@@ -250,83 +250,78 @@ async with httpx.Client(proxies=proxy) as client:
     has not been implemented yet. To use proxies you must pass the proxy
     information at `Client` initialization.
 
-## Timeout fine-tuning
+## Timeout Configuration
 
-HTTPX offers various request timeout management options. Three types of timeouts
-are available: **connect** timeouts, **write** timeouts and **read** timeouts.
+HTTPX is careful to enforce timeouts everywhere by default.
 
-* The **connect timeout** specifies the maximum amount of time to wait until
-a connection to the requested host is established. If HTTPX is unable to connect
-within this time frame, a `ConnectTimeout` exception is raised.
-* The **write timeout** specifies the maximum duration to wait for a chunk of
-data to be sent (for example, a chunk of the request body). If HTTPX is unable
-to send data within this time frame, a `WriteTimeout` exception is raised.
-* The **read timeout** specifies the maximum duration to wait for a chunk of
-data to be received (for example, a chunk of the response body). If HTTPX is
-unable to receive data within this time frame, a `ReadTimeout` exception is raised.
-
-### Setting timeouts
+The default behavior is to raise a `TimeoutException` after 5 seconds of
+network inactivity.
 
-You can set timeouts on two levels:
+### Setting and disabling timeouts
 
-- For a given request:
+You can set timeouts for an individual request:
 
 ```python
-# Using top-level API
-await httpx.get('http://example.com/api/v1/example', timeout=5)
+# Using the top-level API:
+await httpx.get('http://example.com/api/v1/example', timeout=10.0)
 
-# Or, with a client:
+# Using a client instance:
 async with httpx.Client() as client:
-    await client.get("http://example.com/api/v1/example", timeout=5)
+    await client.get("http://example.com/api/v1/example", timeout=10.0)
 ```
 
-- On a client instance, which results in the given `timeout` being used as a default for requests made with this client:
+Or disable timeouts for an individual request:
 
 ```python
-async with httpx.Client(timeout=5) as client:
-    await client.get('http://example.com/api/v1/example')
+# Using the top-level API:
+await httpx.get('http://example.com/api/v1/example', timeout=None)
+
+# Using a client instance:
+async with httpx.Client() as client:
+    await client.get("http://example.com/api/v1/example", timeout=None)
 ```
 
-Besides, you can pass timeouts in two forms:
+### Setting a default timeout on a client
 
-- A number, which sets the read, write and connect timeouts to the same value, as in the examples above.
-- A `Timeout` instance, which allows to define the read, write and connect timeouts independently:
+You can set a timeout on a client instance, which results in the given
+`timeout` being used as the default for requests made with this client:
 
 ```python
-timeout = httpx.Timeout(
-    connect_timeout=5,
-    read_timeout=10,
-    write_timeout=15
-)
-
-resp = await httpx.get('http://example.com/api/v1/example', timeout=timeout)
+client = httpx.Client()              # Use a default 5s timeout everywhere.
+client = httpx.Client(timeout=10.0)  # Use a default 10s timeout everywhere.
+client = httpx.Client(timeout=None)  # Disable all timeouts by default.
 ```
 
-### Default timeouts
-
-By default all types of timeouts are set to 5 second.
+### Fine tuning the configuration
 
-### Disabling timeouts
+HTTPX also allows you to specify the timeout behavior in more fine grained detail.
 
-To disable timeouts, you can pass `None` as a timeout parameter.
-Note that currently this is not supported by the top-level API.
-
-```python
-url = "http://example.com/api/v1/delay/10"
-
-await httpx.get(url, timeout=None)  # Times out after 5s
+There are four different types of timeouts that may occur. These are **connect**,
+**read**, **write**, and **pool** timeouts.
 
+* The **connect timeout** specifies the maximum amount of time to wait until
+a connection to the requested host is established. If HTTPX is unable to connect
+within this time frame, a `ConnectTimeout` exception is raised.
+* The **read timeout** specifies the maximum duration to wait for a chunk of
+data to be received (for example, a chunk of the response body). If HTTPX is
+unable to receive data within this time frame, a `ReadTimeout` exception is raised.
+* The **write timeout** specifies the maximum duration to wait for a chunk of
+data to be sent (for example, a chunk of the request body). If HTTPX is unable
+to send data within this time frame, a `WriteTimeout` exception is raised.
+* The **pool timeout** specifies the maximum duration to wait for acquiring
+a connection from the connection pool. If HTTPX is unable to acquire a connection
+within this time frame, a `PoolTimeout` exception is raised. A related
+configuration here is the maximum number of allowable connections in the
+connection pool, which is configured by the `pool_limits`.
 
-async with httpx.Client(timeout=None) as client:
-    await client.get(url)  # Does not timeout, returns after 10s
+You can configure the timeout behavior for any of these values...
 
+```python
+# A client with a 60s timeout for connecting, and a 10s timeout elsewhere.
+timeout = httpx.Timeout(10.0, connect_timeout=60.0)
+client = httpx.Client(timeout=timeout)
 
-timeout = httpx.Timeout(
-    connect_timeout=5,
-    read_timeout=None,
-    write_timeout=5
-)
-await httpx.get(url, timeout=timeout) # Does not timeout, returns after 10s
+response = await client.get('http://example.com/')
 ```
 
 ## Multipart file encoding
index f1b9be83918a63af43c9d01c500bf48bc9742c8a..636dfe6bb0568467e0b3e540f7e57803bf1a2c8a 100644 (file)
@@ -381,6 +381,12 @@ value to be more or less strict:
 >>> await httpx.get('https://github.com/', timeout=0.001)
 ```
 
+You can also disable the timeout behavior completely...
+
+```python
+>>> await httpx.get('https://github.com/', timeout=None)
+```
+
 For advanced timeout management, see [Timeout fine-tuning](https://www.encode.io/httpx/advanced/#timeout-fine-tuning).
 
 ## Authentication
index 69f7a421425044f3e2d60c65d7934b334eebd530..febe2c708beaa2c4d803e67b3066949e04f1f814 100644 (file)
@@ -36,10 +36,10 @@ from .exceptions import (
     ReadTimeout,
     RedirectBodyUnavailable,
     RedirectLoop,
-    RequestTimeout,
     ResponseClosed,
     ResponseNotRead,
     StreamConsumed,
+    TimeoutException,
     TooManyRedirects,
     WriteTimeout,
 )
@@ -127,7 +127,7 @@ __all__ = [
     "QueryParamTypes",
     "Request",
     "RequestData",
-    "RequestTimeout",
+    "TimeoutException",
     "Response",
     "ResponseContent",
     "RequestFiles",
index 36f6a2404dfc36c635dfd7e23c2a734f39426701..24b6a457dd23ed9ef5a4a35a234c846ed306793b 100644 (file)
@@ -10,10 +10,12 @@ from .config import (
     DEFAULT_MAX_REDIRECTS,
     DEFAULT_POOL_LIMITS,
     DEFAULT_TIMEOUT_CONFIG,
+    UNSET,
     CertTypes,
     PoolLimits,
     Timeout,
     TimeoutTypes,
+    UnsetType,
     VerifyTypes,
 )
 from .dispatch.asgi import ASGIDispatch
@@ -50,13 +52,6 @@ from .utils import ElapsedTimer, NetRCInfo, get_environment_proxies, get_logger
 logger = get_logger(__name__)
 
 
-class UnsetType:
-    pass  # pragma: nocover
-
-
-UNSET = UnsetType()
-
-
 class Client:
     """
     An HTTP client, with connection pooling, HTTP/2, redirects, cookie persistence, etc.
index 109344154944ff833b87edd8e65387016d773ec1..34c3ad04f51dc0588bf5631ee62738e2a799a49d 100644 (file)
@@ -38,6 +38,13 @@ DEFAULT_CIPHERS = ":".join(
 logger = get_logger(__name__)
 
 
+class UnsetType:
+    pass  # pragma: nocover
+
+
+UNSET = UnsetType()
+
+
 class SSLConfig:
     """
     SSL Configuration.
@@ -203,44 +210,58 @@ class SSLConfig:
 
 class Timeout:
     """
-    Timeout values.
+    Timeout configuration.
+
+    **Usage**:
+
+    Timeout()                           # No timeout.
+    Timeout(5.0)                        # 5s timeout on all operations.
+    Timeout(connect_timeout=5.0)        # 5s timeout on connect, no other timeouts.
+    Timeout(5.0, connect_timeout=10.0)  # 10s timeout on connect. 5s timeout elsewhere.
+    Timeout(5.0, pool_timeout=None)     # No timeout on acquiring connection from pool.
+                                        # 5s timeout elsewhere.
     """
 
     def __init__(
         self,
         timeout: TimeoutTypes = None,
         *,
-        connect_timeout: float = None,
-        read_timeout: float = None,
-        write_timeout: float = None,
-        pool_timeout: float = None,
+        connect_timeout: typing.Union[None, float, UnsetType] = UNSET,
+        read_timeout: typing.Union[None, float, UnsetType] = UNSET,
+        write_timeout: typing.Union[None, float, UnsetType] = UNSET,
+        pool_timeout: typing.Union[None, float, UnsetType] = UNSET,
     ):
-        if timeout is None:
-            self.connect_timeout = connect_timeout
-            self.read_timeout = read_timeout
-            self.write_timeout = write_timeout
-            self.pool_timeout = pool_timeout
+        if isinstance(timeout, Timeout):
+            # Passed as a single explicit Timeout.
+            assert connect_timeout is UNSET
+            assert read_timeout is UNSET
+            assert write_timeout is UNSET
+            assert pool_timeout is UNSET
+            self.connect_timeout = (
+                timeout.connect_timeout
+            )  # type: typing.Optional[float]
+            self.read_timeout = timeout.read_timeout  # type: typing.Optional[float]
+            self.write_timeout = timeout.write_timeout  # type: typing.Optional[float]
+            self.pool_timeout = timeout.pool_timeout  # type: typing.Optional[float]
+        elif isinstance(timeout, tuple):
+            # Passed as a tuple.
+            self.connect_timeout = timeout[0]
+            self.read_timeout = timeout[1]
+            self.write_timeout = None if len(timeout) < 3 else timeout[2]
+            self.pool_timeout = None if len(timeout) < 4 else timeout[3]
         else:
-            # Specified as a single timeout value
-            assert connect_timeout is None
-            assert read_timeout is None
-            assert write_timeout is None
-            assert pool_timeout is None
-            if isinstance(timeout, Timeout):
-                self.connect_timeout = timeout.connect_timeout
-                self.read_timeout = timeout.read_timeout
-                self.write_timeout = timeout.write_timeout
-                self.pool_timeout = timeout.pool_timeout
-            elif isinstance(timeout, tuple):
-                self.connect_timeout = timeout[0]
-                self.read_timeout = timeout[1]
-                self.write_timeout = None if len(timeout) < 3 else timeout[2]
-                self.pool_timeout = None if len(timeout) < 4 else timeout[3]
-            else:
-                self.connect_timeout = timeout
-                self.read_timeout = timeout
-                self.write_timeout = timeout
-                self.pool_timeout = timeout
+            self.connect_timeout = (
+                timeout if isinstance(connect_timeout, UnsetType) else connect_timeout
+            )
+            self.read_timeout = (
+                timeout if isinstance(read_timeout, UnsetType) else read_timeout
+            )
+            self.write_timeout = (
+                timeout if isinstance(write_timeout, UnsetType) else write_timeout
+            )
+            self.pool_timeout = (
+                timeout if isinstance(pool_timeout, UnsetType) else pool_timeout
+            )
 
     def __eq__(self, other: typing.Any) -> bool:
         return (
index 61710cf5318d3e7bcef964eb0f0f81bf27970ead..b19669c6ab11fb5393fea8770dbade6ef80956af 100644 (file)
@@ -20,31 +20,31 @@ class HTTPError(Exception):
 # Timeout exceptions...
 
 
-class RequestTimeout(HTTPError):
+class TimeoutException(HTTPError):
     """
     A base class for all timeouts.
     """
 
 
-class ConnectTimeout(RequestTimeout):
+class ConnectTimeout(TimeoutException):
     """
     Timeout while establishing a connection.
     """
 
 
-class ReadTimeout(RequestTimeout):
+class ReadTimeout(TimeoutException):
     """
     Timeout while reading response data.
     """
 
 
-class WriteTimeout(RequestTimeout):
+class WriteTimeout(TimeoutException):
     """
     Timeout while writing request data.
     """
 
 
-class PoolTimeout(RequestTimeout):
+class PoolTimeout(TimeoutException):
     """
     Timeout while waiting to acquire a connection from the pool.
     """
index 0109e8f3e32047ad989a4173453af6a959933105..ad7a626caec3d802dc8dc0433dbfdde17d68acba 100644 (file)
@@ -156,6 +156,16 @@ def test_timeout_from_one_none_value():
     assert timeout == httpx.Timeout()
 
 
+def test_timeout_from_one_value():
+    timeout = httpx.Timeout(read_timeout=5.0)
+    assert timeout == httpx.Timeout(timeout=(None, 5.0, None, None))
+
+
+def test_timeout_from_one_value_and_default():
+    timeout = httpx.Timeout(5.0, pool_timeout=60.0)
+    assert timeout == httpx.Timeout(timeout=(5.0, 5.0, 5.0, 60.0))
+
+
 def test_timeout_from_tuple():
     timeout = httpx.Timeout(timeout=(5.0, 5.0, 5.0, 5.0))
     assert timeout == httpx.Timeout(timeout=5.0)