1""" 2Pure-Python implementation of a Python 2-like str object for Python 3. 3""" 4 5from numbers import Integral 6 7from past.utils import PY2, with_metaclass 8 9if PY2: 10 from collections import Iterable 11else: 12 from collections.abc import Iterable 13 14_builtin_bytes = bytes 15 16 17class BaseOldStr(type): 18 def __instancecheck__(cls, instance): 19 return isinstance(instance, _builtin_bytes) 20 21 22def unescape(s): 23 """ 24 Interprets strings with escape sequences 25 26 Example: 27 >>> s = unescape(r'abc\\def') # i.e. 'abc\\\\def' 28 >>> print(s) 29 'abc\def' 30 >>> s2 = unescape('abc\\ndef') 31 >>> len(s2) 32 8 33 >>> print(s2) 34 abc 35 def 36 """ 37 return s.encode().decode('unicode_escape') 38 39 40class oldstr(with_metaclass(BaseOldStr, _builtin_bytes)): 41 """ 42 A forward port of the Python 2 8-bit string object to Py3 43 """ 44 # Python 2 strings have no __iter__ method: 45 @property 46 def __iter__(self): 47 raise AttributeError 48 49 def __dir__(self): 50 return [thing for thing in dir(_builtin_bytes) if thing != '__iter__'] 51 52 # def __new__(cls, *args, **kwargs): 53 # """ 54 # From the Py3 bytes docstring: 55 56 # bytes(iterable_of_ints) -> bytes 57 # bytes(string, encoding[, errors]) -> bytes 58 # bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer 59 # bytes(int) -> bytes object of size given by the parameter initialized with null bytes 60 # bytes() -> empty bytes object 61 # 62 # Construct an immutable array of bytes from: 63 # - an iterable yielding integers in range(256) 64 # - a text string encoded using the specified encoding 65 # - any object implementing the buffer API. 66 # - an integer 67 # """ 68 # 69 # if len(args) == 0: 70 # return super(newbytes, cls).__new__(cls) 71 # # Was: elif isinstance(args[0], newbytes): 72 # # We use type() instead of the above because we're redefining 73 # # this to be True for all unicode string subclasses. Warning: 74 # # This may render newstr un-subclassable. 75 # elif type(args[0]) == newbytes: 76 # return args[0] 77 # elif isinstance(args[0], _builtin_bytes): 78 # value = args[0] 79 # elif isinstance(args[0], unicode): 80 # if 'encoding' not in kwargs: 81 # raise TypeError('unicode string argument without an encoding') 82 # ### 83 # # Was: value = args[0].encode(**kwargs) 84 # # Python 2.6 string encode() method doesn't take kwargs: 85 # # Use this instead: 86 # newargs = [kwargs['encoding']] 87 # if 'errors' in kwargs: 88 # newargs.append(kwargs['errors']) 89 # value = args[0].encode(*newargs) 90 # ### 91 # elif isinstance(args[0], Iterable): 92 # if len(args[0]) == 0: 93 # # What is this? 94 # raise ValueError('unknown argument type') 95 # elif len(args[0]) > 0 and isinstance(args[0][0], Integral): 96 # # It's a list of integers 97 # value = b''.join([chr(x) for x in args[0]]) 98 # else: 99 # raise ValueError('item cannot be interpreted as an integer') 100 # elif isinstance(args[0], Integral): 101 # if args[0] < 0: 102 # raise ValueError('negative count') 103 # value = b'\x00' * args[0] 104 # else: 105 # value = args[0] 106 # return super(newbytes, cls).__new__(cls, value) 107 108 def __repr__(self): 109 s = super(oldstr, self).__repr__() # e.g. b'abc' on Py3, b'abc' on Py3 110 return s[1:] 111 112 def __str__(self): 113 s = super(oldstr, self).__str__() # e.g. "b'abc'" or "b'abc\\ndef' 114 # TODO: fix this: 115 assert s[:2] == "b'" and s[-1] == "'" 116 return unescape(s[2:-1]) # e.g. 'abc' or 'abc\ndef' 117 118 def __getitem__(self, y): 119 if isinstance(y, Integral): 120 return super(oldstr, self).__getitem__(slice(y, y+1)) 121 else: 122 return super(oldstr, self).__getitem__(y) 123 124 def __getslice__(self, *args): 125 return self.__getitem__(slice(*args)) 126 127 def __contains__(self, key): 128 if isinstance(key, int): 129 return False 130 131 def __native__(self): 132 return bytes(self) 133 134 135__all__ = ['oldstr'] 136