Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to more concise Timeout parameters #1111

Merged
merged 5 commits into from
Aug 1, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change in emphasis is so that it's clearer now that the usage is connect=.... :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely done, yes.

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)