fix unasync

This commit is contained in:
Kar Petrosyan 2025-02-27 20:47:41 +04:00
parent a2fc6825a5
commit e7668b6ea0
9 changed files with 179 additions and 17 deletions

View File

@ -9,10 +9,10 @@ import netrc
import os
import sys
import typing
from threading import Lock
from urllib.request import parse_keqv_list
import pytest
from threading import Lock
import httpx
@ -148,6 +148,7 @@ class Auth(httpx.Auth):
yield request
def test_basic_auth() -> None:
url = "https://example.org/"
auth = ("user", "password123")
@ -160,6 +161,7 @@ def test_basic_auth() -> None:
assert response.json() == {"auth": "Basic dXNlcjpwYXNzd29yZDEyMw=="}
def test_basic_auth_with_stream() -> None:
"""
See: https://github.com/encode/httpx/pull/1312
@ -168,7 +170,9 @@ def test_basic_auth_with_stream() -> None:
auth = ("user", "password123")
app = App()
with httpx.Client(transport=httpx.MockTransport(app), auth=auth) as client:
with httpx.Client(
transport=httpx.MockTransport(app), auth=auth
) as client:
with client.stream("GET", url) as response:
response.read()
@ -176,6 +180,7 @@ def test_basic_auth_with_stream() -> None:
assert response.json() == {"auth": "Basic dXNlcjpwYXNzd29yZDEyMw=="}
def test_basic_auth_in_url() -> None:
url = "https://user:password123@example.org/"
app = App()
@ -187,18 +192,22 @@ def test_basic_auth_in_url() -> None:
assert response.json() == {"auth": "Basic dXNlcjpwYXNzd29yZDEyMw=="}
def test_basic_auth_on_session() -> None:
url = "https://example.org/"
auth = ("user", "password123")
app = App()
with httpx.Client(transport=httpx.MockTransport(app), auth=auth) as client:
with httpx.Client(
transport=httpx.MockTransport(app), auth=auth
) as client:
response = client.get(url)
assert response.status_code == 200
assert response.json() == {"auth": "Basic dXNlcjpwYXNzd29yZDEyMw=="}
def test_custom_auth() -> None:
url = "https://example.org/"
app = App()
@ -214,6 +223,7 @@ def test_custom_auth() -> None:
assert response.json() == {"auth": "Token 123"}
def test_netrc_auth_credentials_exist() -> None:
"""
When netrc auth is being used and a request is made to a host that is
@ -224,7 +234,9 @@ def test_netrc_auth_credentials_exist() -> None:
app = App()
auth = httpx.NetRCAuth(netrc_file)
with httpx.Client(transport=httpx.MockTransport(app), auth=auth) as client:
with httpx.Client(
transport=httpx.MockTransport(app), auth=auth
) as client:
response = client.get(url)
assert response.status_code == 200
@ -233,6 +245,7 @@ def test_netrc_auth_credentials_exist() -> None:
}
def test_netrc_auth_credentials_do_not_exist() -> None:
"""
When netrc auth is being used and a request is made to a host that is
@ -243,7 +256,9 @@ def test_netrc_auth_credentials_do_not_exist() -> None:
app = App()
auth = httpx.NetRCAuth(netrc_file)
with httpx.Client(transport=httpx.MockTransport(app), auth=auth) as client:
with httpx.Client(
transport=httpx.MockTransport(app), auth=auth
) as client:
response = client.get(url)
assert response.status_code == 200
@ -254,6 +269,7 @@ def test_netrc_auth_credentials_do_not_exist() -> None:
sys.version_info >= (3, 11),
reason="netrc files without a password are valid from Python >= 3.11",
)
def test_netrc_auth_nopassword_parse_error() -> None: # pragma: no cover
"""
Python has different netrc parsing behaviours with different versions.
@ -265,18 +281,22 @@ def test_netrc_auth_nopassword_parse_error() -> None: # pragma: no cover
httpx.NetRCAuth(netrc_file)
def test_auth_disable_per_request() -> None:
url = "https://example.org/"
auth = ("user", "password123")
app = App()
with httpx.Client(transport=httpx.MockTransport(app), auth=auth) as client:
with httpx.Client(
transport=httpx.MockTransport(app), auth=auth
) as client:
response = client.get(url, auth=None)
assert response.status_code == 200
assert response.json() == {"auth": None}
def test_auth_hidden_url() -> None:
url = "http://example-username:example-password@example.org/"
expected = "URL('http://example-username:[secure]@example.org/')"
@ -284,6 +304,7 @@ def test_auth_hidden_url() -> None:
assert expected == repr(httpx.URL(url))
def test_auth_hidden_header() -> None:
url = "https://example.org/"
auth = ("example-username", "example-password")
@ -295,6 +316,7 @@ def test_auth_hidden_header() -> None:
assert "'authorization': '[secure]'" in str(response.request.headers)
def test_auth_property() -> None:
app = App()
@ -310,6 +332,7 @@ def test_auth_property() -> None:
assert response.json() == {"auth": "Basic dXNlcjpwYXNzd29yZDEyMw=="}
def test_auth_invalid_type() -> None:
app = App()
@ -327,6 +350,7 @@ def test_auth_invalid_type() -> None:
client.auth = "not a tuple, not a callable" # type: ignore
def test_digest_auth_returns_no_auth_if_no_digest_header_in_response() -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -340,6 +364,7 @@ def test_digest_auth_returns_no_auth_if_no_digest_header_in_response() -> None:
assert len(response.history) == 0
def test_digest_auth_returns_no_auth_if_alternate_auth_scheme() -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -354,6 +379,7 @@ def test_digest_auth_returns_no_auth_if_alternate_auth_scheme() -> None:
assert len(response.history) == 0
def test_digest_auth_200_response_including_digest_auth_header() -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -368,6 +394,7 @@ def test_digest_auth_200_response_including_digest_auth_header() -> None:
assert len(response.history) == 0
def test_digest_auth_401_response_without_digest_auth_header() -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -394,6 +421,7 @@ def test_digest_auth_401_response_without_digest_auth_header() -> None:
("SHA-512-SESS", 64, 128),
],
)
def test_digest_auth(
algorithm: str, expected_hash_length: int, expected_response_length: int
) -> None:
@ -426,6 +454,7 @@ def test_digest_auth(
assert len(digest_data["cnonce"]) == 16 + 2
def test_digest_auth_no_specified_qop() -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -457,6 +486,7 @@ def test_digest_auth_no_specified_qop() -> None:
@pytest.mark.parametrize("qop", ("auth, auth-int", "auth,auth-int", "unknown,auth"))
def test_digest_auth_qop_including_spaces_and_auth_returns_auth(qop: str) -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -469,6 +499,7 @@ def test_digest_auth_qop_including_spaces_and_auth_returns_auth(qop: str) -> Non
assert len(response.history) == 1
def test_digest_auth_qop_auth_int_not_implemented() -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -479,6 +510,7 @@ def test_digest_auth_qop_auth_int_not_implemented() -> None:
client.get(url, auth=auth)
def test_digest_auth_qop_must_be_auth_or_auth_int() -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -489,6 +521,7 @@ def test_digest_auth_qop_must_be_auth_or_auth_int() -> None:
client.get(url, auth=auth)
def test_digest_auth_incorrect_credentials() -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -501,6 +534,7 @@ def test_digest_auth_incorrect_credentials() -> None:
assert len(response.history) == 1
def test_digest_auth_reuses_challenge() -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -517,6 +551,7 @@ def test_digest_auth_reuses_challenge() -> None:
assert len(response_2.history) == 0
def test_digest_auth_resets_nonce_count_after_401() -> None:
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -563,6 +598,7 @@ def test_digest_auth_resets_nonce_count_after_401() -> None:
'Digest realm="httpx@example.org", qop="auth,au', # malformed fields list
],
)
def test_digest_auth_raises_protocol_error_on_malformed_header(
auth_header: str,
) -> None:
@ -575,6 +611,7 @@ def test_digest_auth_raises_protocol_error_on_malformed_header(
client.get(url, auth=auth)
def test_auth_history() -> None:
"""
Test that intermediate requests sent as part of an authentication flow
@ -609,6 +646,7 @@ class ConsumeBodyTransport(httpx.MockTransport):
return self.handler(request) # type: ignore[return-value]
def test_digest_auth_unavailable_streaming_body():
url = "https://example.org/"
auth = httpx.DigestAuth(username="user", password="password123")
@ -622,6 +660,7 @@ def test_digest_auth_unavailable_streaming_body():
client.post(url, content=streaming_body(), auth=auth)
def test_auth_reads_response_body() -> None:
"""
Test that we can read the response body in an auth flow if `requires_response_body`
@ -638,6 +677,7 @@ def test_auth_reads_response_body() -> None:
assert response.json() == {"auth": '{"auth":"xyz"}'}
def test_auth() -> None:
"""
Test that we can use an auth implementation specific to the async case, to

View File

@ -8,6 +8,7 @@ import pytest
import httpx
def test_get(server):
url = server.url
with httpx.Client(http2=True) as client:
@ -28,12 +29,14 @@ def test_get(server):
pytest.param("http://", id="no-host"),
],
)
def test_get_invalid_url(server, url):
with httpx.Client() as client:
with pytest.raises((httpx.UnsupportedProtocol, httpx.LocalProtocolError)):
client.get(url)
def test_build_request(server):
url = server.url.copy_with(path="/echo_headers")
headers = {"Custom-header": "value"}
@ -48,6 +51,7 @@ def test_build_request(server):
assert response.json()["Custom-header"] == "value"
def test_post(server):
url = server.url
with httpx.Client() as client:
@ -55,6 +59,7 @@ def test_post(server):
assert response.status_code == 200
def test_post_json(server):
url = server.url
with httpx.Client() as client:
@ -62,6 +67,7 @@ def test_post_json(server):
assert response.status_code == 200
def test_stream_response(server):
with httpx.Client() as client:
with client.stream("GET", server.url) as response:
@ -72,6 +78,7 @@ def test_stream_response(server):
assert response.content == b"Hello, world!"
def test_access_content_stream_response(server):
with httpx.Client() as client:
with client.stream("GET", server.url) as response:
@ -82,6 +89,7 @@ def test_access_content_stream_response(server):
response.content # noqa: B018
def test_stream_request(server):
def hello_world() -> typing.Iterator[bytes]:
yield b"Hello, "
@ -92,6 +100,7 @@ def test_stream_request(server):
assert response.status_code == 200
def test_raise_for_status(server):
with httpx.Client() as client:
for status_code in (200, 400, 404, 500, 505):
@ -107,6 +116,7 @@ def test_raise_for_status(server):
assert response.raise_for_status() is response
def test_options(server):
with httpx.Client() as client:
response = client.options(server.url)
@ -114,6 +124,7 @@ def test_options(server):
assert response.text == "Hello, world!"
def test_head(server):
with httpx.Client() as client:
response = client.head(server.url)
@ -121,18 +132,21 @@ def test_head(server):
assert response.text == ""
def test_put(server):
with httpx.Client() as client:
response = client.put(server.url, content=b"Hello, world!")
assert response.status_code == 200
def test_patch(server):
with httpx.Client() as client:
response = client.patch(server.url, content=b"Hello, world!")
assert response.status_code == 200
def test_delete(server):
with httpx.Client() as client:
response = client.delete(server.url)
@ -140,6 +154,7 @@ def test_delete(server):
assert response.text == "Hello, world!"
def test_100_continue(server):
headers = {"Expect": "100-continue"}
content = b"Echo request body"
@ -153,6 +168,7 @@ def test_100_continue(server):
assert response.content == content
def test_context_managed_transport():
class Transport(httpx.BaseTransport):
def __init__(self) -> None:
@ -184,6 +200,7 @@ def test_context_managed_transport():
]
def test_context_managed_transport_and_mount():
class Transport(httpx.BaseTransport):
def __init__(self, name: str) -> None:
@ -207,7 +224,9 @@ def test_context_managed_transport_and_mount():
transport = Transport(name="transport")
mounted = Transport(name="mounted")
with httpx.Client(transport=transport, mounts={"http://www.example.org": mounted}):
with httpx.Client(
transport=transport, mounts={"http://www.example.org": mounted}
):
pass
assert transport.events == [
@ -226,6 +245,7 @@ def hello_world(request):
return httpx.Response(200, text="Hello, world!")
def test_client_closed_state_using_implicit_open():
client = httpx.Client(transport=httpx.MockTransport(hello_world))
@ -246,6 +266,7 @@ def test_client_closed_state_using_implicit_open():
pass # pragma: no cover
def test_client_closed_state_using_with_block():
with httpx.Client(transport=httpx.MockTransport(hello_world)) as client:
assert not client.is_closed
@ -266,6 +287,7 @@ def mounted(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, json=data)
def test_mounted_transport():
transport = httpx.MockTransport(unmounted)
mounts = {"custom://": httpx.MockTransport(mounted)}
@ -280,6 +302,7 @@ def test_mounted_transport():
assert response.json() == {"app": "mounted"}
def test_mock_transport():
def hello_world(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, text="Hello, world!")
@ -292,6 +315,7 @@ def test_mock_transport():
assert response.text == "Hello, world!"
def test_cancellation_during_stream():
"""
If any BaseException is raised during streaming the response, then the
@ -331,6 +355,7 @@ def test_cancellation_during_stream():
assert stream_was_closed
def test_server_extensions(server):
url = server.url
with httpx.Client(http2=True) as client:

View File

@ -15,6 +15,7 @@ def get_and_set_cookies(request: httpx.Request) -> httpx.Response:
raise NotImplementedError() # pragma: no cover
def test_set_cookie() -> None:
"""
Send a request including a cookie.
@ -31,6 +32,7 @@ def test_set_cookie() -> None:
assert response.json() == {"cookies": "example-name=example-value"}
def test_set_per_request_cookie_is_deprecated() -> None:
"""
Sending a request including a per-request cookie is deprecated.
@ -38,7 +40,9 @@ def test_set_per_request_cookie_is_deprecated() -> None:
url = "http://example.org/echo_cookies"
cookies = {"example-name": "example-value"}
with httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) as client:
with httpx.Client(
transport=httpx.MockTransport(get_and_set_cookies)
) as client:
with pytest.warns(DeprecationWarning):
response = client.get(url, cookies=cookies)
@ -46,6 +50,7 @@ def test_set_per_request_cookie_is_deprecated() -> None:
assert response.json() == {"cookies": "example-name=example-value"}
def test_set_cookie_with_cookiejar() -> None:
"""
Send a request including a cookie, using a `CookieJar` instance.
@ -83,6 +88,7 @@ def test_set_cookie_with_cookiejar() -> None:
assert response.json() == {"cookies": "example-name=example-value"}
def test_setting_client_cookies_to_cookiejar() -> None:
"""
Send a request including a cookie, using a `CookieJar` instance.
@ -120,6 +126,7 @@ def test_setting_client_cookies_to_cookiejar() -> None:
assert response.json() == {"cookies": "example-name=example-value"}
def test_set_cookie_with_cookies_model() -> None:
"""
Send a request including a cookie, using a `Cookies` instance.
@ -129,7 +136,9 @@ def test_set_cookie_with_cookies_model() -> None:
cookies = httpx.Cookies()
cookies["example-name"] = "example-value"
with httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) as client:
with httpx.Client(
transport=httpx.MockTransport(get_and_set_cookies)
) as client:
client.cookies = cookies
response = client.get(url)
@ -137,10 +146,13 @@ def test_set_cookie_with_cookies_model() -> None:
assert response.json() == {"cookies": "example-name=example-value"}
def test_get_cookie() -> None:
url = "http://example.org/set_cookie"
with httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) as client:
with httpx.Client(
transport=httpx.MockTransport(get_and_set_cookies)
) as client:
response = client.get(url)
assert response.status_code == 200
@ -148,11 +160,14 @@ def test_get_cookie() -> None:
assert client.cookies["example-name"] == "example-value"
def test_cookie_persistence() -> None:
"""
Ensure that Client instances persist cookies between requests.
"""
with httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) as client:
with httpx.Client(
transport=httpx.MockTransport(get_and_set_cookies)
) as client:
response = client.get("http://example.org/echo_cookies")
assert response.status_code == 200
assert response.json() == {"cookies": None}

View File

@ -1,3 +1,5 @@
import pytest
import httpx
@ -11,6 +13,7 @@ def app(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, headers={"server": "testserver"})
def test_event_hooks():
events = []
@ -46,6 +49,7 @@ def test_event_hooks():
]
def test_event_hooks_raising_exception():
def raise_on_4xx_5xx(response):
response.raise_for_status()
@ -61,6 +65,7 @@ def test_event_hooks_raising_exception():
assert exc.response.is_closed
def test_event_hooks_with_redirect():
"""
A redirect request should trigger additional 'request' and 'response' event hooks.

