Skip to content

Commit

Permalink
Merge branch 'master' into 1.0-release-notes
Browse files Browse the repository at this point in the history
  • Loading branch information
tomchristie authored Jan 25, 2024
2 parents 9cc2e56 + 371b6e9 commit 691fbfa
Show file tree
Hide file tree
Showing 25 changed files with 623 additions and 624 deletions.
168 changes: 85 additions & 83 deletions httpx/_api.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import typing
from contextlib import contextmanager

Expand Down Expand Up @@ -25,20 +27,20 @@ def request(
method: str,
url: URLTypes,
*,
params: typing.Optional[QueryParamTypes] = None,
content: typing.Optional[RequestContent] = None,
data: typing.Optional[RequestData] = None,
files: typing.Optional[RequestFiles] = None,
json: typing.Optional[typing.Any] = None,
headers: typing.Optional[HeaderTypes] = None,
cookies: typing.Optional[CookieTypes] = None,
auth: typing.Optional[AuthTypes] = None,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
params: QueryParamTypes | None = None,
content: RequestContent | None = None,
data: RequestData | None = None,
files: RequestFiles | None = None,
json: typing.Any | None = None,
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
auth: AuthTypes | None = None,
proxy: ProxyTypes | None = None,
proxies: ProxiesTypes | None = None,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
follow_redirects: bool = False,
verify: VerifyTypes = True,
cert: typing.Optional[CertTypes] = None,
cert: CertTypes | None = None,
trust_env: bool = True,
) -> Response:
"""
Expand Down Expand Up @@ -120,20 +122,20 @@ def stream(
method: str,
url: URLTypes,
*,
params: typing.Optional[QueryParamTypes] = None,
content: typing.Optional[RequestContent] = None,
data: typing.Optional[RequestData] = None,
files: typing.Optional[RequestFiles] = None,
json: typing.Optional[typing.Any] = None,
headers: typing.Optional[HeaderTypes] = None,
cookies: typing.Optional[CookieTypes] = None,
auth: typing.Optional[AuthTypes] = None,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
params: QueryParamTypes | None = None,
content: RequestContent | None = None,
data: RequestData | None = None,
files: RequestFiles | None = None,
json: typing.Any | None = None,
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
auth: AuthTypes | None = None,
proxy: ProxyTypes | None = None,
proxies: ProxiesTypes | None = None,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
follow_redirects: bool = False,
verify: VerifyTypes = True,
cert: typing.Optional[CertTypes] = None,
cert: CertTypes | None = None,
trust_env: bool = True,
) -> typing.Iterator[Response]:
"""
Expand Down Expand Up @@ -173,14 +175,14 @@ def stream(
def get(
url: URLTypes,
*,
params: typing.Optional[QueryParamTypes] = None,
headers: typing.Optional[HeaderTypes] = None,
cookies: typing.Optional[CookieTypes] = None,
auth: typing.Optional[AuthTypes] = None,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
params: QueryParamTypes | None = None,
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
auth: AuthTypes | None = None,
proxy: ProxyTypes | None = None,
proxies: ProxiesTypes | None = None,
follow_redirects: bool = False,
cert: typing.Optional[CertTypes] = None,
cert: CertTypes | None = None,
verify: VerifyTypes = True,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
trust_env: bool = True,
Expand Down Expand Up @@ -213,14 +215,14 @@ def get(
def options(
url: URLTypes,
*,
params: typing.Optional[QueryParamTypes] = None,
headers: typing.Optional[HeaderTypes] = None,
cookies: typing.Optional[CookieTypes] = None,
auth: typing.Optional[AuthTypes] = None,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
params: QueryParamTypes | None = None,
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
auth: AuthTypes | None = None,
proxy: ProxyTypes | None = None,
proxies: ProxiesTypes | None = None,
follow_redirects: bool = False,
cert: typing.Optional[CertTypes] = None,
cert: CertTypes | None = None,
verify: VerifyTypes = True,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
trust_env: bool = True,
Expand Down Expand Up @@ -253,14 +255,14 @@ def options(
def head(
url: URLTypes,
*,
params: typing.Optional[QueryParamTypes] = None,
headers: typing.Optional[HeaderTypes] = None,
cookies: typing.Optional[CookieTypes] = None,
auth: typing.Optional[AuthTypes] = None,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
params: QueryParamTypes | None = None,
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
auth: AuthTypes | None = None,
proxy: ProxyTypes | None = None,
proxies: ProxiesTypes | None = None,
follow_redirects: bool = False,
cert: typing.Optional[CertTypes] = None,
cert: CertTypes | None = None,
verify: VerifyTypes = True,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
trust_env: bool = True,
Expand Down Expand Up @@ -293,18 +295,18 @@ def head(
def post(
url: URLTypes,
*,
content: typing.Optional[RequestContent] = None,
data: typing.Optional[RequestData] = None,
files: typing.Optional[RequestFiles] = None,
json: typing.Optional[typing.Any] = None,
params: typing.Optional[QueryParamTypes] = None,
headers: typing.Optional[HeaderTypes] = None,
cookies: typing.Optional[CookieTypes] = None,
auth: typing.Optional[AuthTypes] = None,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
content: RequestContent | None = None,
data: RequestData | None = None,
files: RequestFiles | None = None,
json: typing.Any | None = None,
params: QueryParamTypes | None = None,
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
auth: AuthTypes | None = None,
proxy: ProxyTypes | None = None,
proxies: ProxiesTypes | None = None,
follow_redirects: bool = False,
cert: typing.Optional[CertTypes] = None,
cert: CertTypes | None = None,
verify: VerifyTypes = True,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
trust_env: bool = True,
Expand Down Expand Up @@ -338,18 +340,18 @@ def post(
def put(
url: URLTypes,
*,
content: typing.Optional[RequestContent] = None,
data: typing.Optional[RequestData] = None,
files: typing.Optional[RequestFiles] = None,
json: typing.Optional[typing.Any] = None,
params: typing.Optional[QueryParamTypes] = None,
headers: typing.Optional[HeaderTypes] = None,
cookies: typing.Optional[CookieTypes] = None,
auth: typing.Optional[AuthTypes] = None,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
content: RequestContent | None = None,
data: RequestData | None = None,
files: RequestFiles | None = None,
json: typing.Any | None = None,
params: QueryParamTypes | None = None,
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
auth: AuthTypes | None = None,
proxy: ProxyTypes | None = None,
proxies: ProxiesTypes | None = None,
follow_redirects: bool = False,
cert: typing.Optional[CertTypes] = None,
cert: CertTypes | None = None,
verify: VerifyTypes = True,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
trust_env: bool = True,
Expand Down Expand Up @@ -383,18 +385,18 @@ def put(
def patch(
url: URLTypes,
*,
content: typing.Optional[RequestContent] = None,
data: typing.Optional[RequestData] = None,
files: typing.Optional[RequestFiles] = None,
json: typing.Optional[typing.Any] = None,
params: typing.Optional[QueryParamTypes] = None,
headers: typing.Optional[HeaderTypes] = None,
cookies: typing.Optional[CookieTypes] = None,
auth: typing.Optional[AuthTypes] = None,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
content: RequestContent | None = None,
data: RequestData | None = None,
files: RequestFiles | None = None,
json: typing.Any | None = None,
params: QueryParamTypes | None = None,
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
auth: AuthTypes | None = None,
proxy: ProxyTypes | None = None,
proxies: ProxiesTypes | None = None,
follow_redirects: bool = False,
cert: typing.Optional[CertTypes] = None,
cert: CertTypes | None = None,
verify: VerifyTypes = True,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
trust_env: bool = True,
Expand Down Expand Up @@ -428,14 +430,14 @@ def patch(
def delete(
url: URLTypes,
*,
params: typing.Optional[QueryParamTypes] = None,
headers: typing.Optional[HeaderTypes] = None,
cookies: typing.Optional[CookieTypes] = None,
auth: typing.Optional[AuthTypes] = None,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
params: QueryParamTypes | None = None,
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
auth: AuthTypes | None = None,
proxy: ProxyTypes | None = None,
proxies: ProxiesTypes | None = None,
follow_redirects: bool = False,
cert: typing.Optional[CertTypes] = None,
cert: CertTypes | None = None,
verify: VerifyTypes = True,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
trust_env: bool = True,
Expand Down
40 changes: 16 additions & 24 deletions httpx/_auth.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import hashlib
import os
import re
Expand Down Expand Up @@ -124,18 +126,14 @@ class BasicAuth(Auth):
and uses HTTP Basic authentication.
"""

def __init__(
self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]
) -> None:
def __init__(self, username: str | bytes, password: str | bytes) -> None:
self._auth_header = self._build_auth_header(username, password)

def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]:
request.headers["Authorization"] = self._auth_header
yield request

def _build_auth_header(
self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]
) -> str:
def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str:
userpass = b":".join((to_bytes(username), to_bytes(password)))
token = b64encode(userpass).decode()
return f"Basic {token}"
Expand All @@ -146,7 +144,7 @@ class NetRCAuth(Auth):
Use a 'netrc' file to lookup basic auth credentials based on the url host.
"""

def __init__(self, file: typing.Optional[str] = None) -> None:
def __init__(self, file: str | None = None) -> None:
# Lazily import 'netrc'.
# There's no need for us to load this module unless 'NetRCAuth' is being used.
import netrc
Expand All @@ -165,16 +163,14 @@ def auth_flow(self, request: Request) -> typing.Generator[Request, Response, Non
)
yield request

def _build_auth_header(
self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]
) -> str:
def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str:
userpass = b":".join((to_bytes(username), to_bytes(password)))
token = b64encode(userpass).decode()
return f"Basic {token}"


class DigestAuth(Auth):
_ALGORITHM_TO_HASH_FUNCTION: typing.Dict[str, typing.Callable[[bytes], "_Hash"]] = {
_ALGORITHM_TO_HASH_FUNCTION: dict[str, typing.Callable[[bytes], _Hash]] = {
"MD5": hashlib.md5,
"MD5-SESS": hashlib.md5,
"SHA": hashlib.sha1,
Expand All @@ -185,12 +181,10 @@ class DigestAuth(Auth):
"SHA-512-SESS": hashlib.sha512,
}

def __init__(
self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]
) -> None:
def __init__(self, username: str | bytes, password: str | bytes) -> None:
self._username = to_bytes(username)
self._password = to_bytes(password)
self._last_challenge: typing.Optional[_DigestAuthChallenge] = None
self._last_challenge: _DigestAuthChallenge | None = None
self._nonce_count = 1

def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]:
Expand Down Expand Up @@ -226,7 +220,7 @@ def auth_flow(self, request: Request) -> typing.Generator[Request, Response, Non

def _parse_challenge(
self, request: Request, response: Response, auth_header: str
) -> "_DigestAuthChallenge":
) -> _DigestAuthChallenge:
"""
Returns a challenge from a Digest WWW-Authenticate header.
These take the form of:
Expand All @@ -237,7 +231,7 @@ def _parse_challenge(
# This method should only ever have been called with a Digest auth header.
assert scheme.lower() == "digest"

header_dict: typing.Dict[str, str] = {}
header_dict: dict[str, str] = {}
for field in parse_http_list(fields):
key, value = field.strip().split("=", 1)
header_dict[key] = unquote(value)
Expand All @@ -256,7 +250,7 @@ def _parse_challenge(
raise ProtocolError(message, request=request) from exc

def _build_auth_header(
self, request: Request, challenge: "_DigestAuthChallenge"
self, request: Request, challenge: _DigestAuthChallenge
) -> str:
hash_func = self._ALGORITHM_TO_HASH_FUNCTION[challenge.algorithm.upper()]

Expand Down Expand Up @@ -311,7 +305,7 @@ def _get_client_nonce(self, nonce_count: int, nonce: bytes) -> bytes:

return hashlib.sha1(s).hexdigest()[:16].encode()

def _get_header_value(self, header_fields: typing.Dict[str, bytes]) -> str:
def _get_header_value(self, header_fields: dict[str, bytes]) -> str:
NON_QUOTED_FIELDS = ("algorithm", "qop", "nc")
QUOTED_TEMPLATE = '{}="{}"'
NON_QUOTED_TEMPLATE = "{}={}"
Expand All @@ -329,9 +323,7 @@ def _get_header_value(self, header_fields: typing.Dict[str, bytes]) -> str:

return header_value

def _resolve_qop(
self, qop: typing.Optional[bytes], request: Request
) -> typing.Optional[bytes]:
def _resolve_qop(self, qop: bytes | None, request: Request) -> bytes | None:
if qop is None:
return None
qops = re.split(b", ?", qop)
Expand All @@ -349,5 +341,5 @@ class _DigestAuthChallenge(typing.NamedTuple):
realm: bytes
nonce: bytes
algorithm: str
opaque: typing.Optional[bytes]
qop: typing.Optional[bytes]
opaque: bytes | None
qop: bytes | None
Loading

0 comments on commit 691fbfa

Please sign in to comment.