99
88
return split(url, exclude_trailing_slash=exclude_trailing_slash)[0]
103
quote_from_bytes = urlparse.quote_from_bytes
104
quote = urlparse.quote
105
unquote_to_bytes = urlparse.unquote_to_bytes
107
# Private copies of quote and unquote, copied from Python's urllib module
108
# because urllib unconditionally imports socket, which imports ssl.
110
always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
111
'abcdefghijklmnopqrstuvwxyz'
114
for i, c in zip(range(256), ''.join(map(chr, range(256)))):
115
_safe_map[c] = c if (
116
i < 128 and c in always_safe) else '%{0:02X}'.format(i)
119
def quote_from_bytes(s, safe='/'):
120
"""quote('abc def') -> 'abc%20def'
122
Each part of a URL, e.g. the path info, the query, etc., has a
123
different set of reserved characters that must be quoted.
125
RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists
126
the following reserved characters.
128
reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
131
Each of these characters is reserved in some component of a URL,
132
but not necessarily in all of them.
134
By default, the quote function is intended for quoting the path
135
section of a URL. Thus, it will not encode '/'. This character
136
is reserved, but in typical usage the quote function is being
137
called on a path where the existing slash characters are used as
143
raise TypeError('None object cannot be quoted')
145
cachekey = (safe, always_safe)
147
(quoter, safe) = _safe_quoters[cachekey]
149
safe_map = _safe_map.copy()
150
safe_map.update([(c, c) for c in safe])
151
quoter = safe_map.__getitem__
152
safe = always_safe + safe
153
_safe_quoters[cachekey] = (quoter, safe)
154
if not s.rstrip(safe):
156
return ''.join(map(quoter, s))
158
quote = quote_from_bytes
159
unquote_to_bytes = urlparse.unquote
91
quote_from_bytes = urlparse.quote_from_bytes
92
quote = urlparse.quote
93
unquote_to_bytes = urlparse.unquote_to_bytes
162
94
unquote = urlparse.unquote
165
97
def escape(relpath, safe='/~'):
166
98
"""Escape relpath to be a valid url."""
167
if not isinstance(relpath, str) and sys.version_info[0] == 2:
168
# GZ 2019-06-16: Should use _fs_enc instead here really?
169
relpath = relpath.encode('utf-8')
170
99
return quote(relpath, safe=safe)
411
340
return local_path_to_url(url)
412
341
prefix = url[:path_start]
413
342
path = url[path_start:]
414
if not isinstance(url, text_type):
343
if not isinstance(url, str):
416
345
if c not in _url_safe_characters:
417
346
raise InvalidURL(url, 'URLs can only contain specific'
691
620
# try to encode the UNICODE => ASCII, and then decode
695
if isinstance(url, text_type):
698
except UnicodeError as e:
700
url, 'URL was not a plain ASCII url: %s' % (e,))
701
return urlparse.unquote(url)
703
if isinstance(url, text_type):
705
url = url.encode("ascii")
706
except UnicodeError as e:
708
url, 'URL was not a plain ASCII url: %s' % (e,))
709
unquoted = unquote(url)
623
if isinstance(url, str):
711
unicode_path = unquoted.decode('utf-8')
712
626
except UnicodeError as e:
713
627
raise InvalidURL(
714
url, 'Unable to encode the URL as utf-8: %s' % (e,))
628
url, 'URL was not a plain ASCII url: %s' % (e,))
629
return urlparse.unquote(url)
718
632
# These are characters that if escaped, should stay that way
720
634
_no_decode_ords = [ord(c) for c in _no_decode_chars]
721
635
_no_decode_hex = (['%02x' % o for o in _no_decode_ords]
722
636
+ ['%02X' % o for o in _no_decode_ords])
723
_hex_display_map = dict(([('%02x' % o, int2byte(o)) for o in range(256)]
724
+ [('%02X' % o, int2byte(o)) for o in range(256)]))
637
_hex_display_map = dict(([('%02x' % o, bytes([o])) for o in range(256)]
638
+ [('%02X' % o, bytes([o])) for o in range(256)]))
725
639
# These entries get mapped to themselves
726
640
_hex_display_map.update((hex, b'%' + hex.encode('ascii'))
727
641
for hex in _no_decode_hex)
765
679
escaped_chunks[j] = _hex_display_map[item[:2]]
767
681
# Put back the percent symbol
768
escaped_chunks[j] = b'%' + \
769
(item[:2].encode('utf-8') if PY3 else item[:2])
682
escaped_chunks[j] = b'%' + (item[:2].encode('utf-8'))
770
683
except UnicodeDecodeError:
771
escaped_chunks[j] = unichr(int(item[:2], 16)).encode('utf-8')
772
escaped_chunks[j] += (item[2:].encode('utf-8') if PY3 else item[2:])
684
escaped_chunks[j] = chr(int(item[:2], 16)).encode('utf-8')
685
escaped_chunks[j] += (item[2:].encode('utf-8'))
773
686
unescaped = b''.join(escaped_chunks)
775
688
decoded = unescaped.decode('utf-8')