/usr/lib/python3/dist-packages/yarl/quoting.py is in python3-yarl 1.1.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | import re
from string import ascii_letters, ascii_lowercase, digits
BASCII_LOWERCASE = ascii_lowercase.encode('ascii')
BPCT_ALLOWED = {'%{:02X}'.format(i).encode('ascii') for i in range(256)}
GEN_DELIMS = ":/?#[]@"
SUB_DELIMS_WITHOUT_QS = "!$'()*,"
SUB_DELIMS = SUB_DELIMS_WITHOUT_QS + '+&=;'
RESERVED = GEN_DELIMS + SUB_DELIMS
UNRESERVED = ascii_letters + digits + '-._~'
ALLOWED = UNRESERVED + SUB_DELIMS_WITHOUT_QS
_IS_HEX = re.compile(b'[A-Z0-9][A-Z0-9]')
class _PyQuoter:
def __init__(self, *, safe='', protected='', qs=False):
self._safe = safe
self._protected = protected
self._qs = qs
def __call__(self, val):
if val is None:
return None
if not isinstance(val, str):
raise TypeError("Argument should be str")
if not val:
return ''
val = val.encode('utf8', errors='ignore')
ret = bytearray()
pct = b''
safe = self._safe
safe += ALLOWED
if not self._qs:
safe += '+&=;'
safe += self._protected
bsafe = safe.encode('ascii')
idx = 0
while idx < len(val):
ch = val[idx]
idx += 1
if pct:
if ch in BASCII_LOWERCASE:
ch = ch - 32 # convert to uppercase
pct.append(ch)
if len(pct) == 3: # pragma: no branch # peephole optimizer
pct = bytes(pct)
buf = pct[1:]
if not _IS_HEX.match(buf):
ret.extend(b'%25')
pct = b''
idx -= 2
continue
try:
unquoted = chr(int(pct[1:].decode('ascii'), base=16))
except ValueError:
ret.extend(b'%25')
pct = b''
idx -= 2
continue
if unquoted in self._protected:
ret.extend(pct)
elif unquoted in safe:
ret.append(ord(unquoted))
else:
ret.extend(pct)
pct = b''
# special case, if we have only one char after "%"
elif len(pct) == 2 and idx == len(val):
ret.extend(b'%25')
pct = b''
idx -= 1
continue
elif ch == ord('%'):
pct = bytearray()
pct.append(ch)
# special case if "%" is last char
if idx == len(val):
ret.extend(b'%25')
continue
if self._qs:
if ch == ord(' '):
ret.append(ord('+'))
continue
if ch in bsafe:
ret.append(ch)
continue
ret.extend(('%{:02X}'.format(ch)).encode('ascii'))
return ret.decode('ascii')
class _PyUnquoter:
def __init__(self, *, unsafe='', qs=False):
self._unsafe = unsafe
self._qs = qs
self._quoter = _Quoter()
self._qs_quoter = _Quoter(qs=True)
def __call__(self, val):
if val is None:
return None
if not isinstance(val, str):
raise TypeError("Argument should be str")
if not val:
return ''
pct = ''
last_pct = ''
pcts = bytearray()
ret = []
for ch in val:
if pct:
pct += ch
if len(pct) == 3: # pragma: no branch # peephole optimizer
pcts.append(int(pct[1:], base=16))
last_pct = pct
pct = ''
continue
if pcts:
try:
unquoted = pcts.decode('utf8')
except UnicodeDecodeError:
pass
else:
if self._qs and unquoted in '+=&;':
ret.append(self._qs_quoter(unquoted))
elif unquoted in self._unsafe:
ret.append(self._quoter(unquoted))
else:
ret.append(unquoted)
del pcts[:]
if ch == '%':
pct = ch
continue
if pcts:
ret.append(last_pct) # %F8ab
last_pct = ''
if ch == '+':
if not self._qs or ch in self._unsafe:
ret.append('+')
else:
ret.append(' ')
continue
if ch in self._unsafe:
ret.append('%')
h = hex(ord(ch)).upper()[2:]
for ch in h:
ret.append(ch)
continue
ret.append(ch)
if pcts:
try:
unquoted = pcts.decode('utf8')
except UnicodeDecodeError:
ret.append(last_pct) # %F8
else:
if self._qs and unquoted in '+=&;':
ret.append(self._qs_quoter(unquoted))
elif unquoted in self._unsafe:
ret.append(self._quoter(unquoted))
else:
ret.append(unquoted)
return ''.join(ret)
try:
from ._quoting import _Quoter, _Unquoter
except ImportError: # pragma: no cover
_Quoter = _PyQuoter
_Unquoter = _PyUnquoter
|