Index: third_party/google-endpoints/future/types/newbytes.py |
diff --git a/third_party/google-endpoints/future/types/newbytes.py b/third_party/google-endpoints/future/types/newbytes.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..85e6501cba614a9d4c052e9b60b806b4171ee9bd |
--- /dev/null |
+++ b/third_party/google-endpoints/future/types/newbytes.py |
@@ -0,0 +1,431 @@ |
+""" |
+Pure-Python implementation of a Python 3-like bytes object for Python 2. |
+ |
+Why do this? Without it, the Python 2 bytes object is a very, very |
+different beast to the Python 3 bytes object. |
+""" |
+ |
+from collections import Iterable |
+from numbers import Integral |
+import string |
+import copy |
+ |
+from future.utils import istext, isbytes, PY3, with_metaclass |
+from future.types import no, issubset |
+from future.types.newobject import newobject |
+ |
+ |
+_builtin_bytes = bytes |
+ |
+if PY3: |
+ # We'll probably never use newstr on Py3 anyway... |
+ unicode = str |
+ |
+ |
+class BaseNewBytes(type): |
+ def __instancecheck__(cls, instance): |
+ if cls == newbytes: |
+ return isinstance(instance, _builtin_bytes) |
+ else: |
+ return issubclass(instance.__class__, cls) |
+ |
+ |
+def _newchr(x): |
+ if isinstance(x, str): # this happens on pypy |
+ return x.encode('ascii') |
+ else: |
+ return chr(x) |
+ |
+ |
+class newbytes(with_metaclass(BaseNewBytes, _builtin_bytes)): |
+ """ |
+ A backport of the Python 3 bytes object to Py2 |
+ """ |
+ def __new__(cls, *args, **kwargs): |
+ """ |
+ From the Py3 bytes docstring: |
+ |
+ bytes(iterable_of_ints) -> bytes |
+ bytes(string, encoding[, errors]) -> bytes |
+ bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer |
+ bytes(int) -> bytes object of size given by the parameter initialized with null bytes |
+ bytes() -> empty bytes object |
+ |
+ Construct an immutable array of bytes from: |
+ - an iterable yielding integers in range(256) |
+ - a text string encoded using the specified encoding |
+ - any object implementing the buffer API. |
+ - an integer |
+ """ |
+ |
+ encoding = None |
+ errors = None |
+ |
+ if len(args) == 0: |
+ return super(newbytes, cls).__new__(cls) |
+ elif len(args) >= 2: |
+ args = list(args) |
+ if len(args) == 3: |
+ errors = args.pop() |
+ encoding=args.pop() |
+ # Was: elif isinstance(args[0], newbytes): |
+ # We use type() instead of the above because we're redefining |
+ # this to be True for all unicode string subclasses. Warning: |
+ # This may render newstr un-subclassable. |
+ if type(args[0]) == newbytes: |
+ # Special-case: for consistency with Py3.3, we return the same object |
+ # (with the same id) if a newbytes object is passed into the |
+ # newbytes constructor. |
+ return args[0] |
+ elif isinstance(args[0], _builtin_bytes): |
+ value = args[0] |
+ elif isinstance(args[0], unicode): |
+ try: |
+ if 'encoding' in kwargs: |
+ assert encoding is None |
+ encoding = kwargs['encoding'] |
+ if 'errors' in kwargs: |
+ assert errors is None |
+ errors = kwargs['errors'] |
+ except AssertionError: |
+ raise TypeError('Argument given by name and position') |
+ if encoding is None: |
+ raise TypeError('unicode string argument without an encoding') |
+ ### |
+ # Was: value = args[0].encode(**kwargs) |
+ # Python 2.6 string encode() method doesn't take kwargs: |
+ # Use this instead: |
+ newargs = [encoding] |
+ if errors is not None: |
+ newargs.append(errors) |
+ value = args[0].encode(*newargs) |
+ ### |
+ elif isinstance(args[0], Iterable): |
+ if len(args[0]) == 0: |
+ # This could be an empty list or tuple. Return b'' as on Py3. |
+ value = b'' |
+ else: |
+ # Was: elif len(args[0])>0 and isinstance(args[0][0], Integral): |
+ # # It's a list of integers |
+ # But then we can't index into e.g. frozensets. Try to proceed |
+ # anyway. |
+ try: |
+ value = bytearray([_newchr(x) for x in args[0]]) |
+ except: |
+ raise ValueError('bytes must be in range(0, 256)') |
+ elif isinstance(args[0], Integral): |
+ if args[0] < 0: |
+ raise ValueError('negative count') |
+ value = b'\x00' * args[0] |
+ else: |
+ value = args[0] |
+ if type(value) == newbytes: |
+ # Above we use type(...) rather than isinstance(...) because the |
+ # newbytes metaclass overrides __instancecheck__. |
+ # oldbytes(value) gives the wrong thing on Py2: the same |
+ # result as str(value) on Py3, e.g. "b'abc'". (Issue #193). |
+ # So we handle this case separately: |
+ return copy.copy(value) |
+ else: |
+ return super(newbytes, cls).__new__(cls, value) |
+ |
+ def __repr__(self): |
+ return 'b' + super(newbytes, self).__repr__() |
+ |
+ def __str__(self): |
+ return 'b' + "'{0}'".format(super(newbytes, self).__str__()) |
+ |
+ def __getitem__(self, y): |
+ value = super(newbytes, self).__getitem__(y) |
+ if isinstance(y, Integral): |
+ return ord(value) |
+ else: |
+ return newbytes(value) |
+ |
+ def __getslice__(self, *args): |
+ return self.__getitem__(slice(*args)) |
+ |
+ def __contains__(self, key): |
+ if isinstance(key, int): |
+ newbyteskey = newbytes([key]) |
+ # Don't use isinstance() here because we only want to catch |
+ # newbytes, not Python 2 str: |
+ elif type(key) == newbytes: |
+ newbyteskey = key |
+ else: |
+ newbyteskey = newbytes(key) |
+ return issubset(list(newbyteskey), list(self)) |
+ |
+ @no(unicode) |
+ def __add__(self, other): |
+ return newbytes(super(newbytes, self).__add__(other)) |
+ |
+ @no(unicode) |
+ def __radd__(self, left): |
+ return newbytes(left) + self |
+ |
+ @no(unicode) |
+ def __mul__(self, other): |
+ return newbytes(super(newbytes, self).__mul__(other)) |
+ |
+ @no(unicode) |
+ def __rmul__(self, other): |
+ return newbytes(super(newbytes, self).__rmul__(other)) |
+ |
+ def join(self, iterable_of_bytes): |
+ errmsg = 'sequence item {0}: expected bytes, {1} found' |
+ if isbytes(iterable_of_bytes) or istext(iterable_of_bytes): |
+ raise TypeError(errmsg.format(0, type(iterable_of_bytes))) |
+ for i, item in enumerate(iterable_of_bytes): |
+ if istext(item): |
+ raise TypeError(errmsg.format(i, type(item))) |
+ return newbytes(super(newbytes, self).join(iterable_of_bytes)) |
+ |
+ @classmethod |
+ def fromhex(cls, string): |
+ # Only on Py2: |
+ return cls(string.replace(' ', '').decode('hex')) |
+ |
+ @no(unicode) |
+ def find(self, sub, *args): |
+ return super(newbytes, self).find(sub, *args) |
+ |
+ @no(unicode) |
+ def rfind(self, sub, *args): |
+ return super(newbytes, self).rfind(sub, *args) |
+ |
+ @no(unicode, (1, 2)) |
+ def replace(self, old, new, *args): |
+ return newbytes(super(newbytes, self).replace(old, new, *args)) |
+ |
+ def encode(self, *args): |
+ raise AttributeError("encode method has been disabled in newbytes") |
+ |
+ def decode(self, encoding='utf-8', errors='strict'): |
+ """ |
+ Returns a newstr (i.e. unicode subclass) |
+ |
+ Decode B using the codec registered for encoding. Default encoding |
+ is 'utf-8'. errors may be given to set a different error |
+ handling scheme. Default is 'strict' meaning that encoding errors raise |
+ a UnicodeDecodeError. Other possible values are 'ignore' and 'replace' |
+ as well as any other name registered with codecs.register_error that is |
+ able to handle UnicodeDecodeErrors. |
+ """ |
+ # Py2 str.encode() takes encoding and errors as optional parameter, |
+ # not keyword arguments as in Python 3 str. |
+ |
+ from future.types.newstr import newstr |
+ |
+ if errors == 'surrogateescape': |
+ from future.utils.surrogateescape import register_surrogateescape |
+ register_surrogateescape() |
+ |
+ return newstr(super(newbytes, self).decode(encoding, errors)) |
+ |
+ # This is currently broken: |
+ # # We implement surrogateescape error handling here in addition rather |
+ # # than relying on the custom error handler from |
+ # # future.utils.surrogateescape to be registered globally, even though |
+ # # that is fine in the case of decoding. (But not encoding: see the |
+ # # comments in newstr.encode()``.) |
+ # |
+ # if errors == 'surrogateescape': |
+ # # Decode char by char |
+ # mybytes = [] |
+ # for code in self: |
+ # # Code is an int |
+ # if 0x80 <= code <= 0xFF: |
+ # b = 0xDC00 + code |
+ # elif code <= 0x7F: |
+ # b = _unichr(c).decode(encoding=encoding) |
+ # else: |
+ # # # It may be a bad byte |
+ # # FIXME: What to do in this case? See the Py3 docs / tests. |
+ # # # Try swallowing it. |
+ # # continue |
+ # # print("RAISE!") |
+ # raise NotASurrogateError |
+ # mybytes.append(b) |
+ # return newbytes(mybytes) |
+ # return newbytes(super(newstr, self).decode(encoding, errors)) |
+ |
+ @no(unicode) |
+ def startswith(self, prefix, *args): |
+ return super(newbytes, self).startswith(prefix, *args) |
+ |
+ @no(unicode) |
+ def endswith(self, prefix, *args): |
+ return super(newbytes, self).endswith(prefix, *args) |
+ |
+ @no(unicode) |
+ def split(self, sep=None, maxsplit=-1): |
+ # Py2 str.split() takes maxsplit as an optional parameter, not as a |
+ # keyword argument as in Python 3 bytes. |
+ parts = super(newbytes, self).split(sep, maxsplit) |
+ return [newbytes(part) for part in parts] |
+ |
+ def splitlines(self, keepends=False): |
+ """ |
+ B.splitlines([keepends]) -> list of lines |
+ |
+ Return a list of the lines in B, breaking at line boundaries. |
+ Line breaks are not included in the resulting list unless keepends |
+ is given and true. |
+ """ |
+ # Py2 str.splitlines() takes keepends as an optional parameter, |
+ # not as a keyword argument as in Python 3 bytes. |
+ parts = super(newbytes, self).splitlines(keepends) |
+ return [newbytes(part) for part in parts] |
+ |
+ @no(unicode) |
+ def rsplit(self, sep=None, maxsplit=-1): |
+ # Py2 str.rsplit() takes maxsplit as an optional parameter, not as a |
+ # keyword argument as in Python 3 bytes. |
+ parts = super(newbytes, self).rsplit(sep, maxsplit) |
+ return [newbytes(part) for part in parts] |
+ |
+ @no(unicode) |
+ def partition(self, sep): |
+ parts = super(newbytes, self).partition(sep) |
+ return tuple(newbytes(part) for part in parts) |
+ |
+ @no(unicode) |
+ def rpartition(self, sep): |
+ parts = super(newbytes, self).rpartition(sep) |
+ return tuple(newbytes(part) for part in parts) |
+ |
+ @no(unicode, (1,)) |
+ def rindex(self, sub, *args): |
+ ''' |
+ S.rindex(sub [,start [,end]]) -> int |
+ |
+ Like S.rfind() but raise ValueError when the substring is not found. |
+ ''' |
+ pos = self.rfind(sub, *args) |
+ if pos == -1: |
+ raise ValueError('substring not found') |
+ |
+ @no(unicode) |
+ def index(self, sub, *args): |
+ ''' |
+ Returns index of sub in bytes. |
+ Raises ValueError if byte is not in bytes and TypeError if can't |
+ be converted bytes or its length is not 1. |
+ ''' |
+ if isinstance(sub, int): |
+ if len(args) == 0: |
+ start, end = 0, len(self) |
+ elif len(args) == 1: |
+ start = args[0] |
+ elif len(args) == 2: |
+ start, end = args |
+ else: |
+ raise TypeError('takes at most 3 arguments') |
+ return list(self)[start:end].index(sub) |
+ if not isinstance(sub, bytes): |
+ try: |
+ sub = self.__class__(sub) |
+ except (TypeError, ValueError): |
+ raise TypeError("can't convert sub to bytes") |
+ try: |
+ return super(newbytes, self).index(sub, *args) |
+ except ValueError: |
+ raise ValueError('substring not found') |
+ |
+ def __eq__(self, other): |
+ if isinstance(other, (_builtin_bytes, bytearray)): |
+ return super(newbytes, self).__eq__(other) |
+ else: |
+ return False |
+ |
+ def __ne__(self, other): |
+ if isinstance(other, _builtin_bytes): |
+ return super(newbytes, self).__ne__(other) |
+ else: |
+ return True |
+ |
+ unorderable_err = 'unorderable types: bytes() and {0}' |
+ |
+ def __lt__(self, other): |
+ if not isbytes(other): |
+ raise TypeError(self.unorderable_err.format(type(other))) |
+ return super(newbytes, self).__lt__(other) |
+ |
+ def __le__(self, other): |
+ if not isbytes(other): |
+ raise TypeError(self.unorderable_err.format(type(other))) |
+ return super(newbytes, self).__le__(other) |
+ |
+ def __gt__(self, other): |
+ if not isbytes(other): |
+ raise TypeError(self.unorderable_err.format(type(other))) |
+ return super(newbytes, self).__gt__(other) |
+ |
+ def __ge__(self, other): |
+ if not isbytes(other): |
+ raise TypeError(self.unorderable_err.format(type(other))) |
+ return super(newbytes, self).__ge__(other) |
+ |
+ def __native__(self): |
+ # We can't just feed a newbytes object into str(), because |
+ # newbytes.__str__() returns e.g. "b'blah'", consistent with Py3 bytes. |
+ return super(newbytes, self).__str__() |
+ |
+ def __getattribute__(self, name): |
+ """ |
+ A trick to cause the ``hasattr`` builtin-fn to return False for |
+ the 'encode' method on Py2. |
+ """ |
+ if name in ['encode', u'encode']: |
+ raise AttributeError("encode method has been disabled in newbytes") |
+ return super(newbytes, self).__getattribute__(name) |
+ |
+ @no(unicode) |
+ def rstrip(self, bytes_to_strip=None): |
+ """ |
+ Strip trailing bytes contained in the argument. |
+ If the argument is omitted, strip trailing ASCII whitespace. |
+ """ |
+ return newbytes(super(newbytes, self).rstrip(bytes_to_strip)) |
+ |
+ @no(unicode) |
+ def strip(self, bytes_to_strip=None): |
+ """ |
+ Strip leading and trailing bytes contained in the argument. |
+ If the argument is omitted, strip trailing ASCII whitespace. |
+ """ |
+ return newbytes(super(newbytes, self).strip(bytes_to_strip)) |
+ |
+ def lower(self): |
+ """ |
+ b.lower() -> copy of b |
+ |
+ Return a copy of b with all ASCII characters converted to lowercase. |
+ """ |
+ return newbytes(super(newbytes, self).lower()) |
+ |
+ @no(unicode) |
+ def upper(self): |
+ """ |
+ b.upper() -> copy of b |
+ |
+ Return a copy of b with all ASCII characters converted to uppercase. |
+ """ |
+ return newbytes(super(newbytes, self).upper()) |
+ |
+ @classmethod |
+ @no(unicode) |
+ def maketrans(cls, frm, to): |
+ """ |
+ B.maketrans(frm, to) -> translation table |
+ |
+ Return a translation table (a bytes object of length 256) suitable |
+ for use in the bytes or bytearray translate method where each byte |
+ in frm is mapped to the byte at the same position in to. |
+ The bytes objects frm and to must be of the same length. |
+ """ |
+ return newbytes(string.maketrans(frm, to)) |
+ |
+ |
+__all__ = ['newbytes'] |