Skip to content

Commit a55dc58

Browse files
committed
Fix URL parameter merge to preserve copy_* method behavior
Updated URL.__init__ to properly handle QueryParams objects vs dict params: - When params is a QueryParams object (from copy_remove_param, etc), use it directly without merging - When params is a dict/list, merge with existing URL query parameters - Updated test expectations to reflect merge behavior instead of replacement This fixes the copy_remove_param test failure while maintaining the intended merge behavior for user-provided params, matching requests library behavior.
1 parent 3ebaa83 commit a55dc58

3 files changed

Lines changed: 31 additions & 26 deletions

File tree

httpx/_urls.py

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -116,29 +116,34 @@ def __init__(self, url: URL | str = "", **kwargs: typing.Any) -> None:
116116
# include an empty trailing "?".
117117
params = kwargs.pop("params")
118118

119-
# Get existing query params from the URL
120-
if isinstance(url, str):
121-
parsed_url = urlparse(url)
122-
existing_params = (
123-
QueryParams(parsed_url.query)
124-
if parsed_url.query
125-
else QueryParams()
126-
)
127-
elif isinstance(url, URL):
128-
existing_params = url.params
119+
# If params is a QueryParams object, use it directly
120+
# (for copy_* methods like copy_remove_param)
121+
if isinstance(params, QueryParams):
122+
kwargs["query"] = str(params) if params else None
129123
else:
130-
existing_params = QueryParams()
131-
132-
# Merge existing and new params
133-
if params:
134-
merged_params = existing_params.merge(params)
135-
kwargs["query"] = str(merged_params) if merged_params else None
136-
elif existing_params:
137-
# params is empty but URL has existing params - keep them
138-
kwargs["query"] = str(existing_params)
139-
else:
140-
# Both empty - no query string
141-
kwargs["query"] = None
124+
# Get existing query params from the URL
125+
if isinstance(url, str):
126+
parsed_url = urlparse(url)
127+
existing_params = (
128+
QueryParams(parsed_url.query)
129+
if parsed_url.query
130+
else QueryParams()
131+
)
132+
elif isinstance(url, URL):
133+
existing_params = url.params
134+
else:
135+
existing_params = QueryParams()
136+
137+
# Merge existing and new params
138+
if params:
139+
merged_params = existing_params.merge(params)
140+
kwargs["query"] = str(merged_params) if merged_params else None
141+
elif existing_params:
142+
# params is empty but URL has existing params - keep them
143+
kwargs["query"] = str(existing_params)
144+
else:
145+
# Both empty - no query string
146+
kwargs["query"] = None
142147

143148
if isinstance(url, str):
144149
self._uri_reference = urlparse(url, **kwargs)

tests/models/test_requests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ def test_request_params():
235235
request = httpx.Request(
236236
"GET", "http://example.com?c=3", params={"a": "1", "b": "2"}
237237
)
238-
assert str(request.url) == "http://example.com?a=1&b=2"
238+
assert str(request.url) == "http://example.com?c=3&a=1&b=2"
239239

240240
request = httpx.Request("GET", "http://example.com?a=1", params={})
241-
assert str(request.url) == "http://example.com"
241+
assert str(request.url) == "http://example.com?a=1"

tests/models/test_url.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ def test_url_params():
159159
url = httpx.URL(
160160
"https://example.org:123/path/to/somewhere?b=456", params={"a": "123"}
161161
)
162-
assert str(url) == "https://example.org:123/path/to/somewhere?a=123"
163-
assert url.params == httpx.QueryParams({"a": "123"})
162+
assert str(url) == "https://example.org:123/path/to/somewhere?b=456&a=123"
163+
assert url.params == httpx.QueryParams({"b": "456", "a": "123"})
164164

165165

166166
# Tests for username and password

0 commit comments

Comments
 (0)