View File

@ -20,6 +20,7 @@ def echo_repeated_headers_items(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, json=data)
def test_client_header():
"""
Set a header in the Client.
@ -45,6 +46,7 @@ def test_client_header():
}
def test_header_merge():
url = "http://example.org/echo_headers"
client_headers = {"User-Agent": "python-myclient/0.2.1"}
@ -67,6 +69,7 @@ def test_header_merge():
}
def test_header_merge_conflicting_headers():
url = "http://example.org/echo_headers"
client_headers = {"X-Auth-Token": "FooBar"}
@ -89,6 +92,7 @@ def test_header_merge_conflicting_headers():
}
def test_header_update():
url = "http://example.org/echo_headers"
with httpx.Client(transport=httpx.MockTransport(echo_headers)) as client:
@ -122,12 +126,15 @@ def test_header_update():
}
def test_header_repeated_items():
url = "http://example.org/echo_headers"
with httpx.Client(
transport=httpx.MockTransport(echo_repeated_headers_items)
) as client:
response = client.get(url, headers=[("x-header", "1"), ("x-header", "2,3")])
response = client.get(
url, headers=[("x-header", "1"), ("x-header", "2,3")]
)
assert response.status_code == 200
@ -139,12 +146,15 @@ def test_header_repeated_items():
]
def test_header_repeated_multi_items():
url = "http://example.org/echo_headers"
with httpx.Client(
transport=httpx.MockTransport(echo_repeated_headers_multi_items)
) as client:
response = client.get(url, headers=[("x-header", "1"), ("x-header", "2,3")])
response = client.get(
url, headers=[("x-header", "1"), ("x-header", "2,3")]
)
assert response.status_code == 200
@ -153,6 +163,7 @@ def test_header_repeated_multi_items():
assert ["x-header", "2,3"] in echoed_headers
def test_remove_default_header():
"""
Remove a default header from the Client.
@ -175,12 +186,14 @@ def test_remove_default_header():
}
def test_header_does_not_exist():
headers = httpx.Headers({"foo": "bar"})
with pytest.raises(KeyError):
del headers["baz"]
def test_header_with_incorrect_value():
with pytest.raises(
TypeError,
@ -189,6 +202,7 @@ def test_header_with_incorrect_value():
httpx.Headers({"foo": None}) # type: ignore
def test_host_with_auth_and_port_in_url():
"""
The Host header should only include the hostname, or hostname:port
@ -213,6 +227,7 @@ def test_host_with_auth_and_port_in_url():
}
def test_host_with_non_default_port_in_url():
"""
If the URL includes a non-default port, then it should be included in
@ -236,11 +251,13 @@ def test_host_with_non_default_port_in_url():
}
def test_request_auto_headers():
request = httpx.Request("GET", "https://www.example.org/")
assert "host" in request.headers
def test_same_origin():
origin = httpx.URL("https://example.com")
request = httpx.Request("GET", "HTTPS://EXAMPLE.COM:443")
@ -251,6 +268,7 @@ def test_same_origin():
assert headers["Host"] == request.url.netloc.decode("ascii")
def test_not_same_origin():
origin = httpx.URL("https://example.com")
request = httpx.Request("GET", "HTTP://EXAMPLE.COM:80")
@ -261,6 +279,7 @@ def test_not_same_origin():
assert headers["Host"] == origin.netloc.decode("ascii")
def test_is_https_redirect():
url = httpx.URL("https://example.com")
request = httpx.Request(
@ -273,6 +292,7 @@ def test_is_https_redirect():
assert "Authorization" in headers
def test_is_not_https_redirect():
url = httpx.URL("https://www.example.com")
request = httpx.Request(
@ -285,6 +305,7 @@ def test_is_not_https_redirect():
assert "Authorization" not in headers
def test_is_not_https_redirect_if_not_default_ports():
url = httpx.URL("https://example.com:1337")
request = httpx.Request(

View File

@ -1,6 +1,9 @@
import pytest
import httpx
def test_client_base_url():
with httpx.Client() as client:
client.base_url = "https://www.example.org/" # type: ignore
@ -8,6 +11,7 @@ def test_client_base_url():
assert client.base_url == "https://www.example.org/"
def test_client_base_url_without_trailing_slash():
with httpx.Client() as client:
client.base_url = "https://www.example.org/path" # type: ignore
@ -15,6 +19,7 @@ def test_client_base_url_without_trailing_slash():
assert client.base_url == "https://www.example.org/path/"
def test_client_base_url_with_trailing_slash():
client = httpx.Client()
client.base_url = "https://www.example.org/path/" # type: ignore
@ -22,6 +27,7 @@ def test_client_base_url_with_trailing_slash():
assert client.base_url == "https://www.example.org/path/"
def test_client_headers():
with httpx.Client() as client:
client.headers = {"a": "b"} # type: ignore
@ -29,6 +35,7 @@ def test_client_headers():
assert client.headers["A"] == "b"
def test_client_cookies():
with httpx.Client() as client:
client.cookies = {"a": "b"} # type: ignore
@ -38,6 +45,7 @@ def test_client_cookies():
assert mycookies[0].name == "a" and mycookies[0].value == "b"
def test_client_timeout():
expected_timeout = 12.0
with httpx.Client() as client:
@ -50,6 +58,7 @@ def test_client_timeout():
assert client.timeout.pool == expected_timeout
def test_client_event_hooks():
def on_request(request):
pass # pragma: no cover
@ -59,6 +68,7 @@ def test_client_event_hooks():
assert client.event_hooks == {"request": [on_request], "response": []}
def test_client_trust_env():
with httpx.Client() as client:
assert client.trust_env

View File

@ -13,6 +13,7 @@ def url_to_origin(url: str) -> httpcore.URL:
return httpcore.URL(scheme=u.raw_scheme, host=u.raw_host, port=u.port, target="/")
def test_socks_proxy():
url = httpx.URL("http://www.example.com")
@ -80,8 +81,11 @@ PROXY_URL = "http://[::1]"
),
],
)
def test_transport_for_request(url, proxies, expected):
mounts = {key: httpx.HTTPTransport(proxy=value) for key, value in proxies.items()}
mounts = {
key: httpx.HTTPTransport(proxy=value) for key, value in proxies.items()
}
with httpx.Client(mounts=mounts) as client:
transport = client._transport_for_url(httpx.URL(url))
@ -93,6 +97,7 @@ def test_transport_for_request(url, proxies, expected):
assert transport._pool._proxy_url == url_to_origin(expected)
@pytest.mark.network
def test_proxy_close():
try:
@ -103,6 +108,7 @@ def test_proxy_close():
client.close()
def test_unsupported_proxy_scheme():
with pytest.raises(ValueError):
httpx.Client(proxy="ftp://127.0.0.1")
@ -205,6 +211,7 @@ def test_unsupported_proxy_scheme():
),
],
)
def test_proxies_environ(monkeypatch, url, env, expected):
for name, value in env.items():
monkeypatch.setenv(name, value)
@ -229,8 +236,11 @@ def test_proxies_environ(monkeypatch, url, env, expected):
({"all://": "http://127.0.0.1"}, True),
],
)
def test_for_deprecated_proxy_params(proxies, is_valid):
mounts = {key: httpx.HTTPTransport(proxy=value) for key, value in proxies.items()}
mounts = {
key: httpx.HTTPTransport(proxy=value) for key, value in proxies.items()
}
if not is_valid:
with pytest.raises(ValueError):
@ -239,6 +249,7 @@ def test_for_deprecated_proxy_params(proxies, is_valid):
httpx.Client(mounts=mounts)
def test_proxy_with_mounts():
proxy_transport = httpx.HTTPTransport(proxy="http://127.0.0.1")

