Skip to content

Commit 165f165

Browse files
rennerochaGallaecio
authored andcommitted
Add or replace URL parameters without removing duplicate values in query strings (#126)
1 parent 11d988b commit 165f165

File tree

2 files changed

+31
-6
lines changed

2 files changed

+31
-6
lines changed

tests/test_url.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,12 @@ def test_add_or_replace_parameter(self):
321321
self.assertEqual(add_or_replace_parameter(url, 'pageurl', 'test'),
322322
'http://example.com/?version=1&pageurl=test&param2=value2')
323323

324+
url = 'http://domain/test?arg1=v1&arg2=v2&arg1=v3'
325+
self.assertEqual(add_or_replace_parameter(url, 'arg4', 'v4'),
326+
'http://domain/test?arg1=v1&arg2=v2&arg1=v3&arg4=v4')
327+
self.assertEqual(add_or_replace_parameter(url, 'arg1', 'v3'),
328+
'http://domain/test?arg1=v3&arg2=v2')
329+
324330
def test_add_or_replace_parameters(self):
325331
url = 'http://domain/test'
326332
self.assertEqual(add_or_replace_parameters(url, {'arg': 'v'}),
@@ -330,6 +336,17 @@ def test_add_or_replace_parameters(self):
330336
'http://domain/test?arg1=v1&arg2=v2&arg3=v3&arg4=v4')
331337
self.assertEqual(add_or_replace_parameters(url, {'arg4': 'v4', 'arg3': 'v3new'}),
332338
'http://domain/test?arg1=v1&arg2=v2&arg3=v3new&arg4=v4')
339+
url = 'http://domain/test?arg1=v1&arg2=v2&arg1=v3'
340+
self.assertEqual(add_or_replace_parameters(url, {'arg4': 'v4'}),
341+
'http://domain/test?arg1=v1&arg2=v2&arg1=v3&arg4=v4')
342+
self.assertEqual(add_or_replace_parameters(url, {'arg1': 'v3'}),
343+
'http://domain/test?arg1=v3&arg2=v2')
344+
345+
def test_add_or_replace_parameters_does_not_change_input_param(self):
346+
url = 'http://domain/test?arg=original'
347+
input_param = {'arg': 'value'}
348+
new_url = add_or_replace_parameters(url, input_param) # noqa
349+
self.assertEqual(input_param, {'arg': 'value'})
333350

334351
def test_url_query_cleaner(self):
335352
self.assertEqual('product.html',

w3lib/url.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import posixpath
1010
import warnings
1111
import string
12-
from collections import namedtuple, OrderedDict
12+
from collections import namedtuple
1313
import six
1414
from six.moves.urllib.parse import (urljoin, urlsplit, urlunsplit,
1515
urldefrag, urlencode, urlparse,
@@ -208,13 +208,21 @@ def url_query_cleaner(url, parameterlist=(), sep='&', kvsep='=', remove=False, u
208208
url += '#' + fragment
209209
return url
210210

211-
212211
def _add_or_replace_parameters(url, params):
213212
parsed = urlsplit(url)
214-
args = parse_qsl(parsed.query, keep_blank_values=True)
215-
216-
new_args = OrderedDict(args)
217-
new_args.update(params)
213+
current_args = parse_qsl(parsed.query, keep_blank_values=True)
214+
215+
new_args = []
216+
seen_params = set()
217+
for name, value in current_args:
218+
if name not in params:
219+
new_args.append((name, value))
220+
elif name not in seen_params:
221+
new_args.append((name, params[name]))
222+
seen_params.add(name)
223+
224+
not_modified_args = [(name, value) for name, value in params.items() if name not in seen_params]
225+
new_args += not_modified_args
218226

219227
query = urlencode(new_args)
220228
return urlunsplit(parsed._replace(query=query))

0 commit comments

Comments
 (0)