Skip to content

Commit

Permalink
Switch to more concise Timeout parameters (#1111)
Browse files Browse the repository at this point in the history
* Switch to more concise Timeout parameters

* Update docs

* Rename attributes

Co-authored-by: Tom Christie <tom@tomchristie.com>
  • Loading branch information
florimondmanca and tomchristie committed Aug 1, 2020
1 parent a928421 commit 26cd4f5
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 89 deletions.
10 changes: 5 additions & 5 deletions docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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/')
Expand Down
144 changes: 80 additions & 64 deletions httpx/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
[
Expand Down Expand Up @@ -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(
Expand All @@ -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})"
)


Expand Down
27 changes: 11 additions & 16 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand All @@ -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))


Expand All @@ -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(
Expand Down
22 changes: 18 additions & 4 deletions tests/test_timeouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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)

0 comments on commit 26cd4f5

Please sign in to comment.