def unquote_as_string(s, charset=None): if charset is None: charset = "UTF-8" return str(unquote(s), charset, 'strict') def unquote(s): """unquote('abc%20def') -> 'abc def'.""" res = s.split('%') res[0] = res[0].encode('ASCII', 'strict') for i in range(1, len(res)): res[i] = (bytes.fromhex(res[i][:2]) + res[i][2:].encode('ASCII', 'strict') return b''.join(res) def unquote_plus(s): """unquote('%7e/abc+def') -> '~/abc def'""" s = s.replace('+', ' ') return unquote(s) always_safe = (b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' b'abcdefghijklmnopqrstuvwxyz' b'0123456789' b'_.-') percent_code = ord('%') hextable = b'0123456789ABCDEF' def quote(s, safe = '/', plus=False): """quote(b'abc@def') -> 'abc%40def'""" if isinstance(s, str): s = s.encode("UTF-8", "strict") if not (isinstance(s, bytes) or isinstance(s, bytearray)): raise ValueError("Argument to quote must be either bytes " "or bytearray; string arguments will be " "converted to UTF-8 bytes") safeset = always_safe + safe.encode('ASCII', 'strict') result = bytearray() for i in s: if i not in safeset: result.append(percent_code) result.append(hextable[(i >> 4) & 0xF]) result.append(hextable[i & 0xF]) else: result.append(i) return str(result, 'ASCII', 'strict')