fix unasync
This commit is contained in:
parent
a2fc6825a5
commit
e7668b6ea0
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user