View File

@ -1,3 +1,5 @@
import pytest
import httpx
@ -5,12 +7,14 @@ def hello_world(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, text="Hello, world")
def test_client_queryparams():
client = httpx.Client(params={"a": "b"})
assert isinstance(client.params, httpx.QueryParams)
assert client.params["a"] == "b"
def test_client_queryparams_string():
with httpx.Client(params="a=b") as client:
assert isinstance(client.params, httpx.QueryParams)
@ -22,6 +26,7 @@ def test_client_queryparams_string():
assert client.params["a"] == "b"
def test_client_queryparams_echo():
url = "http://example.org/echo_queryparams"
client_queryparams = "first=str"

View File

@ -113,6 +113,7 @@ def redirects(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, html="<html><body>Hello, world!</body></html>")
def test_redirect_301():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
response = client.post(
@ -123,6 +124,7 @@ def test_redirect_301():
assert len(response.history) == 1
def test_redirect_302():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
response = client.post(
@ -133,14 +135,18 @@ def test_redirect_302():
assert len(response.history) == 1
def test_redirect_303():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
response = client.get("https://example.org/redirect_303", follow_redirects=True)
response = client.get(
"https://example.org/redirect_303", follow_redirects=True
)
assert response.status_code == httpx.codes.OK
assert response.url == "https://example.org/"
assert len(response.history) == 1
def test_next_request():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
request = client.build_request("POST", "https://example.org/redirect_303")
@ -155,6 +161,7 @@ def test_next_request():
assert response.next_request is None
def test_head_redirect():
"""
Contrary to Requests, redirects remain enabled by default for HEAD requests.
@ -170,6 +177,7 @@ def test_head_redirect():
assert response.text == ""
def test_relative_redirect():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
response = client.get(
@ -180,6 +188,7 @@ def test_relative_redirect():
assert len(response.history) == 1
def test_malformed_redirect():
# https://github.com/encode/httpx/issues/771
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
@ -191,6 +200,7 @@ def test_malformed_redirect():
assert len(response.history) == 1
def test_no_scheme_redirect():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
response = client.get(
@ -201,6 +211,7 @@ def test_no_scheme_redirect():
assert len(response.history) == 1
def test_fragment_redirect():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
response = client.get(
@ -211,6 +222,7 @@ def test_fragment_redirect():
assert len(response.history) == 1
def test_multiple_redirects():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
response = client.get(
@ -229,6 +241,7 @@ def test_multiple_redirects():
assert len(response.history[1].history) == 1
def test_too_many_redirects():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
with pytest.raises(httpx.TooManyRedirects):
@ -237,12 +250,14 @@ def test_too_many_redirects():
)
def test_redirect_loop():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
with pytest.raises(httpx.TooManyRedirects):
client.get("https://example.org/redirect_loop", follow_redirects=True)
def test_cross_domain_redirect_with_auth_header():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
url = "https://example.com/cross_domain"
@ -252,6 +267,7 @@ def test_cross_domain_redirect_with_auth_header():
assert "authorization" not in response.json()["headers"]
def test_cross_domain_https_redirect_with_auth_header():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
url = "http://example.com/cross_domain"
@ -261,6 +277,7 @@ def test_cross_domain_https_redirect_with_auth_header():
assert "authorization" not in response.json()["headers"]
def test_cross_domain_redirect_with_auth():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
url = "https://example.com/cross_domain"
@ -269,6 +286,7 @@ def test_cross_domain_redirect_with_auth():
assert "authorization" not in response.json()["headers"]
def test_same_domain_redirect():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
url = "https://example.org/cross_domain"
@ -278,6 +296,7 @@ def test_same_domain_redirect():
assert response.json()["headers"]["authorization"] == "abc"
def test_same_domain_https_redirect_with_auth_header():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
url = "http://example.org/cross_domain"
@ -287,6 +306,7 @@ def test_same_domain_https_redirect_with_auth_header():
assert response.json()["headers"]["authorization"] == "abc"
def test_body_redirect():
"""
A 308 redirect should preserve the request body.
@ -300,6 +320,7 @@ def test_body_redirect():
assert "content-length" in response.json()["headers"]
def test_no_body_redirect():
"""
A 303 redirect should remove the request body.
@ -313,6 +334,7 @@ def test_no_body_redirect():
assert "content-length" not in response.json()["headers"]
def test_can_stream_if_no_redirect():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
url = "https://example.org/redirect_301"
@ -330,6 +352,7 @@ class ConsumeBodyTransport(httpx.MockTransport):
return self.handler(request) # type: ignore[return-value]
def test_cannot_redirect_streaming_body():
with httpx.Client(transport=ConsumeBodyTransport(redirects)) as client:
url = "https://example.org/redirect_body"
@ -341,6 +364,7 @@ def test_cannot_redirect_streaming_body():
client.post(url, content=streaming_body(), follow_redirects=True)
def test_cross_subdomain_redirect():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
url = "https://example.com/cross_subdomain"
@ -348,6 +372,7 @@ def test_cross_subdomain_redirect():
assert response.url == "https://www.example.org/cross_subdomain"
def cookie_sessions(request: httpx.Request) -> httpx.Response:
if request.url.path == "/":
cookie = request.headers.get("Cookie")
@ -381,6 +406,7 @@ def cookie_sessions(request: httpx.Request) -> httpx.Response:
return httpx.Response(status_code, headers=headers)
def test_redirect_cookie_behavior():
with httpx.Client(
transport=httpx.MockTransport(cookie_sessions), follow_redirects=True
@ -411,6 +437,7 @@ def test_redirect_cookie_behavior():
assert response.text == "Not logged in"
def test_redirect_custom_scheme():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
with pytest.raises(httpx.UnsupportedProtocol) as e:
@ -420,7 +447,10 @@ def test_redirect_custom_scheme():
assert str(e.value) == "Scheme 'market' not supported."
def test_invalid_redirect():
with httpx.Client(transport=httpx.MockTransport(redirects)) as client:
with pytest.raises(httpx.RemoteProtocolError):
client.get("http://example.org/invalid_redirect", follow_redirects=True)
client.get(
"http://example.org/invalid_redirect", follow_redirects=True
)