From 5a51f4412f1a360cfb0a66e5fea0d54cb36bb861 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 14 Apr 2021 14:16:39 +0100 Subject: [PATCH 1/4] Deprecate per-request cookies --- docs/compatibility.md | 22 +++++++++++++++++++++- httpx/_api.py | 13 +++++++++---- httpx/_client.py | 8 ++++++++ tests/client/test_cookies.py | 35 +++++++++++++++++++++++++++++------ 4 files changed, 67 insertions(+), 11 deletions(-) diff --git a/docs/compatibility.md b/docs/compatibility.md index b71bc38c77..feafe08f54 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -37,6 +37,26 @@ HTTPX uses `utf-8` for encoding `str` request bodies. For example, when using `c For response bodies, assuming the server didn't send an explicit encoding then HTTPX will do its best to figure out an appropriate encoding. Unlike Requests which uses the `chardet` library, HTTPX relies on a plainer fallback strategy (basically attempting UTF-8, or using Windows-1252 as a fallback). This strategy should be robust enough to handle the vast majority of use cases. +## Cookies + +If using a client instance, then cookies should always be set on the client rather than on a per-request basis. + +This usage is supported... + +```python +client = httpx.Client(cookies=...) +client.post(...) +``` + +This usage is deprecated... + +```python +client = httpx.Client() +client.post(..., cookies=...) +``` + +We prefer enforcing a stricter API here because it provides clearer expectations around cookie persistence, particularly when redirects occur. + ## Status Codes In our documentation we prefer the uppercased versions, such as `codes.NOT_FOUND`, but also provide lower-cased versions for API compatibility with `requests`. @@ -147,6 +167,6 @@ while request is not None: `requests` allows event hooks to mutate `Request` and `Response` objects. See [examples](https://requests.readthedocs.io/en/master/user/advanced/#event-hooks) given in the documentation for `requests`. -In HTTPX, event hooks may access properties of requests and responses, but event hook callbacks cannot mutate the original request/response. +In HTTPX, event hooks may access properties of requests and responses, but event hook callbacks cannot mutate the original request/response. If you are looking for more control, consider checking out [Custom Transports](advanced.md#custom-transports). diff --git a/httpx/_api.py b/httpx/_api.py index 8cfaf6dfda..88e441acb2 100644 --- a/httpx/_api.py +++ b/httpx/_api.py @@ -88,7 +88,12 @@ def request( ``` """ with Client( - proxies=proxies, cert=cert, verify=verify, timeout=timeout, trust_env=trust_env + cookies=cookies, + proxies=proxies, + cert=cert, + verify=verify, + timeout=timeout, + trust_env=trust_env, ) as client: return client.request( method=method, @@ -99,7 +104,6 @@ def request( json=json, params=params, headers=headers, - cookies=cookies, auth=auth, allow_redirects=allow_redirects, ) @@ -134,7 +138,9 @@ def stream( [0]: /quickstart#streaming-responses """ - client = Client(proxies=proxies, cert=cert, verify=verify, trust_env=trust_env) + client = Client( + cookies=cookies, proxies=proxies, cert=cert, verify=verify, trust_env=trust_env + ) request = Request( method=method, url=url, @@ -144,7 +150,6 @@ def stream( files=files, json=json, headers=headers, - cookies=cookies, ) return StreamContextManager( client=client, diff --git a/httpx/_client.py b/httpx/_client.py index ce466aa3a4..a5b1c086a6 100644 --- a/httpx/_client.py +++ b/httpx/_client.py @@ -731,6 +731,14 @@ def request( [0]: /advanced/#merging-of-configuration """ + if cookies is not None: + message = ( + "Setting per-request cookies=... is being deprecated, because " + "the expected behaviour on cookie persistence is ambiguous. Set " + "cookies directly on the client instance instead." + ) + warnings.warn(message, DeprecationWarning) + request = self.build_request( method=method, url=url, diff --git a/tests/client/test_cookies.py b/tests/client/test_cookies.py index fe9125fa06..f0c8352593 100644 --- a/tests/client/test_cookies.py +++ b/tests/client/test_cookies.py @@ -1,5 +1,7 @@ from http.cookiejar import Cookie, CookieJar +import pytest + import httpx @@ -20,8 +22,25 @@ def test_set_cookie() -> None: url = "http://example.org/echo_cookies" cookies = {"example-name": "example-value"} + client = httpx.Client( + cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies) + ) + response = client.get(url) + + assert response.status_code == 200 + 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. + """ + url = "http://example.org/echo_cookies" + cookies = {"example-name": "example-value"} + client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) - response = client.get(url, cookies=cookies) + with pytest.warns(DeprecationWarning): + response = client.get(url, cookies=cookies) assert response.status_code == 200 assert response.json() == {"cookies": "example-name=example-value"} @@ -55,8 +74,10 @@ def test_set_cookie_with_cookiejar() -> None: ) cookies.set_cookie(cookie) - client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) - response = client.get(url, cookies=cookies) + client = httpx.Client( + cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies) + ) + response = client.get(url) assert response.status_code == 200 assert response.json() == {"cookies": "example-name=example-value"} @@ -90,8 +111,9 @@ def test_setting_client_cookies_to_cookiejar() -> None: ) cookies.set_cookie(cookie) - client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) - client.cookies = cookies # type: ignore + client = httpx.Client( + cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies) + ) response = client.get(url) assert response.status_code == 200 @@ -108,7 +130,8 @@ def test_set_cookie_with_cookies_model() -> None: cookies["example-name"] = "example-value" client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) - response = client.get(url, cookies=cookies) + client.cookies = cookies + response = client.get(url) assert response.status_code == 200 assert response.json() == {"cookies": "example-name=example-value"} From 85707b958af54393489d0b89246784527db34647 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 16 Apr 2021 11:56:04 +0100 Subject: [PATCH 2/4] Update docs/compatibility.md Co-authored-by: Stephen Brown II --- docs/compatibility.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/compatibility.md b/docs/compatibility.md index feafe08f54..79d616b87c 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -41,14 +41,11 @@ For response bodies, assuming the server didn't send an explicit encoding then H If using a client instance, then cookies should always be set on the client rather than on a per-request basis. -This usage is supported... +This usage is supported: ```python client = httpx.Client(cookies=...) client.post(...) -``` - -This usage is deprecated... ```python client = httpx.Client() From 6e090e1b78fa6d54c17bc2449ad80b3a4212996d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 16 Apr 2021 11:56:14 +0100 Subject: [PATCH 3/4] Update httpx/_client.py Co-authored-by: Stephen Brown II --- httpx/_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httpx/_client.py b/httpx/_client.py index a5b1c086a6..4a5789e0ac 100644 --- a/httpx/_client.py +++ b/httpx/_client.py @@ -733,7 +733,7 @@ def request( """ if cookies is not None: message = ( - "Setting per-request cookies=... is being deprecated, because " + "Setting per-request cookies=<...> is being deprecated, because " "the expected behaviour on cookie persistence is ambiguous. Set " "cookies directly on the client instance instead." ) From 220235c06a641d212be8b2ebc7d7aab9ed0e6586 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 16 Apr 2021 11:57:02 +0100 Subject: [PATCH 4/4] Update compatibility.md --- docs/compatibility.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/compatibility.md b/docs/compatibility.md index 00b8d72c5b..7aed9dc1ed 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -46,6 +46,9 @@ This usage is supported: ```python client = httpx.Client(cookies=...) client.post(...) +``` + +This usage is **not** supported: ```python client = httpx.Client()