diff --git a/CHANGES/9068.misc.rst b/CHANGES/9068.misc.rst new file mode 100644 index 00000000000..7ce5ec5c839 --- /dev/null +++ b/CHANGES/9068.misc.rst @@ -0,0 +1,3 @@ +Use :meth:`URL.extend_query() ` to extend query params (requires yarl 1.11.0+) -- by :user:`bdraco`. + +If yarl is older than 1.11.0, the previous slower hand rolled version will be used. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 6eaba561b43..e6110cb0134 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -27,7 +27,7 @@ ) from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy -from yarl import URL +from yarl import URL, __version__ as yarl_version from . import hdrs, helpers, http, multipart, payload from .abc import AbstractStreamWriter @@ -90,6 +90,7 @@ _CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") +_YARL_SUPPORTS_EXTEND_QUERY = tuple(map(int, yarl_version.split(".")[:2])) >= (1, 11) def _gen_default_accept_encoding() -> str: @@ -229,10 +230,13 @@ def __init__( # assert session is not None self._session = cast("ClientSession", session) if params: - q = MultiDict(url.query) - url2 = url.with_query(params) - q.extend(url2.query) - url = url.with_query(q) + if _YARL_SUPPORTS_EXTEND_QUERY: + url = url.extend_query(params) + else: + q = MultiDict(url.query) + url2 = url.with_query(params) + q.extend(url2.query) + url = url.with_query(q) self.original_url = url self.url = url.with_fragment(None) self.method = method.upper() diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index c5e9bdf125c..6ee8afe098a 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -33,7 +33,7 @@ from yarl import URL import aiohttp -from aiohttp import Fingerprint, ServerFingerprintMismatch, hdrs, web +from aiohttp import Fingerprint, ServerFingerprintMismatch, client_reqrep, hdrs, web from aiohttp.abc import AbstractResolver, ResolveResult from aiohttp.client_exceptions import ( InvalidURL, @@ -702,7 +702,10 @@ async def handler(request: web.Request) -> web.Response: assert 200 == resp.status -async def test_params_and_query_string(aiohttp_client: AiohttpClient) -> None: +@pytest.mark.parametrize("yarl_supports_extend_query", [True, False]) +async def test_params_and_query_string( + aiohttp_client: AiohttpClient, yarl_supports_extend_query: bool +) -> None: """Test combining params with an existing query_string.""" async def handler(request: web.Request) -> web.Response: @@ -713,13 +716,18 @@ async def handler(request: web.Request) -> web.Response: app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - async with client.get("/?q=abc", params="q=test&d=dog") as resp: - assert resp.status == 200 + # Ensure the old path is tested for old yarl versions + with mock.patch.object( + client_reqrep, "_YARL_SUPPORTS_EXTEND_QUERY", yarl_supports_extend_query + ): + async with client.get("/?q=abc", params="q=test&d=dog") as resp: + assert resp.status == 200 @pytest.mark.parametrize("params", [None, "", {}, MultiDict()]) +@pytest.mark.parametrize("yarl_supports_extend_query", [True, False]) async def test_empty_params_and_query_string( - aiohttp_client: AiohttpClient, params: Any + aiohttp_client: AiohttpClient, params: Any, yarl_supports_extend_query: bool ) -> None: """Test combining empty params with an existing query_string.""" @@ -731,8 +739,12 @@ async def handler(request: web.Request) -> web.Response: app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - async with client.get("/?q=abc", params=params) as resp: - assert resp.status == 200 + # Ensure the old path is tested for old yarl versions + with mock.patch.object( + client_reqrep, "_YARL_SUPPORTS_EXTEND_QUERY", yarl_supports_extend_query + ): + async with client.get("/?q=abc", params=params) as resp: + assert resp.status == 200 async def test_drop_params_on_redirect(aiohttp_client: AiohttpClient) -> None: