diff --git a/docs/advanced.md b/docs/advanced.md index a55aacd810..4fdafc1c1a 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -374,16 +374,16 @@ HTTPX also allows you to specify the timeout behavior in more fine grained detai 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 +* 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 +* 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 +* 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 +* 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 @@ -393,7 +393,7 @@ 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) +timeout = httpx.Timeout(10.0, connect=60.0) client = httpx.Client(timeout=timeout) response = client.get('http://example.com/') diff --git a/httpx/_config.py b/httpx/_config.py index f7a185bf0e..4bd633aa8c 100644 --- a/httpx/_config.py +++ b/httpx/_config.py @@ -9,7 +9,7 @@ from ._models import URL, Headers from ._types import CertTypes, HeaderTypes, TimeoutTypes, URLTypes, VerifyTypes -from ._utils import get_ca_bundle_from_env, get_logger +from ._utils import get_ca_bundle_from_env, get_logger, warn_deprecated DEFAULT_CIPHERS = ":".join( [ @@ -196,51 +196,86 @@ class Timeout: **Usage**: - Timeout(None) # No timeouts. - Timeout(5.0) # 5s timeout on all operations. - Timeout(None, 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. + Timeout(None) # No timeouts. + Timeout(5.0) # 5s timeout on all operations. + Timeout(None, connect=5.0) # 5s timeout on connect, no other timeouts. + Timeout(5.0, connect=10.0) # 10s timeout on connect. 5s timeout elsewhere. + Timeout(5.0, pool=None) # No timeout on acquiring connection from pool. + # 5s timeout elsewhere. """ def __init__( self, timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET, *, + connect: typing.Union[None, float, UnsetType] = UNSET, + read: typing.Union[None, float, UnsetType] = UNSET, + write: typing.Union[None, float, UnsetType] = UNSET, + pool: typing.Union[None, float, UnsetType] = UNSET, + # Deprecated aliases. 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 not isinstance(connect_timeout, UnsetType): + warn_deprecated( + "httpx.Timeout(..., connect_timeout=...) is deprecated and will " + "raise errors in a future version. " + "Use httpx.Timeout(..., connect=...) instead." + ) + connect = connect_timeout + + if not isinstance(read_timeout, UnsetType): + warn_deprecated( + "httpx.Timeout(..., read_timeout=...) is deprecated and will " + "raise errors in a future version. " + "Use httpx.Timeout(..., write=...) instead." + ) + read = read_timeout + + if not isinstance(write_timeout, UnsetType): + warn_deprecated( + "httpx.Timeout(..., write_timeout=...) is deprecated and will " + "raise errors in a future version. " + "Use httpx.Timeout(..., write=...) instead." + ) + write = write_timeout + + if not isinstance(pool_timeout, UnsetType): + warn_deprecated( + "httpx.Timeout(..., pool_timeout=...) is deprecated and will " + "raise errors in a future version. " + "Use httpx.Timeout(..., pool=...) instead." + ) + pool = 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] + assert connect is UNSET + assert read is UNSET + assert write is UNSET + assert pool is UNSET + self.connect = timeout.connect # type: typing.Optional[float] + self.read = timeout.read # type: typing.Optional[float] + self.write = timeout.write # type: typing.Optional[float] + self.pool = timeout.pool # 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] + self.connect = timeout[0] + self.read = timeout[1] + self.write = None if len(timeout) < 3 else timeout[2] + self.pool = None if len(timeout) < 4 else timeout[3] elif not ( - isinstance(connect_timeout, UnsetType) - or isinstance(read_timeout, UnsetType) - or isinstance(write_timeout, UnsetType) - or isinstance(pool_timeout, UnsetType) + isinstance(connect, UnsetType) + or isinstance(read, UnsetType) + or isinstance(write, UnsetType) + or isinstance(pool, UnsetType) ): - self.connect_timeout = connect_timeout - self.read_timeout = read_timeout - self.write_timeout = write_timeout - self.pool_timeout = pool_timeout + self.connect = connect + self.read = read + self.write = write + self.pool = pool else: if isinstance(timeout, UnsetType): warnings.warn( @@ -250,54 +285,35 @@ def __init__( DeprecationWarning, ) timeout = None - 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 - ) + self.connect = timeout if isinstance(connect, UnsetType) else connect + self.read = timeout if isinstance(read, UnsetType) else read + self.write = timeout if isinstance(write, UnsetType) else write + self.pool = timeout if isinstance(pool, UnsetType) else pool def as_dict(self) -> typing.Dict[str, typing.Optional[float]]: return { - "connect": self.connect_timeout, - "read": self.read_timeout, - "write": self.write_timeout, - "pool": self.pool_timeout, + "connect": self.connect, + "read": self.read, + "write": self.write, + "pool": self.pool, } def __eq__(self, other: typing.Any) -> bool: return ( isinstance(other, self.__class__) - and self.connect_timeout == other.connect_timeout - and self.read_timeout == other.read_timeout - and self.write_timeout == other.write_timeout - and self.pool_timeout == other.pool_timeout + and self.connect == other.connect + and self.read == other.read + and self.write == other.write + and self.pool == other.pool ) def __repr__(self) -> str: class_name = self.__class__.__name__ - if ( - len( - { - self.connect_timeout, - self.read_timeout, - self.write_timeout, - self.pool_timeout, - } - ) - == 1 - ): - return f"{class_name}(timeout={self.connect_timeout})" + if len({self.connect, self.read, self.write, self.pool}) == 1: + return f"{class_name}(timeout={self.connect})" return ( - f"{class_name}(connect_timeout={self.connect_timeout}, " - f"read_timeout={self.read_timeout}, write_timeout={self.write_timeout}, " - f"pool_timeout={self.pool_timeout})" + f"{class_name}(connect={self.connect}, " + f"read={self.read}, write={self.write}, pool={self.pool})" ) diff --git a/tests/test_config.py b/tests/test_config.py index 7dafaf11aa..ee3932c769 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -116,18 +116,16 @@ def test_timeout_eq(): def test_timeout_all_parameters_set(): - timeout = httpx.Timeout( - connect_timeout=5.0, read_timeout=5.0, write_timeout=5.0, pool_timeout=5.0 - ) + timeout = httpx.Timeout(connect=5.0, read=5.0, write=5.0, pool=5.0) assert timeout == httpx.Timeout(timeout=5.0) def test_timeout_from_nothing(): timeout = httpx.Timeout(None) - assert timeout.connect_timeout is None - assert timeout.read_timeout is None - assert timeout.write_timeout is None - assert timeout.pool_timeout is None + assert timeout.connect is None + assert timeout.read is None + assert timeout.write is None + assert timeout.pool is None def test_timeout_from_none(): @@ -136,23 +134,23 @@ def test_timeout_from_none(): def test_timeout_from_one_none_value(): - timeout = httpx.Timeout(None, read_timeout=None) + timeout = httpx.Timeout(None, read=None) assert timeout == httpx.Timeout(None) def test_timeout_from_one_value(): - timeout = httpx.Timeout(None, read_timeout=5.0) + timeout = httpx.Timeout(None, read=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) + timeout = httpx.Timeout(5.0, pool=60.0) assert timeout == httpx.Timeout(timeout=(5.0, 5.0, 5.0, 60.0)) def test_timeout_missing_default(): with pytest.warns(DeprecationWarning): - timeout = httpx.Timeout(pool_timeout=60.0) + timeout = httpx.Timeout(pool=60.0) assert timeout == httpx.Timeout(timeout=(None, None, None, 60.0)) @@ -170,11 +168,8 @@ def test_timeout_repr(): timeout = httpx.Timeout(timeout=5.0) assert repr(timeout) == "Timeout(timeout=5.0)" - timeout = httpx.Timeout(None, read_timeout=5.0) - assert repr(timeout) == ( - "Timeout(connect_timeout=None, read_timeout=5.0, " - "write_timeout=None, pool_timeout=None)" - ) + timeout = httpx.Timeout(None, read=5.0) + assert repr(timeout) == "Timeout(connect=None, read=5.0, write=None, pool=None)" @pytest.mark.skipif( diff --git a/tests/test_timeouts.py b/tests/test_timeouts.py index f748148f7a..1c61ba5ee4 100644 --- a/tests/test_timeouts.py +++ b/tests/test_timeouts.py @@ -5,7 +5,7 @@ @pytest.mark.usefixtures("async_environment") async def test_read_timeout(server): - timeout = httpx.Timeout(None, read_timeout=1e-6) + timeout = httpx.Timeout(None, read=1e-6) async with httpx.AsyncClient(timeout=timeout) as client: with pytest.raises(httpx.ReadTimeout): @@ -14,7 +14,7 @@ async def test_read_timeout(server): @pytest.mark.usefixtures("async_environment") async def test_write_timeout(server): - timeout = httpx.Timeout(None, write_timeout=1e-6) + timeout = httpx.Timeout(None, write=1e-6) async with httpx.AsyncClient(timeout=timeout) as client: with pytest.raises(httpx.WriteTimeout): @@ -24,7 +24,7 @@ async def test_write_timeout(server): @pytest.mark.usefixtures("async_environment") async def test_connect_timeout(server): - timeout = httpx.Timeout(None, connect_timeout=1e-6) + timeout = httpx.Timeout(None, connect=1e-6) async with httpx.AsyncClient(timeout=timeout) as client: with pytest.raises(httpx.ConnectTimeout): @@ -35,9 +35,23 @@ async def test_connect_timeout(server): @pytest.mark.usefixtures("async_environment") async def test_pool_timeout(server): pool_limits = httpx.PoolLimits(max_connections=1) - timeout = httpx.Timeout(None, pool_timeout=1e-4) + timeout = httpx.Timeout(None, pool=1e-4) async with httpx.AsyncClient(pool_limits=pool_limits, timeout=timeout) as client: async with client.stream("GET", server.url): with pytest.raises(httpx.PoolTimeout): await client.get("http://localhost:8000/") + + +def test_deprecated_verbose_timeout_params(): + with pytest.warns(DeprecationWarning): + httpx.Timeout(None, read_timeout=1.0) + + with pytest.warns(DeprecationWarning): + httpx.Timeout(None, write_timeout=1.0) + + with pytest.warns(DeprecationWarning): + httpx.Timeout(None, connect_timeout=1.0) + + with pytest.warns(DeprecationWarning): + httpx.Timeout(None, pool_timeout=1.0)