@@ -169,8 +169,10 @@ def __nonzero__(self):
169
169
_SCHEMELESS_PATH_DELIMS = _ALL_DELIMS - _SCHEMELESS_PATH_SAFE
170
170
_FRAGMENT_SAFE = _UNRESERVED_CHARS | _PATH_SAFE | set (u'/?' )
171
171
_FRAGMENT_DELIMS = _ALL_DELIMS - _FRAGMENT_SAFE
172
- _QUERY_SAFE = _UNRESERVED_CHARS | _FRAGMENT_SAFE - set (u'&=+' )
173
- _QUERY_DELIMS = _ALL_DELIMS - _QUERY_SAFE
172
+ _QUERY_VALUE_SAFE = _UNRESERVED_CHARS | _FRAGMENT_SAFE - set (u'&+' )
173
+ _QUERY_VALUE_DELIMS = _ALL_DELIMS - _QUERY_VALUE_SAFE
174
+ _QUERY_KEY_SAFE = _UNRESERVED_CHARS | _QUERY_VALUE_SAFE - set (u'=' )
175
+ _QUERY_KEY_DELIMS = _ALL_DELIMS - _QUERY_KEY_SAFE
174
176
175
177
176
178
def _make_decode_map (delims , allow_percent = False ):
@@ -204,8 +206,10 @@ def _make_quote_map(safe_chars):
204
206
_PATH_PART_QUOTE_MAP = _make_quote_map (_PATH_SAFE )
205
207
_SCHEMELESS_PATH_PART_QUOTE_MAP = _make_quote_map (_SCHEMELESS_PATH_SAFE )
206
208
_PATH_DECODE_MAP = _make_decode_map (_PATH_DELIMS )
207
- _QUERY_PART_QUOTE_MAP = _make_quote_map (_QUERY_SAFE )
208
- _QUERY_DECODE_MAP = _make_decode_map (_QUERY_DELIMS )
209
+ _QUERY_KEY_QUOTE_MAP = _make_quote_map (_QUERY_KEY_SAFE )
210
+ _QUERY_KEY_DECODE_MAP = _make_decode_map (_QUERY_KEY_DELIMS )
211
+ _QUERY_VALUE_QUOTE_MAP = _make_quote_map (_QUERY_VALUE_SAFE )
212
+ _QUERY_VALUE_DECODE_MAP = _make_decode_map (_QUERY_VALUE_DELIMS )
209
213
_FRAGMENT_QUOTE_MAP = _make_quote_map (_FRAGMENT_SAFE )
210
214
_FRAGMENT_DECODE_MAP = _make_decode_map (_FRAGMENT_DELIMS )
211
215
_UNRESERVED_QUOTE_MAP = _make_quote_map (_UNRESERVED_CHARS )
@@ -290,17 +294,28 @@ def _encode_path_parts(text_parts, rooted=False, has_scheme=True,
290
294
return tuple (encoded_parts )
291
295
292
296
293
- def _encode_query_part (text , maximal = True ):
297
+ def _encode_query_key (text , maximal = True ):
294
298
"""
295
299
Percent-encode a single query string key or value.
296
300
"""
297
301
if maximal :
298
302
bytestr = normalize ('NFC' , text ).encode ('utf8' )
299
- return u'' .join ([_QUERY_PART_QUOTE_MAP [b ] for b in bytestr ])
300
- return u'' .join ([_QUERY_PART_QUOTE_MAP [t ] if t in _QUERY_DELIMS else t
303
+ return u'' .join ([_QUERY_KEY_QUOTE_MAP [b ] for b in bytestr ])
304
+ return u'' .join ([_QUERY_KEY_QUOTE_MAP [t ] if t in _QUERY_KEY_DELIMS else t
301
305
for t in text ])
302
306
303
307
308
+ def _encode_query_value (text , maximal = True ):
309
+ """
310
+ Percent-encode a single query string key or value.
311
+ """
312
+ if maximal :
313
+ bytestr = normalize ('NFC' , text ).encode ('utf8' )
314
+ return u'' .join ([_QUERY_VALUE_QUOTE_MAP [b ] for b in bytestr ])
315
+ return u'' .join ([_QUERY_VALUE_QUOTE_MAP [t ]
316
+ if t in _QUERY_VALUE_DELIMS else t for t in text ])
317
+
318
+
304
319
def _encode_fragment_part (text , maximal = True ):
305
320
"""Quote the fragment part of the URL. Fragments don't have
306
321
subdelimiters, so the whole URL fragment can be passed.
@@ -498,10 +513,16 @@ def _decode_path_part(text, normalize_case=False, encode_stray_percents=False):
498
513
_decode_map = _PATH_DECODE_MAP )
499
514
500
515
501
- def _decode_query_part (text , normalize_case = False , encode_stray_percents = False ):
516
+ def _decode_query_key (text , normalize_case = False , encode_stray_percents = False ):
517
+ return _percent_decode (text , normalize_case = normalize_case ,
518
+ encode_stray_percents = encode_stray_percents ,
519
+ _decode_map = _QUERY_KEY_DECODE_MAP )
520
+
521
+
522
+ def _decode_query_value (text , normalize_case = False , encode_stray_percents = False ):
502
523
return _percent_decode (text , normalize_case = normalize_case ,
503
524
encode_stray_percents = encode_stray_percents ,
504
- _decode_map = _QUERY_DECODE_MAP )
525
+ _decode_map = _QUERY_VALUE_DECODE_MAP )
505
526
506
527
507
528
def _decode_fragment_part (text , normalize_case = False , encode_stray_percents = False ):
@@ -1341,9 +1362,9 @@ def to_uri(self):
1341
1362
userinfo = new_userinfo ,
1342
1363
host = new_host ,
1343
1364
path = new_path ,
1344
- query = tuple ([tuple ( _encode_query_part ( x , maximal = True )
1345
- if x is not None else None
1346
- for x in ( k , v ) )
1365
+ query = tuple ([( _encode_query_key ( k , maximal = True ),
1366
+ _encode_query_value ( v , maximal = True )
1367
+ if v is not None else None )
1347
1368
for k , v in self .query ]),
1348
1369
fragment = _encode_fragment_part (self .fragment , maximal = True )
1349
1370
)
@@ -1379,9 +1400,9 @@ def to_iri(self):
1379
1400
host = host_text ,
1380
1401
path = [_decode_path_part (segment )
1381
1402
for segment in self .path ],
1382
- query = [tuple ( _decode_query_part ( x )
1383
- if x is not None else None
1384
- for x in ( k , v ) )
1403
+ query = [( _decode_query_key ( k ),
1404
+ _decode_query_value ( v )
1405
+ if v is not None else None )
1385
1406
for k , v in self .query ],
1386
1407
fragment = _decode_fragment_part (self .fragment ))
1387
1408
@@ -1417,10 +1438,14 @@ def to_text(self, with_password=False):
1417
1438
has_scheme = bool (scheme ),
1418
1439
has_authority = bool (authority ),
1419
1440
maximal = False )
1420
- query_string = u'&' .join (
1421
- u'=' .join ((_encode_query_part (x , maximal = False )
1422
- for x in ([k ] if v is None else [k , v ])))
1423
- for (k , v ) in self .query )
1441
+ query_parts = []
1442
+ for k , v in self .query :
1443
+ if v is None :
1444
+ query_parts .append (_encode_query_key (k , maximal = False ))
1445
+ else :
1446
+ query_parts .append (u'=' .join ((_encode_query_key (k , maximal = False ),
1447
+ _encode_query_value (v , maximal = False ))))
1448
+ query_string = u'&' .join (query_parts )
1424
1449
1425
1450
fragment = self .fragment
1426
1451
0 commit comments