Skip to content
Merged
3 changes: 2 additions & 1 deletion httpx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .__version__ import __description__, __title__, __version__
from ._api import delete, get, head, options, patch, post, put, request, stream
from ._auth import Auth, BasicAuth, DigestAuth
from ._client import AsyncClient, Client
from ._client import USE_CLIENT_DEFAULT, AsyncClient, Client
from ._config import Limits, Proxy, Timeout, create_ssl_context
from ._content import ByteStream
from ._exceptions import (
Expand Down Expand Up @@ -111,6 +111,7 @@
"TransportError",
"UnsupportedProtocol",
"URL",
"USE_CLIENT_DEFAULT",
"WriteError",
"WriteTimeout",
"WSGITransport",
Expand Down
123 changes: 77 additions & 46 deletions httpx/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@
DEFAULT_LIMITS,
DEFAULT_MAX_REDIRECTS,
DEFAULT_TIMEOUT_CONFIG,
UNSET,
Limits,
Proxy,
Timeout,
UnsetType,
)
from ._decoders import SUPPORTED_DECODERS
from ._exceptions import (
Expand Down Expand Up @@ -65,6 +63,31 @@
U = typing.TypeVar("U", bound="AsyncClient")


class UseClientDefault:
"""
For some parameters such as `auth=...` and `timeout=...` we need to be able
to indicate the default "unset" state, in a way that is distinctly different
to using `None`.

The default "unset" state indicates that whatever default is set on the
client should be used. This is different to setting `None`, which
explicitly disables the parameter, possibly overriding a client default.

For example we use `timeout=USE_CLIENT_DEFAULT` in the `request()` signature.
Omitting the `timeout` parameter will send a request using whatever default
timeout has been configured on the client. Including `timeout=None` will
ensure no timeout is used.

Note that user code shouldn't need to use the `USE_CLIENT_DEFAULT` constant,
but it is used internally when a parameter is not included.
"""

pass # pragma: nocover


USE_CLIENT_DEFAULT = UseClientDefault()


logger = get_logger(__name__)

USER_AGENT = f"python-httpx/{__version__}"
Expand Down Expand Up @@ -402,9 +425,13 @@ def _build_auth(self, auth: AuthTypes) -> typing.Optional[Auth]:
raise TypeError(f'Invalid "auth" argument: {auth!r}')

def _build_request_auth(
self, request: Request, auth: typing.Union[AuthTypes, UnsetType] = UNSET
self,
request: Request,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Auth:
auth = self._auth if isinstance(auth, UnsetType) else self._build_auth(auth)
auth = (
self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth)
)

if auth is not None:
return auth
Expand Down Expand Up @@ -705,9 +732,9 @@ def request(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Build and send a request.
Expand Down Expand Up @@ -761,9 +788,9 @@ def stream(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> typing.Iterator[Response]:
"""
Alternative to `httpx.request()` that streams the response body
Expand Down Expand Up @@ -803,9 +830,9 @@ def send(
request: Request,
*,
stream: bool = False,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a request.
Expand All @@ -824,7 +851,9 @@ def send(
raise RuntimeError("Cannot send a request, as the client has been closed.")

self._state = ClientState.OPENED
timeout = self.timeout if isinstance(timeout, UnsetType) else Timeout(timeout)
timeout = (
self.timeout if isinstance(timeout, UseClientDefault) else Timeout(timeout)
)

auth = self._build_request_auth(request, auth)

Expand Down Expand Up @@ -963,9 +992,9 @@ def get(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `GET` request.
Expand All @@ -990,9 +1019,9 @@ def options(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send an `OPTIONS` request.
Expand All @@ -1017,9 +1046,9 @@ def head(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `HEAD` request.
Expand Down Expand Up @@ -1048,9 +1077,9 @@ def post(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `POST` request.
Expand Down Expand Up @@ -1083,9 +1112,9 @@ def put(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `PUT` request.
Expand Down Expand Up @@ -1118,9 +1147,9 @@ def patch(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `PATCH` request.
Expand Down Expand Up @@ -1149,9 +1178,9 @@ def delete(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `DELETE` request.
Expand Down Expand Up @@ -1391,9 +1420,9 @@ async def request(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Build and send a request.
Expand Down Expand Up @@ -1440,9 +1469,9 @@ async def stream(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> typing.AsyncIterator[Response]:
"""
Alternative to `httpx.request()` that streams the response body
Expand Down Expand Up @@ -1482,9 +1511,9 @@ async def send(
request: Request,
*,
stream: bool = False,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a request.
Expand All @@ -1503,7 +1532,9 @@ async def send(
raise RuntimeError("Cannot send a request, as the client has been closed.")

self._state = ClientState.OPENED
timeout = self.timeout if isinstance(timeout, UnsetType) else Timeout(timeout)
timeout = (
self.timeout if isinstance(timeout, UseClientDefault) else Timeout(timeout)
)

auth = self._build_request_auth(request, auth)

Expand Down Expand Up @@ -1649,9 +1680,9 @@ async def get(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `GET` request.
Expand All @@ -1676,9 +1707,9 @@ async def options(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send an `OPTIONS` request.
Expand All @@ -1703,9 +1734,9 @@ async def head(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `HEAD` request.
Expand Down Expand Up @@ -1734,9 +1765,9 @@ async def post(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `POST` request.
Expand Down Expand Up @@ -1769,9 +1800,9 @@ async def put(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `PUT` request.
Expand Down Expand Up @@ -1804,9 +1835,9 @@ async def patch(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `PATCH` request.
Expand Down Expand Up @@ -1835,9 +1866,9 @@ async def delete(
params: QueryParamTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
allow_redirects: bool = True,
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
) -> Response:
"""
Send a `DELETE` request.
Expand Down