From e906337174579cb4d8bac5c1f44ec5ca280a7c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Fri, 23 Jun 2023 22:11:21 +0700 Subject: [PATCH 01/26] Method to drop query keys --- tests/test_url_query.py | 23 ++++++++++++++++++++++- yarl/_url.py | 22 +++++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/tests/test_url_query.py b/tests/test_url_query.py index bcd2433cb..324e07e31 100644 --- a/tests/test_url_query.py +++ b/tests/test_url_query.py @@ -1,4 +1,4 @@ -from typing import List, Tuple +from typing import List, Tuple, Sequence from urllib.parse import parse_qs, urlencode import pytest @@ -171,3 +171,24 @@ def test_query_from_empty_update_query( if "b" in original_url.query: assert new_url.query["b"] == original_url.query["b"] + + +@pytest.mark.parametrize( + "original_query_string, keys_to_drop, expected_query_string", + [ + ("a=10&b=20", ["a"], "b=20"), + ("a=10&b=20", ["b"], "a=10"), + ("a=10&b=20&c=30", ["b"], "a=10&c=30"), + ("a=10&b=20&c=30", ["invalid_key"], "a=10&b=20&c=30"), + ("a=10&b=20", ["a", "b"], ""), + ("a=10&b=20", [], "a=10&b=20"), + ] +) +def test_drop_query_keys( + original_query_string: str, + keys_to_drop: Sequence[str], + expected_query_string: str + ): + url = URL(f"http://example.com?{original_query_string}") + new_url = url.drop_query_keys(*keys_to_drop) + assert new_url.query_string == expected_query_string diff --git a/yarl/_url.py b/yarl/_url.py index c8f2acb39..4295d7349 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -1,3 +1,5 @@ +from typing import cast + import functools import math import warnings @@ -298,7 +300,7 @@ def __bytes__(self): return str(self).encode("ascii") def __eq__(self, other): - if not type(other) is URL: + if type(other) is not URL: return NotImplemented val1 = self._val @@ -321,22 +323,22 @@ def __hash__(self): return ret def __le__(self, other): - if not type(other) is URL: + if type(other) is not URL: return NotImplemented return self._val <= other._val def __lt__(self, other): - if not type(other) is URL: + if type(other) is not URL: return NotImplemented return self._val < other._val def __ge__(self, other): - if not type(other) is URL: + if type(other) is not URL: return NotImplemented return self._val >= other._val def __gt__(self, other): - if not type(other) is URL: + if type(other) is not URL: return NotImplemented return self._val > other._val @@ -1023,6 +1025,16 @@ def update_query(self, *args, **kwargs): self._val._replace(query=self._get_str_query(query) or ""), encoded=True ) + def drop_query_keys(self, *keys: str) -> "URL": + """Remove some keys from query part and return new URL.""" + valid_keys = set(keys) & set(self.query.keys()) + if not valid_keys: + return self + editable_query = cast(MultiDict, self.query.copy()) + for k in valid_keys: + editable_query.pop(k, None) + return self.with_query(editable_query) + def with_fragment(self, fragment): """Return a new URL with fragment replaced. From f21e9075ce0b3396b7190601d12783974758c235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Sun, 16 Jul 2023 23:17:25 +0700 Subject: [PATCH 02/26] Rename the method to `without_query_keys` --- tests/test_url_query.py | 14 ++++++-------- yarl/_url.py | 5 ++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/test_url_query.py b/tests/test_url_query.py index 324e07e31..67247a99c 100644 --- a/tests/test_url_query.py +++ b/tests/test_url_query.py @@ -1,4 +1,4 @@ -from typing import List, Tuple, Sequence +from typing import List, Sequence, Tuple from urllib.parse import parse_qs, urlencode import pytest @@ -182,13 +182,11 @@ def test_query_from_empty_update_query( ("a=10&b=20&c=30", ["invalid_key"], "a=10&b=20&c=30"), ("a=10&b=20", ["a", "b"], ""), ("a=10&b=20", [], "a=10&b=20"), - ] + ], ) -def test_drop_query_keys( - original_query_string: str, - keys_to_drop: Sequence[str], - expected_query_string: str - ): +def test_without_query_keys( + original_query_string: str, keys_to_drop: Sequence[str], expected_query_string: str +): url = URL(f"http://example.com?{original_query_string}") - new_url = url.drop_query_keys(*keys_to_drop) + new_url = url.without_query_keys(*keys_to_drop) assert new_url.query_string == expected_query_string diff --git a/yarl/_url.py b/yarl/_url.py index 4295d7349..da01962ae 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -1,11 +1,10 @@ -from typing import cast - import functools import math import warnings from collections.abc import Mapping, Sequence from contextlib import suppress from ipaddress import ip_address +from typing import cast from urllib.parse import SplitResult, parse_qsl, quote, urljoin, urlsplit, urlunsplit import idna @@ -1025,7 +1024,7 @@ def update_query(self, *args, **kwargs): self._val._replace(query=self._get_str_query(query) or ""), encoded=True ) - def drop_query_keys(self, *keys: str) -> "URL": + def without_query_keys(self, *keys: str) -> "URL": """Remove some keys from query part and return new URL.""" valid_keys = set(keys) & set(self.query.keys()) if not valid_keys: From 49c3b82c29481f05be3e353f41c8ab40328d747f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Wed, 30 Aug 2023 11:54:06 +0700 Subject: [PATCH 03/26] Add documentation about without_query_keys --- CHANGES/898.feature.rst | 1 + docs/api.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 CHANGES/898.feature.rst diff --git a/CHANGES/898.feature.rst b/CHANGES/898.feature.rst new file mode 100644 index 000000000..cf5974727 --- /dev/null +++ b/CHANGES/898.feature.rst @@ -0,0 +1 @@ +Add `without_query_keys` method. diff --git a/docs/api.rst b/docs/api.rst index b2ddf8421..1f88e96e8 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -726,6 +726,12 @@ section generates a new :class:`URL` instance. Support subclasses of :class:`int` (except :class:`bool`) and :class:`float` as a query parameter value. +.. method:: URL.without_query_keys(*keys) + + Return a new URL whose *query* part does not contain specified keys. + + It does nothing if none of specified keys are present in the query. + .. method:: URL.with_fragment(fragment) Return a new URL with *fragment* replaced, auto-encode *fragment* if needed. From b9bb3aeb946fa4ffc90ef962b5dce14ab2c993b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Fri, 23 Jun 2023 22:11:21 +0700 Subject: [PATCH 04/26] Method to drop query keys --- tests/test_url_query.py | 23 ++++++++++++++++++++++- yarl/_url.py | 22 +++++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/tests/test_url_query.py b/tests/test_url_query.py index bcd2433cb..324e07e31 100644 --- a/tests/test_url_query.py +++ b/tests/test_url_query.py @@ -1,4 +1,4 @@ -from typing import List, Tuple +from typing import List, Tuple, Sequence from urllib.parse import parse_qs, urlencode import pytest @@ -171,3 +171,24 @@ def test_query_from_empty_update_query( if "b" in original_url.query: assert new_url.query["b"] == original_url.query["b"] + + +@pytest.mark.parametrize( + "original_query_string, keys_to_drop, expected_query_string", + [ + ("a=10&b=20", ["a"], "b=20"), + ("a=10&b=20", ["b"], "a=10"), + ("a=10&b=20&c=30", ["b"], "a=10&c=30"), + ("a=10&b=20&c=30", ["invalid_key"], "a=10&b=20&c=30"), + ("a=10&b=20", ["a", "b"], ""), + ("a=10&b=20", [], "a=10&b=20"), + ] +) +def test_drop_query_keys( + original_query_string: str, + keys_to_drop: Sequence[str], + expected_query_string: str + ): + url = URL(f"http://example.com?{original_query_string}") + new_url = url.drop_query_keys(*keys_to_drop) + assert new_url.query_string == expected_query_string diff --git a/yarl/_url.py b/yarl/_url.py index c8f2acb39..4295d7349 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -1,3 +1,5 @@ +from typing import cast + import functools import math import warnings @@ -298,7 +300,7 @@ def __bytes__(self): return str(self).encode("ascii") def __eq__(self, other): - if not type(other) is URL: + if type(other) is not URL: return NotImplemented val1 = self._val @@ -321,22 +323,22 @@ def __hash__(self): return ret def __le__(self, other): - if not type(other) is URL: + if type(other) is not URL: return NotImplemented return self._val <= other._val def __lt__(self, other): - if not type(other) is URL: + if type(other) is not URL: return NotImplemented return self._val < other._val def __ge__(self, other): - if not type(other) is URL: + if type(other) is not URL: return NotImplemented return self._val >= other._val def __gt__(self, other): - if not type(other) is URL: + if type(other) is not URL: return NotImplemented return self._val > other._val @@ -1023,6 +1025,16 @@ def update_query(self, *args, **kwargs): self._val._replace(query=self._get_str_query(query) or ""), encoded=True ) + def drop_query_keys(self, *keys: str) -> "URL": + """Remove some keys from query part and return new URL.""" + valid_keys = set(keys) & set(self.query.keys()) + if not valid_keys: + return self + editable_query = cast(MultiDict, self.query.copy()) + for k in valid_keys: + editable_query.pop(k, None) + return self.with_query(editable_query) + def with_fragment(self, fragment): """Return a new URL with fragment replaced. From 2bee71e6aca4b6b51bf8405d44c5dc75337f61ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Sun, 16 Jul 2023 23:17:25 +0700 Subject: [PATCH 05/26] Rename the method to `without_query_keys` --- tests/test_url_query.py | 14 ++++++-------- yarl/_url.py | 5 ++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/test_url_query.py b/tests/test_url_query.py index 324e07e31..67247a99c 100644 --- a/tests/test_url_query.py +++ b/tests/test_url_query.py @@ -1,4 +1,4 @@ -from typing import List, Tuple, Sequence +from typing import List, Sequence, Tuple from urllib.parse import parse_qs, urlencode import pytest @@ -182,13 +182,11 @@ def test_query_from_empty_update_query( ("a=10&b=20&c=30", ["invalid_key"], "a=10&b=20&c=30"), ("a=10&b=20", ["a", "b"], ""), ("a=10&b=20", [], "a=10&b=20"), - ] + ], ) -def test_drop_query_keys( - original_query_string: str, - keys_to_drop: Sequence[str], - expected_query_string: str - ): +def test_without_query_keys( + original_query_string: str, keys_to_drop: Sequence[str], expected_query_string: str +): url = URL(f"http://example.com?{original_query_string}") - new_url = url.drop_query_keys(*keys_to_drop) + new_url = url.without_query_keys(*keys_to_drop) assert new_url.query_string == expected_query_string diff --git a/yarl/_url.py b/yarl/_url.py index 4295d7349..da01962ae 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -1,11 +1,10 @@ -from typing import cast - import functools import math import warnings from collections.abc import Mapping, Sequence from contextlib import suppress from ipaddress import ip_address +from typing import cast from urllib.parse import SplitResult, parse_qsl, quote, urljoin, urlsplit, urlunsplit import idna @@ -1025,7 +1024,7 @@ def update_query(self, *args, **kwargs): self._val._replace(query=self._get_str_query(query) or ""), encoded=True ) - def drop_query_keys(self, *keys: str) -> "URL": + def without_query_keys(self, *keys: str) -> "URL": """Remove some keys from query part and return new URL.""" valid_keys = set(keys) & set(self.query.keys()) if not valid_keys: From 01091a628359f47cbc41a26741f848fe411ac387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Wed, 30 Aug 2023 11:54:06 +0700 Subject: [PATCH 06/26] Add documentation about without_query_keys --- CHANGES/898.feature.rst | 1 + docs/api.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 CHANGES/898.feature.rst diff --git a/CHANGES/898.feature.rst b/CHANGES/898.feature.rst new file mode 100644 index 000000000..cf5974727 --- /dev/null +++ b/CHANGES/898.feature.rst @@ -0,0 +1 @@ +Add `without_query_keys` method. diff --git a/docs/api.rst b/docs/api.rst index b2ddf8421..1f88e96e8 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -726,6 +726,12 @@ section generates a new :class:`URL` instance. Support subclasses of :class:`int` (except :class:`bool`) and :class:`float` as a query parameter value. +.. method:: URL.without_query_keys(*keys) + + Return a new URL whose *query* part does not contain specified keys. + + It does nothing if none of specified keys are present in the query. + .. method:: URL.with_fragment(fragment) Return a new URL with *fragment* replaced, auto-encode *fragment* if needed. From ebe8b199b5e92bf557027a9c5c00370afb2f643d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Tue, 21 Nov 2023 23:51:32 +0700 Subject: [PATCH 07/26] =?UTF-8?q?=F0=9F=8E=A8=20Rename=20to=20without=5Fqu?= =?UTF-8?q?ery=5Fparams?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES/898.feature.rst | 2 +- docs/api.rst | 2 +- tests/test_url_query.py | 6 +++--- yarl/_url.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGES/898.feature.rst b/CHANGES/898.feature.rst index cf5974727..9c64bb43d 100644 --- a/CHANGES/898.feature.rst +++ b/CHANGES/898.feature.rst @@ -1 +1 @@ -Add `without_query_keys` method. +Added ``without_query_params`` method -- by :user:`hongquan`. diff --git a/docs/api.rst b/docs/api.rst index c4548af12..8caa8575c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -729,7 +729,7 @@ section generates a new :class:`URL` instance. Support subclasses of :class:`int` (except :class:`bool`) and :class:`float` as a query parameter value. -.. method:: URL.without_query_keys(*keys) +.. method:: URL.without_query_params(*keys) Return a new URL whose *query* part does not contain specified keys. diff --git a/tests/test_url_query.py b/tests/test_url_query.py index 67247a99c..1e9b8cccb 100644 --- a/tests/test_url_query.py +++ b/tests/test_url_query.py @@ -174,7 +174,7 @@ def test_query_from_empty_update_query( @pytest.mark.parametrize( - "original_query_string, keys_to_drop, expected_query_string", + ("original_query_string", "keys_to_drop", "expected_query_string"), [ ("a=10&b=20", ["a"], "b=20"), ("a=10&b=20", ["b"], "a=10"), @@ -184,9 +184,9 @@ def test_query_from_empty_update_query( ("a=10&b=20", [], "a=10&b=20"), ], ) -def test_without_query_keys( +def test_without_query_params( original_query_string: str, keys_to_drop: Sequence[str], expected_query_string: str ): url = URL(f"http://example.com?{original_query_string}") - new_url = url.without_query_keys(*keys_to_drop) + new_url = url.without_query_params(*keys_to_drop) assert new_url.query_string == expected_query_string diff --git a/yarl/_url.py b/yarl/_url.py index da01962ae..7a739f8d4 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -1024,7 +1024,7 @@ def update_query(self, *args, **kwargs): self._val._replace(query=self._get_str_query(query) or ""), encoded=True ) - def without_query_keys(self, *keys: str) -> "URL": + def without_query_params(self, *keys: str) -> "URL": """Remove some keys from query part and return new URL.""" valid_keys = set(keys) & set(self.query.keys()) if not valid_keys: From 014612f8d9a4fa8d21d7de14bfccadf48b8d97db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Mon, 4 Dec 2023 09:33:08 +0700 Subject: [PATCH 08/26] Make sure without_query_params always returns new object --- CHANGES/898.feature.rst | 2 +- tests/test_url_query.py | 1 + yarl/_url.py | 10 +++++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGES/898.feature.rst b/CHANGES/898.feature.rst index 9c64bb43d..eb95ee33c 100644 --- a/CHANGES/898.feature.rst +++ b/CHANGES/898.feature.rst @@ -1 +1 @@ -Added ``without_query_params`` method -- by :user:`hongquan`. +Added ``without_query_params`` method, to drop some parameters from query string -- by :user:`hongquan`. diff --git a/tests/test_url_query.py b/tests/test_url_query.py index 1e9b8cccb..23e14dd9d 100644 --- a/tests/test_url_query.py +++ b/tests/test_url_query.py @@ -190,3 +190,4 @@ def test_without_query_params( url = URL(f"http://example.com?{original_query_string}") new_url = url.without_query_params(*keys_to_drop) assert new_url.query_string == expected_query_string + assert new_url is not url diff --git a/yarl/_url.py b/yarl/_url.py index 7a739f8d4..e493cd430 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -1024,13 +1024,13 @@ def update_query(self, *args, **kwargs): self._val._replace(query=self._get_str_query(query) or ""), encoded=True ) - def without_query_params(self, *keys: str) -> "URL": + def without_query_params(self, *query_params: str) -> "URL": """Remove some keys from query part and return new URL.""" - valid_keys = set(keys) & set(self.query.keys()) - if not valid_keys: - return self + params_to_remove = set(query_params) & set(self.query.keys()) + if not params_to_remove: + return URL(self._val._replace(query=self.query_string), encoded=True) editable_query = cast(MultiDict, self.query.copy()) - for k in valid_keys: + for k in params_to_remove: editable_query.pop(k, None) return self.with_query(editable_query) From fb757a5a2993f7d83c2bb516de9867b75e131d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Tue, 5 Dec 2023 10:22:30 +0700 Subject: [PATCH 09/26] Revert irrelevant changes --- yarl/_url.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarl/_url.py b/yarl/_url.py index 82d6dfb8b..84ce109aa 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -301,7 +301,7 @@ def __bytes__(self): return str(self).encode("ascii") def __eq__(self, other): - if type(other) is not URL: + if not type(other) is URL: return NotImplemented val1 = self._val @@ -324,7 +324,7 @@ def __hash__(self): return ret def __le__(self, other): - if type(other) is not URL: + if not type(other) is URL: return NotImplemented return self._val <= other._val From ec6a77961c314b8ec1f4176ba27914375706d490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Tue, 5 Dec 2023 10:41:25 +0700 Subject: [PATCH 10/26] Not to copy original query dict --- yarl/_url.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/yarl/_url.py b/yarl/_url.py index 84ce109aa..33cdb670c 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -4,7 +4,6 @@ from collections.abc import Mapping, Sequence from contextlib import suppress from ipaddress import ip_address -from typing import cast from urllib.parse import SplitResult, parse_qsl, quote, urljoin, urlsplit, urlunsplit import idna @@ -1031,10 +1030,13 @@ def without_query_params(self, *query_params: str) -> "URL": params_to_remove = set(query_params) & set(self.query.keys()) if not params_to_remove: return URL(self._val._replace(query=self.query_string), encoded=True) - editable_query = cast(MultiDict, self.query.copy()) - for k in params_to_remove: - editable_query.pop(k, None) - return self.with_query(editable_query) + return self.with_query( + tuple( + (name, value) + for name, value in self.query.items() + if name not in params_to_remove + ) + ) def with_fragment(self, fragment): """Return a new URL with fragment replaced. From c60444345a3af847eacdaa4e03a562b7c85ca8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Tue, 5 Dec 2023 10:47:45 +0700 Subject: [PATCH 11/26] Revert irrelevant changes --- yarl/_url.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarl/_url.py b/yarl/_url.py index 33cdb670c..5490398be 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -328,17 +328,17 @@ def __le__(self, other): return self._val <= other._val def __lt__(self, other): - if type(other) is not URL: + if not type(other) is URL: return NotImplemented return self._val < other._val def __ge__(self, other): - if type(other) is not URL: + if not type(other) is URL: return NotImplemented return self._val >= other._val def __gt__(self, other): - if type(other) is not URL: + if not type(other) is URL: return NotImplemented return self._val > other._val From a310b8caa6630911693a8e1a44853e5cda8eaa40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Wed, 17 Jan 2024 12:16:49 +0700 Subject: [PATCH 12/26] Add without_query_params to type stubs. --- yarl/__init__.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/yarl/__init__.pyi b/yarl/__init__.pyi index 5fd4bd0d1..5f1d60d44 100644 --- a/yarl/__init__.pyi +++ b/yarl/__init__.pyi @@ -94,6 +94,7 @@ class URL: def update_query(self, query: _Query) -> URL: ... @overload def update_query(self, **kwargs: _QueryVariable) -> URL: ... + def without_query_params(self, *query_params: str) -> URL: ... def with_fragment(self, fragment: Optional[str]) -> URL: ... def with_name(self, name: str) -> URL: ... def with_suffix(self, suffix: str) -> URL: ... From b0e264ff2548ada3cef2c24206ddd842316ab3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Wed, 4 Sep 2024 15:10:16 +0700 Subject: [PATCH 13/26] Adjust test cases to prevent double encoding. --- tests/test_url_query.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_url_query.py b/tests/test_url_query.py index a0c3eec53..35f657397 100644 --- a/tests/test_url_query.py +++ b/tests/test_url_query.py @@ -176,12 +176,12 @@ def test_query_from_empty_update_query( @pytest.mark.parametrize( ("original_query_string", "keys_to_drop", "expected_query_string"), [ - ("a=10&b=20", ["a"], "b=20"), - ("a=10&b=20", ["b"], "a=10"), - ("a=10&b=20&c=30", ["b"], "a=10&c=30"), - ("a=10&b=20&c=30", ["invalid_key"], "a=10&b=20&c=30"), - ("a=10&b=20", ["a", "b"], ""), - ("a=10&b=20", [], "a=10&b=20"), + ("a=10&b=M%C3%B9a+xu%C3%A2n", ["a"], "b=Mùa xuân"), + ("a=10&b=M%C3%B9a+xu%C3%A2n", ["b"], "a=10"), + ("a=10&b=M%C3%B9a+xu%C3%A2n&c=30", ["b"], "a=10&c=30"), + ("a=10&b=M%C3%B9a+xu%C3%A2n&c=30", ["invalid_key"], "a=10&b=Mùa xuân&c=30"), + ("a=10&b=M%C3%B9a+xu%C3%A2n", ["a", "b"], ""), + ("a=10&b=M%C3%B9a+xu%C3%A2n", [], "a=10&b=Mùa xuân"), ], ) def test_without_query_params( From dd08f4c6f9bdfcb4570389d9d7c601f775834f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Thu, 5 Sep 2024 12:45:45 +0700 Subject: [PATCH 14/26] Simplify & operand Co-authored-by: J. Nick Koston --- yarl/_url.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarl/_url.py b/yarl/_url.py index 2fd2d5b81..1e12e9a96 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -1114,7 +1114,7 @@ def update_query(self, *args: Any, **kwargs: Any) -> "URL": def without_query_params(self, *query_params: str) -> "URL": """Remove some keys from query part and return new URL.""" - params_to_remove = set(query_params) & set(self.query.keys()) + params_to_remove = set(query_params) & self.query.keys() if not params_to_remove: return URL(self._val._replace(query=self.query_string), encoded=True) return self.with_query( From 7c986c8b5f5be37bc5bddfde51526ff9588e582f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Fri, 6 Sep 2024 09:56:26 +0700 Subject: [PATCH 15/26] Return original URL when no query params to be dropped. --- tests/test_url_query.py | 24 +++++++++++++++++++++--- yarl/_url.py | 3 +-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/tests/test_url_query.py b/tests/test_url_query.py index 35f657397..d8f31a73e 100644 --- a/tests/test_url_query.py +++ b/tests/test_url_query.py @@ -176,12 +176,15 @@ def test_query_from_empty_update_query( @pytest.mark.parametrize( ("original_query_string", "keys_to_drop", "expected_query_string"), [ - ("a=10&b=M%C3%B9a+xu%C3%A2n", ["a"], "b=Mùa xuân"), + ("a=10&b=M%C3%B9a+xu%C3%A2n&u%E1%BB%91ng=cafe", ["a"], "b=Mùa xuân&uống=cafe"), ("a=10&b=M%C3%B9a+xu%C3%A2n", ["b"], "a=10"), ("a=10&b=M%C3%B9a+xu%C3%A2n&c=30", ["b"], "a=10&c=30"), - ("a=10&b=M%C3%B9a+xu%C3%A2n&c=30", ["invalid_key"], "a=10&b=Mùa xuân&c=30"), + ( + "a=10&b=M%C3%B9a+xu%C3%A2n&u%E1%BB%91ng=cafe", + ["uống"], + "a=10&b=Mùa xuân", + ), ("a=10&b=M%C3%B9a+xu%C3%A2n", ["a", "b"], ""), - ("a=10&b=M%C3%B9a+xu%C3%A2n", [], "a=10&b=Mùa xuân"), ], ) def test_without_query_params( @@ -191,3 +194,18 @@ def test_without_query_params( new_url = url.without_query_params(*keys_to_drop) assert new_url.query_string == expected_query_string assert new_url is not url + + +@pytest.mark.parametrize( + ("original_query_string", "keys_to_drop"), + [ + ("a=10&b=M%C3%B9a+xu%C3%A2n&c=30", ["invalid_key"]), + ("a=10&b=M%C3%B9a+xu%C3%A2n", []), + ], +) +def test_skip_dropping_query_params( + original_query_string: str, keys_to_drop: Sequence[str] +): + url = URL(f"http://example.com?{original_query_string}") + new_url = url.without_query_params(*keys_to_drop) + assert new_url is url diff --git a/yarl/_url.py b/yarl/_url.py index 2b63234c0..44ff3220f 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -63,7 +63,6 @@ class CacheInfo(TypedDict): class _SplitResultDict(TypedDict, total=False): - scheme: str netloc: str path: str @@ -1167,7 +1166,7 @@ def without_query_params(self, *query_params: str) -> "URL": """Remove some keys from query part and return new URL.""" params_to_remove = set(query_params) & self.query.keys() if not params_to_remove: - return URL(self._val._replace(query=self.query_string), encoded=True) + return self return self.with_query( tuple( (name, value) From fe2772ed71792a9f1d9ae1be7d78e3a980e085c1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:24:07 -0500 Subject: [PATCH 16/26] Update yarl/_url.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) --- yarl/_url.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarl/_url.py b/yarl/_url.py index 44ff3220f..077f750ef 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -1162,7 +1162,7 @@ def update_query(self, *args: Any, **kwargs: Any) -> "URL": self._val._replace(query=self._get_str_query(query) or ""), encoded=True ) - def without_query_params(self, *query_params: str) -> "URL": + def without_query_params(self, *query_params: str, /) -> "URL": """Remove some keys from query part and return new URL.""" params_to_remove = set(query_params) & self.query.keys() if not params_to_remove: From 86c34dc4e3d176075a9853208d51384dbd5e66bd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:24:14 -0500 Subject: [PATCH 17/26] Update yarl/_url.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) --- yarl/_url.py | 1 + 1 file changed, 1 insertion(+) diff --git a/yarl/_url.py b/yarl/_url.py index 077f750ef..49c7117e6 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -63,6 +63,7 @@ class CacheInfo(TypedDict): class _SplitResultDict(TypedDict, total=False): + scheme: str netloc: str path: str From 0bc4eefbac7258b9f0e38fd95c43d425d805ae45 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:24:22 -0500 Subject: [PATCH 18/26] Update docs/api.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) --- docs/api.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index bcd2076bb..ccc0ad5d2 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -739,6 +739,8 @@ section generates a new :class:`URL` instance. It does nothing if none of specified keys are present in the query. + .. versionadded:: 1.10.0 + .. method:: URL.with_fragment(fragment) Return a new URL with *fragment* replaced, auto-encode *fragment* if needed. From 6a9099091d3a82d2c84629f160a1dc55b15247d9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:25:06 -0500 Subject: [PATCH 19/26] Apply suggestions from code review --- tests/test_url_query.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_url_query.py b/tests/test_url_query.py index d8f31a73e..6772ef342 100644 --- a/tests/test_url_query.py +++ b/tests/test_url_query.py @@ -189,7 +189,7 @@ def test_query_from_empty_update_query( ) def test_without_query_params( original_query_string: str, keys_to_drop: Sequence[str], expected_query_string: str -): +) -> None: url = URL(f"http://example.com?{original_query_string}") new_url = url.without_query_params(*keys_to_drop) assert new_url.query_string == expected_query_string @@ -205,7 +205,7 @@ def test_without_query_params( ) def test_skip_dropping_query_params( original_query_string: str, keys_to_drop: Sequence[str] -): +) -> None: url = URL(f"http://example.com?{original_query_string}") new_url = url.without_query_params(*keys_to_drop) assert new_url is url From 777946567e270b8803e6ee2cc777becb4d7ec68e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:25:32 -0500 Subject: [PATCH 20/26] Update docs/api.rst --- docs/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index ccc0ad5d2..f5cc6fe92 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -733,7 +733,7 @@ section generates a new :class:`URL` instance. Support subclasses of :class:`int` (except :class:`bool`) and :class:`float` as a query parameter value. -.. method:: URL.without_query_params(*keys) +.. method:: URL.without_query_params(*query_params) Return a new URL whose *query* part does not contain specified keys. From dedd71c1c125325908a8ced89f5dfe6e49481802 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:26:28 -0500 Subject: [PATCH 21/26] Update docs/api.rst --- docs/api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index f5cc6fe92..ac314ba81 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -735,9 +735,9 @@ section generates a new :class:`URL` instance. .. method:: URL.without_query_params(*query_params) - Return a new URL whose *query* part does not contain specified keys. + Return a new URL whose *query* part does not contain specified ``query_params``. - It does nothing if none of specified keys are present in the query. + It does nothing if none of specified ``query_params`` are present in the query. .. versionadded:: 1.10.0 From e6cf2f2ace2dee1fa5b7677e358f60a7436c41eb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:29:39 -0500 Subject: [PATCH 22/26] Update docs/api.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) --- docs/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index ac314ba81..bef74c874 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -733,7 +733,7 @@ section generates a new :class:`URL` instance. Support subclasses of :class:`int` (except :class:`bool`) and :class:`float` as a query parameter value. -.. method:: URL.without_query_params(*query_params) +.. method:: URL.without_query_params(*query_params, /) Return a new URL whose *query* part does not contain specified ``query_params``. From 672d3413a52c429212fabf699a09ab0b740bed5a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:31:05 -0500 Subject: [PATCH 23/26] Update docs/api.rst --- docs/api.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index bef74c874..3d8e689d0 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -737,6 +737,8 @@ section generates a new :class:`URL` instance. Return a new URL whose *query* part does not contain specified ``query_params``. + Accepts :class:`str` for ``query_params``. + It does nothing if none of specified ``query_params`` are present in the query. .. versionadded:: 1.10.0 From f838f5e1c4441a1bedceef268be71840e8178da8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:36:51 -0500 Subject: [PATCH 24/26] Apply suggestions from code review --- docs/api.rst | 2 +- yarl/_url.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 3d8e689d0..05a4db613 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -733,7 +733,7 @@ section generates a new :class:`URL` instance. Support subclasses of :class:`int` (except :class:`bool`) and :class:`float` as a query parameter value. -.. method:: URL.without_query_params(*query_params, /) +.. method:: URL.without_query_params(*query_params) Return a new URL whose *query* part does not contain specified ``query_params``. diff --git a/yarl/_url.py b/yarl/_url.py index 49c7117e6..7418a4b7d 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -1163,7 +1163,7 @@ def update_query(self, *args: Any, **kwargs: Any) -> "URL": self._val._replace(query=self._get_str_query(query) or ""), encoded=True ) - def without_query_params(self, *query_params: str, /) -> "URL": + def without_query_params(self, *query_params: str) -> "URL": """Remove some keys from query part and return new URL.""" params_to_remove = set(query_params) & self.query.keys() if not params_to_remove: From fbcf6f2ef00bbd53074e7992e4762dacf53ac787 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:47:19 -0500 Subject: [PATCH 25/26] Update CHANGES/898.feature.rst --- CHANGES/898.feature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES/898.feature.rst b/CHANGES/898.feature.rst index eb95ee33c..15216245e 100644 --- a/CHANGES/898.feature.rst +++ b/CHANGES/898.feature.rst @@ -1 +1 @@ -Added ``without_query_params`` method, to drop some parameters from query string -- by :user:`hongquan`. +Added :meth:`URL.without_query_params() ` method, to drop some parameters from query string -- by :user:`hongquan`. From f7aabcea4d262ae79ac6cbe9b246f095fff03a35 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 10:49:47 -0500 Subject: [PATCH 26/26] changelog --- CHANGES/1010.feature.rst | 1 + CHANGES/774.feature.rst | 1 + 2 files changed, 2 insertions(+) create mode 120000 CHANGES/1010.feature.rst create mode 120000 CHANGES/774.feature.rst diff --git a/CHANGES/1010.feature.rst b/CHANGES/1010.feature.rst new file mode 120000 index 000000000..f6c34fe97 --- /dev/null +++ b/CHANGES/1010.feature.rst @@ -0,0 +1 @@ +898.feature.rst \ No newline at end of file diff --git a/CHANGES/774.feature.rst b/CHANGES/774.feature.rst new file mode 120000 index 000000000..f6c34fe97 --- /dev/null +++ b/CHANGES/774.feature.rst @@ -0,0 +1 @@ +898.feature.rst \ No newline at end of file