99
90
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
93
quote_from_bytes = urlparse.quote_from_bytes
94
quote = urlparse.quote
95
unquote_to_bytes = urlparse.unquote_to_bytes
162
96
unquote = urlparse.unquote
165
99
def escape(relpath, safe='/~'):
166
100
"""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
101
return quote(relpath, safe=safe)
411
342
return local_path_to_url(url)
412
343
prefix = url[:path_start]
413
344
path = url[path_start:]
414
if not isinstance(url, text_type):
345
if not isinstance(url, str):
416
347
if c not in _url_safe_characters:
417
348
raise InvalidURL(url, 'URLs can only contain specific'
691
622
# 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)
625
if isinstance(url, str):
711
unicode_path = unquoted.decode('utf-8')
712
628
except UnicodeError as e:
713
629
raise InvalidURL(
714
url, 'Unable to encode the URL as utf-8: %s' % (e,))
630
url, 'URL was not a plain ASCII url: %s' % (e,))
631
return urlparse.unquote(url)
718
634
# These are characters that if escaped, should stay that way
720
636
_no_decode_ords = [ord(c) for c in _no_decode_chars]
721
637
_no_decode_hex = (['%02x' % o for o in _no_decode_ords]
722
638
+ ['%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)]))
639
_hex_display_map = dict(([('%02x' % o, bytes([o])) for o in range(256)]
640
+ [('%02X' % o, bytes([o])) for o in range(256)]))
725
641
# These entries get mapped to themselves
726
642
_hex_display_map.update((hex, b'%' + hex.encode('ascii'))
727
643
for hex in _no_decode_hex)
765
681
escaped_chunks[j] = _hex_display_map[item[:2]]
767
683
# Put back the percent symbol
768
escaped_chunks[j] = b'%' + \
769
(item[:2].encode('utf-8') if PY3 else item[:2])
684
escaped_chunks[j] = b'%' + (item[:2].encode('utf-8'))
770
685
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:])
686
escaped_chunks[j] = chr(int(item[:2], 16)).encode('utf-8')
687
escaped_chunks[j] += (item[2:].encode('utf-8'))
773
688
unescaped = b''.join(escaped_chunks)
775
690
decoded = unescaped.decode('utf-8')