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 r"""
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']