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