Add future 0.18.2

This commit is contained in:
JonnyWong16 2020-03-23 18:45:35 -07:00
commit fa97d3f88d
210 changed files with 43159 additions and 0 deletions

View file

@ -0,0 +1,257 @@
"""
This module contains backports the data types that were significantly changed
in the transition from Python 2 to Python 3.
- an implementation of Python 3's bytes object (pure Python subclass of
Python 2's builtin 8-bit str type)
- an implementation of Python 3's str object (pure Python subclass of
Python 2's builtin unicode type)
- a backport of the range iterator from Py3 with slicing support
It is used as follows::
from __future__ import division, absolute_import, print_function
from builtins import bytes, dict, int, range, str
to bring in the new semantics for these functions from Python 3. And
then, for example::
b = bytes(b'ABCD')
assert list(b) == [65, 66, 67, 68]
assert repr(b) == "b'ABCD'"
assert [65, 66] in b
# These raise TypeErrors:
# b + u'EFGH'
# b.split(u'B')
# bytes(b',').join([u'Fred', u'Bill'])
s = str(u'ABCD')
# These raise TypeErrors:
# s.join([b'Fred', b'Bill'])
# s.startswith(b'A')
# b'B' in s
# s.find(b'A')
# s.replace(u'A', b'a')
# This raises an AttributeError:
# s.decode('utf-8')
assert repr(s) == 'ABCD' # consistent repr with Py3 (no u prefix)
for i in range(10**11)[:10]:
pass
and::
class VerboseList(list):
def append(self, item):
print('Adding an item')
super().append(item) # new simpler super() function
For more information:
---------------------
- future.types.newbytes
- future.types.newdict
- future.types.newint
- future.types.newobject
- future.types.newrange
- future.types.newstr
Notes
=====
range()
-------
``range`` is a custom class that backports the slicing behaviour from
Python 3 (based on the ``xrange`` module by Dan Crosta). See the
``newrange`` module docstring for more details.
super()
-------
``super()`` is based on Ryan Kelly's ``magicsuper`` module. See the
``newsuper`` module docstring for more details.
round()
-------
Python 3 modifies the behaviour of ``round()`` to use "Banker's Rounding".
See http://stackoverflow.com/a/10825998. See the ``newround`` module
docstring for more details.
"""
from __future__ import absolute_import, division, print_function
import functools
from numbers import Integral
from future import utils
# Some utility functions to enforce strict type-separation of unicode str and
# bytes:
def disallow_types(argnums, disallowed_types):
"""
A decorator that raises a TypeError if any of the given numbered
arguments is of the corresponding given type (e.g. bytes or unicode
string).
For example:
@disallow_types([0, 1], [unicode, bytes])
def f(a, b):
pass
raises a TypeError when f is called if a unicode object is passed as
`a` or a bytes object is passed as `b`.
This also skips over keyword arguments, so
@disallow_types([0, 1], [unicode, bytes])
def g(a, b=None):
pass
doesn't raise an exception if g is called with only one argument a,
e.g.:
g(b'Byte string')
Example use:
>>> class newbytes(object):
... @disallow_types([1], [unicode])
... def __add__(self, other):
... pass
>>> newbytes('1234') + u'1234' #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: can't concat 'bytes' to (unicode) str
"""
def decorator(function):
@functools.wraps(function)
def wrapper(*args, **kwargs):
# These imports are just for this decorator, and are defined here
# to prevent circular imports:
from .newbytes import newbytes
from .newint import newint
from .newstr import newstr
errmsg = "argument can't be {0}"
for (argnum, mytype) in zip(argnums, disallowed_types):
# Handle the case where the type is passed as a string like 'newbytes'.
if isinstance(mytype, str) or isinstance(mytype, bytes):
mytype = locals()[mytype]
# Only restrict kw args only if they are passed:
if len(args) <= argnum:
break
# Here we use type() rather than isinstance() because
# __instancecheck__ is being overridden. E.g.
# isinstance(b'abc', newbytes) is True on Py2.
if type(args[argnum]) == mytype:
raise TypeError(errmsg.format(mytype))
return function(*args, **kwargs)
return wrapper
return decorator
def no(mytype, argnums=(1,)):
"""
A shortcut for the disallow_types decorator that disallows only one type
(in any position in argnums).
Example use:
>>> class newstr(object):
... @no('bytes')
... def __add__(self, other):
... pass
>>> newstr(u'1234') + b'1234' #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: argument can't be bytes
The object can also be passed directly, but passing the string helps
to prevent circular import problems.
"""
if isinstance(argnums, Integral):
argnums = (argnums,)
disallowed_types = [mytype] * len(argnums)
return disallow_types(argnums, disallowed_types)
def issubset(list1, list2):
"""
Examples:
>>> issubset([], [65, 66, 67])
True
>>> issubset([65], [65, 66, 67])
True
>>> issubset([65, 66], [65, 66, 67])
True
>>> issubset([65, 67], [65, 66, 67])
False
"""
n = len(list1)
for startpos in range(len(list2) - n + 1):
if list2[startpos:startpos+n] == list1:
return True
return False
if utils.PY3:
import builtins
bytes = builtins.bytes
dict = builtins.dict
int = builtins.int
list = builtins.list
object = builtins.object
range = builtins.range
str = builtins.str
# The identity mapping
newtypes = {bytes: bytes,
dict: dict,
int: int,
list: list,
object: object,
range: range,
str: str}
__all__ = ['newtypes']
else:
from .newbytes import newbytes
from .newdict import newdict
from .newint import newint
from .newlist import newlist
from .newrange import newrange
from .newobject import newobject
from .newstr import newstr
newtypes = {bytes: newbytes,
dict: newdict,
int: newint,
long: newint,
list: newlist,
object: newobject,
range: newrange,
str: newbytes,
unicode: newstr}
__all__ = ['newbytes', 'newdict', 'newint', 'newlist', 'newrange', 'newstr', 'newtypes']

View file

@ -0,0 +1,460 @@
"""
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 numbers import Integral
import string
import copy
from future.utils import istext, isbytes, PY2, PY3, with_metaclass
from future.types import no, issubset
from future.types.newobject import newobject
if PY2:
from collections import Iterable
else:
from collections.abc import Iterable
_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 hasattr(args[0], '__bytes__'):
value = args[0].__bytes__()
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 __mod__(self, vals):
if isinstance(vals, newbytes):
vals = _builtin_bytes.__str__(vals)
elif isinstance(vals, tuple):
newvals = []
for v in vals:
if isinstance(v, newbytes):
v = _builtin_bytes.__str__(v)
newvals.append(v)
vals = tuple(newvals)
elif (hasattr(vals.__class__, '__getitem__') and
hasattr(vals.__class__, 'iteritems')):
for k, v in vals.iteritems():
if isinstance(v, newbytes):
vals[k] = _builtin_bytes.__str__(v)
return _builtin_bytes.__mod__(self, vals)
def __imod__(self, other):
return self.__mod__(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 isinstance(other, _builtin_bytes):
return super(newbytes, self).__lt__(other)
raise TypeError(self.unorderable_err.format(type(other)))
def __le__(self, other):
if isinstance(other, _builtin_bytes):
return super(newbytes, self).__le__(other)
raise TypeError(self.unorderable_err.format(type(other)))
def __gt__(self, other):
if isinstance(other, _builtin_bytes):
return super(newbytes, self).__gt__(other)
raise TypeError(self.unorderable_err.format(type(other)))
def __ge__(self, other):
if isinstance(other, _builtin_bytes):
return super(newbytes, self).__ge__(other)
raise TypeError(self.unorderable_err.format(type(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']

111
lib/future/types/newdict.py Normal file
View file

@ -0,0 +1,111 @@
"""
A dict subclass for Python 2 that behaves like Python 3's dict
Example use:
>>> from builtins import dict
>>> d1 = dict() # instead of {} for an empty dict
>>> d2 = dict(key1='value1', key2='value2')
The keys, values and items methods now return iterators on Python 2.x
(with set-like behaviour on Python 2.7).
>>> for d in (d1, d2):
... assert not isinstance(d.keys(), list)
... assert not isinstance(d.values(), list)
... assert not isinstance(d.items(), list)
"""
import sys
from future.utils import with_metaclass
from future.types.newobject import newobject
_builtin_dict = dict
ver = sys.version_info[:2]
class BaseNewDict(type):
def __instancecheck__(cls, instance):
if cls == newdict:
return isinstance(instance, _builtin_dict)
else:
return issubclass(instance.__class__, cls)
class newdict(with_metaclass(BaseNewDict, _builtin_dict)):
"""
A backport of the Python 3 dict object to Py2
"""
def items(self):
"""
On Python 2.7+:
D.items() -> a set-like object providing a view on D's items
On Python 2.6:
D.items() -> an iterator over D's items
"""
if ver == (2, 7):
return self.viewitems()
elif ver == (2, 6):
return self.iteritems()
elif ver >= (3, 0):
return self.items()
def keys(self):
"""
On Python 2.7+:
D.keys() -> a set-like object providing a view on D's keys
On Python 2.6:
D.keys() -> an iterator over D's keys
"""
if ver == (2, 7):
return self.viewkeys()
elif ver == (2, 6):
return self.iterkeys()
elif ver >= (3, 0):
return self.keys()
def values(self):
"""
On Python 2.7+:
D.values() -> a set-like object providing a view on D's values
On Python 2.6:
D.values() -> an iterator over D's values
"""
if ver == (2, 7):
return self.viewvalues()
elif ver == (2, 6):
return self.itervalues()
elif ver >= (3, 0):
return self.values()
def __new__(cls, *args, **kwargs):
"""
dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
(key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
d = {}
for k, v in iterable:
d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
in the keyword argument list. For example: dict(one=1, two=2)
"""
if len(args) == 0:
return super(newdict, cls).__new__(cls)
elif type(args[0]) == newdict:
value = args[0]
else:
value = args[0]
return super(newdict, cls).__new__(cls, value)
def __native__(self):
"""
Hook for the future.utils.native() function
"""
return dict(self)
__all__ = ['newdict']

381
lib/future/types/newint.py Normal file
View file

@ -0,0 +1,381 @@
"""
Backport of Python 3's int, based on Py2's long.
They are very similar. The most notable difference is:
- representation: trailing L in Python 2 removed in Python 3
"""
from __future__ import division
import struct
from future.types.newbytes import newbytes
from future.types.newobject import newobject
from future.utils import PY3, isint, istext, isbytes, with_metaclass, native
if PY3:
long = int
from collections.abc import Iterable
else:
from collections import Iterable
class BaseNewInt(type):
def __instancecheck__(cls, instance):
if cls == newint:
# Special case for Py2 short or long int
return isinstance(instance, (int, long))
else:
return issubclass(instance.__class__, cls)
class newint(with_metaclass(BaseNewInt, long)):
"""
A backport of the Python 3 int object to Py2
"""
def __new__(cls, x=0, base=10):
"""
From the Py3 int docstring:
| int(x=0) -> integer
| int(x, base=10) -> integer
|
| Convert a number or string to an integer, or return 0 if no
| arguments are given. If x is a number, return x.__int__(). For
| floating point numbers, this truncates towards zero.
|
| If x is not a number or if base is given, then x must be a string,
| bytes, or bytearray instance representing an integer literal in the
| given base. The literal can be preceded by '+' or '-' and be
| surrounded by whitespace. The base defaults to 10. Valid bases are
| 0 and 2-36. Base 0 means to interpret the base from the string as an
| integer literal.
| >>> int('0b100', base=0)
| 4
"""
try:
val = x.__int__()
except AttributeError:
val = x
else:
if not isint(val):
raise TypeError('__int__ returned non-int ({0})'.format(
type(val)))
if base != 10:
# Explicit base
if not (istext(val) or isbytes(val) or isinstance(val, bytearray)):
raise TypeError(
"int() can't convert non-string with explicit base")
try:
return super(newint, cls).__new__(cls, val, base)
except TypeError:
return super(newint, cls).__new__(cls, newbytes(val), base)
# After here, base is 10
try:
return super(newint, cls).__new__(cls, val)
except TypeError:
# Py2 long doesn't handle bytearray input with an explicit base, so
# handle this here.
# Py3: int(bytearray(b'10'), 2) == 2
# Py2: int(bytearray(b'10'), 2) == 2 raises TypeError
# Py2: long(bytearray(b'10'), 2) == 2 raises TypeError
try:
return super(newint, cls).__new__(cls, newbytes(val))
except:
raise TypeError("newint argument must be a string or a number,"
"not '{0}'".format(type(val)))
def __repr__(self):
"""
Without the L suffix
"""
value = super(newint, self).__repr__()
assert value[-1] == 'L'
return value[:-1]
def __add__(self, other):
value = super(newint, self).__add__(other)
if value is NotImplemented:
return long(self) + other
return newint(value)
def __radd__(self, other):
value = super(newint, self).__radd__(other)
if value is NotImplemented:
return other + long(self)
return newint(value)
def __sub__(self, other):
value = super(newint, self).__sub__(other)
if value is NotImplemented:
return long(self) - other
return newint(value)
def __rsub__(self, other):
value = super(newint, self).__rsub__(other)
if value is NotImplemented:
return other - long(self)
return newint(value)
def __mul__(self, other):
value = super(newint, self).__mul__(other)
if isint(value):
return newint(value)
elif value is NotImplemented:
return long(self) * other
return value
def __rmul__(self, other):
value = super(newint, self).__rmul__(other)
if isint(value):
return newint(value)
elif value is NotImplemented:
return other * long(self)
return value
def __div__(self, other):
# We override this rather than e.g. relying on object.__div__ or
# long.__div__ because we want to wrap the value in a newint()
# call if other is another int
value = long(self) / other
if isinstance(other, (int, long)):
return newint(value)
else:
return value
def __rdiv__(self, other):
value = other / long(self)
if isinstance(other, (int, long)):
return newint(value)
else:
return value
def __idiv__(self, other):
# long has no __idiv__ method. Use __itruediv__ and cast back to
# newint:
value = self.__itruediv__(other)
if isinstance(other, (int, long)):
return newint(value)
else:
return value
def __truediv__(self, other):
value = super(newint, self).__truediv__(other)
if value is NotImplemented:
value = long(self) / other
return value
def __rtruediv__(self, other):
return super(newint, self).__rtruediv__(other)
def __itruediv__(self, other):
# long has no __itruediv__ method
mylong = long(self)
mylong /= other
return mylong
def __floordiv__(self, other):
return newint(super(newint, self).__floordiv__(other))
def __rfloordiv__(self, other):
return newint(super(newint, self).__rfloordiv__(other))
def __ifloordiv__(self, other):
# long has no __ifloordiv__ method
mylong = long(self)
mylong //= other
return newint(mylong)
def __mod__(self, other):
value = super(newint, self).__mod__(other)
if value is NotImplemented:
return long(self) % other
return newint(value)
def __rmod__(self, other):
value = super(newint, self).__rmod__(other)
if value is NotImplemented:
return other % long(self)
return newint(value)
def __divmod__(self, other):
value = super(newint, self).__divmod__(other)
if value is NotImplemented:
mylong = long(self)
return (mylong // other, mylong % other)
return (newint(value[0]), newint(value[1]))
def __rdivmod__(self, other):
value = super(newint, self).__rdivmod__(other)
if value is NotImplemented:
mylong = long(self)
return (other // mylong, other % mylong)
return (newint(value[0]), newint(value[1]))
def __pow__(self, other):
value = super(newint, self).__pow__(other)
if value is NotImplemented:
return long(self) ** other
return newint(value)
def __rpow__(self, other):
value = super(newint, self).__rpow__(other)
if value is NotImplemented:
return other ** long(self)
return newint(value)
def __lshift__(self, other):
if not isint(other):
raise TypeError(
"unsupported operand type(s) for <<: '%s' and '%s'" %
(type(self).__name__, type(other).__name__))
return newint(super(newint, self).__lshift__(other))
def __rshift__(self, other):
if not isint(other):
raise TypeError(
"unsupported operand type(s) for >>: '%s' and '%s'" %
(type(self).__name__, type(other).__name__))
return newint(super(newint, self).__rshift__(other))
def __and__(self, other):
if not isint(other):
raise TypeError(
"unsupported operand type(s) for &: '%s' and '%s'" %
(type(self).__name__, type(other).__name__))
return newint(super(newint, self).__and__(other))
def __or__(self, other):
if not isint(other):
raise TypeError(
"unsupported operand type(s) for |: '%s' and '%s'" %
(type(self).__name__, type(other).__name__))
return newint(super(newint, self).__or__(other))
def __xor__(self, other):
if not isint(other):
raise TypeError(
"unsupported operand type(s) for ^: '%s' and '%s'" %
(type(self).__name__, type(other).__name__))
return newint(super(newint, self).__xor__(other))
def __neg__(self):
return newint(super(newint, self).__neg__())
def __pos__(self):
return newint(super(newint, self).__pos__())
def __abs__(self):
return newint(super(newint, self).__abs__())
def __invert__(self):
return newint(super(newint, self).__invert__())
def __int__(self):
return self
def __nonzero__(self):
return self.__bool__()
def __bool__(self):
"""
So subclasses can override this, Py3-style
"""
return super(newint, self).__nonzero__()
def __native__(self):
return long(self)
def to_bytes(self, length, byteorder='big', signed=False):
"""
Return an array of bytes representing an integer.
The integer is represented using length bytes. An OverflowError is
raised if the integer is not representable with the given number of
bytes.
The byteorder argument determines the byte order used to represent the
integer. If byteorder is 'big', the most significant byte is at the
beginning of the byte array. If byteorder is 'little', the most
significant byte is at the end of the byte array. To request the native
byte order of the host system, use `sys.byteorder' as the byte order value.
The signed keyword-only argument determines whether two's complement is
used to represent the integer. If signed is False and a negative integer
is given, an OverflowError is raised.
"""
if length < 0:
raise ValueError("length argument must be non-negative")
if length == 0 and self == 0:
return newbytes()
if signed and self < 0:
bits = length * 8
num = (2**bits) + self
if num <= 0:
raise OverflowError("int too smal to convert")
else:
if self < 0:
raise OverflowError("can't convert negative int to unsigned")
num = self
if byteorder not in ('little', 'big'):
raise ValueError("byteorder must be either 'little' or 'big'")
h = b'%x' % num
s = newbytes((b'0'*(len(h) % 2) + h).zfill(length*2).decode('hex'))
if signed:
high_set = s[0] & 0x80
if self > 0 and high_set:
raise OverflowError("int too big to convert")
if self < 0 and not high_set:
raise OverflowError("int too small to convert")
if len(s) > length:
raise OverflowError("int too big to convert")
return s if byteorder == 'big' else s[::-1]
@classmethod
def from_bytes(cls, mybytes, byteorder='big', signed=False):
"""
Return the integer represented by the given array of bytes.
The mybytes argument must either support the buffer protocol or be an
iterable object producing bytes. Bytes and bytearray are examples of
built-in objects that support the buffer protocol.
The byteorder argument determines the byte order used to represent the
integer. If byteorder is 'big', the most significant byte is at the
beginning of the byte array. If byteorder is 'little', the most
significant byte is at the end of the byte array. To request the native
byte order of the host system, use `sys.byteorder' as the byte order value.
The signed keyword-only argument indicates whether two's complement is
used to represent the integer.
"""
if byteorder not in ('little', 'big'):
raise ValueError("byteorder must be either 'little' or 'big'")
if isinstance(mybytes, unicode):
raise TypeError("cannot convert unicode objects to bytes")
# mybytes can also be passed as a sequence of integers on Py3.
# Test for this:
elif isinstance(mybytes, Iterable):
mybytes = newbytes(mybytes)
b = mybytes if byteorder == 'big' else mybytes[::-1]
if len(b) == 0:
b = b'\x00'
# The encode() method has been disabled by newbytes, but Py2's
# str has it:
num = int(native(b).encode('hex'), 16)
if signed and (b[0] & 0x80):
num = num - (2 ** (len(b)*8))
return cls(num)
# def _twos_comp(val, bits):
# """compute the 2's compliment of int value val"""
# if( (val&(1<<(bits-1))) != 0 ):
# val = val - (1<<bits)
# return val
__all__ = ['newint']

View file

@ -0,0 +1,95 @@
"""
A list subclass for Python 2 that behaves like Python 3's list.
The primary difference is that lists have a .copy() method in Py3.
Example use:
>>> from builtins import list
>>> l1 = list() # instead of {} for an empty list
>>> l1.append('hello')
>>> l2 = l1.copy()
"""
import sys
import copy
from future.utils import with_metaclass
from future.types.newobject import newobject
_builtin_list = list
ver = sys.version_info[:2]
class BaseNewList(type):
def __instancecheck__(cls, instance):
if cls == newlist:
return isinstance(instance, _builtin_list)
else:
return issubclass(instance.__class__, cls)
class newlist(with_metaclass(BaseNewList, _builtin_list)):
"""
A backport of the Python 3 list object to Py2
"""
def copy(self):
"""
L.copy() -> list -- a shallow copy of L
"""
return copy.copy(self)
def clear(self):
"""L.clear() -> None -- remove all items from L"""
for i in range(len(self)):
self.pop()
def __new__(cls, *args, **kwargs):
"""
list() -> new empty list
list(iterable) -> new list initialized from iterable's items
"""
if len(args) == 0:
return super(newlist, cls).__new__(cls)
elif type(args[0]) == newlist:
value = args[0]
else:
value = args[0]
return super(newlist, cls).__new__(cls, value)
def __add__(self, value):
return newlist(super(newlist, self).__add__(value))
def __radd__(self, left):
" left + self "
try:
return newlist(left) + self
except:
return NotImplemented
def __getitem__(self, y):
"""
x.__getitem__(y) <==> x[y]
Warning: a bug in Python 2.x prevents indexing via a slice from
returning a newlist object.
"""
if isinstance(y, slice):
return newlist(super(newlist, self).__getitem__(y))
else:
return super(newlist, self).__getitem__(y)
def __native__(self):
"""
Hook for the future.utils.native() function
"""
return list(self)
def __nonzero__(self):
return len(self) > 0
__all__ = ['newlist']

View file

@ -0,0 +1,29 @@
"""
A pretty lame implementation of a memoryview object for Python 2.6.
"""
from numbers import Integral
import string
from future.utils import istext, isbytes, PY2, with_metaclass
from future.types import no, issubset
if PY2:
from collections import Iterable
else:
from collections.abc import Iterable
# class BaseNewBytes(type):
# def __instancecheck__(cls, instance):
# return isinstance(instance, _builtin_bytes)
class newmemoryview(object): # with_metaclass(BaseNewBytes, _builtin_bytes)):
"""
A pretty lame backport of the Python 2.7 and Python 3.x
memoryviewview object to Py2.6.
"""
def __init__(self, obj):
return obj
__all__ = ['newmemoryview']

View file

@ -0,0 +1,117 @@
"""
An object subclass for Python 2 that gives new-style classes written in the
style of Python 3 (with ``__next__`` and unicode-returning ``__str__`` methods)
the appropriate Python 2-style ``next`` and ``__unicode__`` methods for compatible.
Example use::
from builtins import object
my_unicode_str = u'Unicode string: \u5b54\u5b50'
class A(object):
def __str__(self):
return my_unicode_str
a = A()
print(str(a))
# On Python 2, these relations hold:
assert unicode(a) == my_unicode_string
assert str(a) == my_unicode_string.encode('utf-8')
Another example::
from builtins import object
class Upper(object):
def __init__(self, iterable):
self._iter = iter(iterable)
def __next__(self): # note the Py3 interface
return next(self._iter).upper()
def __iter__(self):
return self
assert list(Upper('hello')) == list('HELLO')
"""
class newobject(object):
"""
A magical object class that provides Python 2 compatibility methods::
next
__unicode__
__nonzero__
Subclasses of this class can merely define the Python 3 methods (__next__,
__str__, and __bool__).
"""
def next(self):
if hasattr(self, '__next__'):
return type(self).__next__(self)
raise TypeError('newobject is not an iterator')
def __unicode__(self):
# All subclasses of the builtin object should have __str__ defined.
# Note that old-style classes do not have __str__ defined.
if hasattr(self, '__str__'):
s = type(self).__str__(self)
else:
s = str(self)
if isinstance(s, unicode):
return s
else:
return s.decode('utf-8')
def __nonzero__(self):
if hasattr(self, '__bool__'):
return type(self).__bool__(self)
if hasattr(self, '__len__'):
return type(self).__len__(self)
# object has no __nonzero__ method
return True
# Are these ever needed?
# def __div__(self):
# return self.__truediv__()
# def __idiv__(self, other):
# return self.__itruediv__(other)
def __long__(self):
if not hasattr(self, '__int__'):
return NotImplemented
return self.__int__() # not type(self).__int__(self)
# def __new__(cls, *args, **kwargs):
# """
# dict() -> new empty dictionary
# dict(mapping) -> new dictionary initialized from a mapping object's
# (key, value) pairs
# dict(iterable) -> new dictionary initialized as if via:
# d = {}
# for k, v in iterable:
# d[k] = v
# dict(**kwargs) -> new dictionary initialized with the name=value pairs
# in the keyword argument list. For example: dict(one=1, two=2)
# """
# if len(args) == 0:
# return super(newdict, cls).__new__(cls)
# elif type(args[0]) == newdict:
# return args[0]
# else:
# value = args[0]
# return super(newdict, cls).__new__(cls, value)
def __native__(self):
"""
Hook for the future.utils.native() function
"""
return object(self)
__slots__ = []
__all__ = ['newobject']

View file

@ -0,0 +1,32 @@
"""
A substitute for the Python 3 open() function.
Note that io.open() is more complete but maybe slower. Even so, the
completeness may be a better default. TODO: compare these
"""
_builtin_open = open
class newopen(object):
"""Wrapper providing key part of Python 3 open() interface.
From IPython's py3compat.py module. License: BSD.
"""
def __init__(self, fname, mode="r", encoding="utf-8"):
self.f = _builtin_open(fname, mode)
self.enc = encoding
def write(self, s):
return self.f.write(s.encode(self.enc))
def read(self, size=-1):
return self.f.read(size).decode(self.enc)
def close(self):
return self.f.close()
def __enter__(self):
return self
def __exit__(self, etype, value, traceback):
self.f.close()

View file

@ -0,0 +1,170 @@
"""
Nearly identical to xrange.py, by Dan Crosta, from
https://github.com/dcrosta/xrange.git
This is included here in the ``future`` package rather than pointed to as
a dependency because there is no package for ``xrange`` on PyPI. It is
also tweaked to appear like a regular Python 3 ``range`` object rather
than a Python 2 xrange.
From Dan Crosta's README:
"A pure-Python implementation of Python 2.7's xrange built-in, with
some features backported from the Python 3.x range built-in (which
replaced xrange) in that version."
Read more at
https://late.am/post/2012/06/18/what-the-heck-is-an-xrange
"""
from __future__ import absolute_import
from future.utils import PY2
if PY2:
from collections import Sequence, Iterator
else:
from collections.abc import Sequence, Iterator
from itertools import islice
from future.backports.misc import count # with step parameter on Py2.6
# For backward compatibility with python-future versions < 0.14.4:
_count = count
class newrange(Sequence):
"""
Pure-Python backport of Python 3's range object. See `the CPython
documentation for details:
<http://docs.python.org/py3k/library/functions.html#range>`_
"""
def __init__(self, *args):
if len(args) == 1:
start, stop, step = 0, args[0], 1
elif len(args) == 2:
start, stop, step = args[0], args[1], 1
elif len(args) == 3:
start, stop, step = args
else:
raise TypeError('range() requires 1-3 int arguments')
try:
start, stop, step = int(start), int(stop), int(step)
except ValueError:
raise TypeError('an integer is required')
if step == 0:
raise ValueError('range() arg 3 must not be zero')
elif step < 0:
stop = min(stop, start)
else:
stop = max(stop, start)
self._start = start
self._stop = stop
self._step = step
self._len = (stop - start) // step + bool((stop - start) % step)
@property
def start(self):
return self._start
@property
def stop(self):
return self._stop
@property
def step(self):
return self._step
def __repr__(self):
if self._step == 1:
return 'range(%d, %d)' % (self._start, self._stop)
return 'range(%d, %d, %d)' % (self._start, self._stop, self._step)
def __eq__(self, other):
return (isinstance(other, newrange) and
(self._len == 0 == other._len or
(self._start, self._step, self._len) ==
(other._start, other._step, self._len)))
def __len__(self):
return self._len
def index(self, value):
"""Return the 0-based position of integer `value` in
the sequence this range represents."""
try:
diff = value - self._start
except TypeError:
raise ValueError('%r is not in range' % value)
quotient, remainder = divmod(diff, self._step)
if remainder == 0 and 0 <= quotient < self._len:
return abs(quotient)
raise ValueError('%r is not in range' % value)
def count(self, value):
"""Return the number of ocurrences of integer `value`
in the sequence this range represents."""
# a value can occur exactly zero or one times
return int(value in self)
def __contains__(self, value):
"""Return ``True`` if the integer `value` occurs in
the sequence this range represents."""
try:
self.index(value)
return True
except ValueError:
return False
def __reversed__(self):
return iter(self[::-1])
def __getitem__(self, index):
"""Return the element at position ``index`` in the sequence
this range represents, or raise :class:`IndexError` if the
position is out of range."""
if isinstance(index, slice):
return self.__getitem_slice(index)
if index < 0:
# negative indexes access from the end
index = self._len + index
if index < 0 or index >= self._len:
raise IndexError('range object index out of range')
return self._start + index * self._step
def __getitem_slice(self, slce):
"""Return a range which represents the requested slce
of the sequence represented by this range.
"""
scaled_indices = (self._step * n for n in slce.indices(self._len))
start_offset, stop_offset, new_step = scaled_indices
return newrange(self._start + start_offset,
self._start + stop_offset,
new_step)
def __iter__(self):
"""Return an iterator which enumerates the elements of the
sequence this range represents."""
return range_iterator(self)
class range_iterator(Iterator):
"""An iterator for a :class:`range`.
"""
def __init__(self, range_):
self._stepper = islice(count(range_.start, range_.step), len(range_))
def __iter__(self):
return self
def __next__(self):
return next(self._stepper)
def next(self):
return next(self._stepper)
__all__ = ['newrange']

426
lib/future/types/newstr.py Normal file
View file

@ -0,0 +1,426 @@
"""
This module redefines ``str`` on Python 2.x to be a subclass of the Py2
``unicode`` type that behaves like the Python 3.x ``str``.
The main differences between ``newstr`` and Python 2.x's ``unicode`` type are
the stricter type-checking and absence of a `u''` prefix in the representation.
It is designed to be used together with the ``unicode_literals`` import
as follows:
>>> from __future__ import unicode_literals
>>> from builtins import str, isinstance
On Python 3.x and normally on Python 2.x, these expressions hold
>>> str('blah') is 'blah'
True
>>> isinstance('blah', str)
True
However, on Python 2.x, with this import:
>>> from __future__ import unicode_literals
the same expressions are False:
>>> str('blah') is 'blah'
False
>>> isinstance('blah', str)
False
This module is designed to be imported together with ``unicode_literals`` on
Python 2 to bring the meaning of ``str`` back into alignment with unprefixed
string literals (i.e. ``unicode`` subclasses).
Note that ``str()`` (and ``print()``) would then normally call the
``__unicode__`` method on objects in Python 2. To define string
representations of your objects portably across Py3 and Py2, use the
:func:`python_2_unicode_compatible` decorator in :mod:`future.utils`.
"""
from numbers import Number
from future.utils import PY3, istext, with_metaclass, isnewbytes
from future.types import no, issubset
from future.types.newobject import newobject
if PY3:
# We'll probably never use newstr on Py3 anyway...
unicode = str
from collections.abc import Iterable
else:
from collections import Iterable
class BaseNewStr(type):
def __instancecheck__(cls, instance):
if cls == newstr:
return isinstance(instance, unicode)
else:
return issubclass(instance.__class__, cls)
class newstr(with_metaclass(BaseNewStr, unicode)):
"""
A backport of the Python 3 str object to Py2
"""
no_convert_msg = "Can't convert '{0}' object to str implicitly"
def __new__(cls, *args, **kwargs):
"""
From the Py3 str docstring:
str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or
errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
encoding defaults to sys.getdefaultencoding().
errors defaults to 'strict'.
"""
if len(args) == 0:
return super(newstr, cls).__new__(cls)
# Special case: If someone requests str(str(u'abc')), return the same
# object (same id) for consistency with Py3.3. This is not true for
# other objects like list or dict.
elif type(args[0]) == newstr and cls == newstr:
return args[0]
elif isinstance(args[0], unicode):
value = args[0]
elif isinstance(args[0], bytes): # i.e. Py2 bytes or newbytes
if 'encoding' in kwargs or len(args) > 1:
value = args[0].decode(*args[1:], **kwargs)
else:
value = args[0].__str__()
else:
value = args[0]
return super(newstr, cls).__new__(cls, value)
def __repr__(self):
"""
Without the u prefix
"""
value = super(newstr, self).__repr__()
# assert value[0] == u'u'
return value[1:]
def __getitem__(self, y):
"""
Warning: Python <= 2.7.6 has a bug that causes this method never to be called
when y is a slice object. Therefore the type of newstr()[:2] is wrong
(unicode instead of newstr).
"""
return newstr(super(newstr, self).__getitem__(y))
def __contains__(self, key):
errmsg = "'in <string>' requires string as left operand, not {0}"
# Don't use isinstance() here because we only want to catch
# newstr, not Python 2 unicode:
if type(key) == newstr:
newkey = key
elif isinstance(key, unicode) or isinstance(key, bytes) and not isnewbytes(key):
newkey = newstr(key)
else:
raise TypeError(errmsg.format(type(key)))
return issubset(list(newkey), list(self))
@no('newbytes')
def __add__(self, other):
return newstr(super(newstr, self).__add__(other))
@no('newbytes')
def __radd__(self, left):
" left + self "
try:
return newstr(left) + self
except:
return NotImplemented
def __mul__(self, other):
return newstr(super(newstr, self).__mul__(other))
def __rmul__(self, other):
return newstr(super(newstr, self).__rmul__(other))
def join(self, iterable):
errmsg = 'sequence item {0}: expected unicode string, found bytes'
for i, item in enumerate(iterable):
# Here we use type() rather than isinstance() because
# __instancecheck__ is being overridden. E.g.
# isinstance(b'abc', newbytes) is True on Py2.
if isnewbytes(item):
raise TypeError(errmsg.format(i))
# Support use as a staticmethod: str.join('-', ['a', 'b'])
if type(self) == newstr:
return newstr(super(newstr, self).join(iterable))
else:
return newstr(super(newstr, newstr(self)).join(iterable))
@no('newbytes')
def find(self, sub, *args):
return super(newstr, self).find(sub, *args)
@no('newbytes')
def rfind(self, sub, *args):
return super(newstr, self).rfind(sub, *args)
@no('newbytes', (1, 2))
def replace(self, old, new, *args):
return newstr(super(newstr, self).replace(old, new, *args))
def decode(self, *args):
raise AttributeError("decode method has been disabled in newstr")
def encode(self, encoding='utf-8', errors='strict'):
"""
Returns bytes
Encode S 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 UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
'xmlcharrefreplace' as well as any other name registered with
codecs.register_error that can handle UnicodeEncodeErrors.
"""
from future.types.newbytes import newbytes
# Py2 unicode.encode() takes encoding and errors as optional parameter,
# not keyword arguments as in Python 3 str.
# For the surrogateescape error handling mechanism, the
# codecs.register_error() function seems to be inadequate for an
# implementation of it when encoding. (Decoding seems fine, however.)
# For example, in the case of
# u'\udcc3'.encode('ascii', 'surrogateescape_handler')
# after registering the ``surrogateescape_handler`` function in
# future.utils.surrogateescape, both Python 2.x and 3.x raise an
# exception anyway after the function is called because the unicode
# string it has to return isn't encodable strictly as ASCII.
if errors == 'surrogateescape':
if encoding == 'utf-16':
# Known to fail here. See test_encoding_works_normally()
raise NotImplementedError('FIXME: surrogateescape handling is '
'not yet implemented properly')
# Encode char by char, building up list of byte-strings
mybytes = []
for c in self:
code = ord(c)
if 0xD800 <= code <= 0xDCFF:
mybytes.append(newbytes([code - 0xDC00]))
else:
mybytes.append(c.encode(encoding=encoding))
return newbytes(b'').join(mybytes)
return newbytes(super(newstr, self).encode(encoding, errors))
@no('newbytes', 1)
def startswith(self, prefix, *args):
if isinstance(prefix, Iterable):
for thing in prefix:
if isnewbytes(thing):
raise TypeError(self.no_convert_msg.format(type(thing)))
return super(newstr, self).startswith(prefix, *args)
@no('newbytes', 1)
def endswith(self, prefix, *args):
# Note we need the decorator above as well as the isnewbytes()
# check because prefix can be either a bytes object or e.g. a
# tuple of possible prefixes. (If it's a bytes object, each item
# in it is an int.)
if isinstance(prefix, Iterable):
for thing in prefix:
if isnewbytes(thing):
raise TypeError(self.no_convert_msg.format(type(thing)))
return super(newstr, self).endswith(prefix, *args)
@no('newbytes', 1)
def split(self, sep=None, maxsplit=-1):
# Py2 unicode.split() takes maxsplit as an optional parameter,
# not as a keyword argument as in Python 3 str.
parts = super(newstr, self).split(sep, maxsplit)
return [newstr(part) for part in parts]
@no('newbytes', 1)
def rsplit(self, sep=None, maxsplit=-1):
# Py2 unicode.rsplit() takes maxsplit as an optional parameter,
# not as a keyword argument as in Python 3 str.
parts = super(newstr, self).rsplit(sep, maxsplit)
return [newstr(part) for part in parts]
@no('newbytes', 1)
def partition(self, sep):
parts = super(newstr, self).partition(sep)
return tuple(newstr(part) for part in parts)
@no('newbytes', 1)
def rpartition(self, sep):
parts = super(newstr, self).rpartition(sep)
return tuple(newstr(part) for part in parts)
@no('newbytes', 1)
def index(self, sub, *args):
"""
Like newstr.find() but raise ValueError when the substring is not
found.
"""
pos = self.find(sub, *args)
if pos == -1:
raise ValueError('substring not found')
return pos
def splitlines(self, keepends=False):
"""
S.splitlines(keepends=False) -> list of strings
Return a list of the lines in S, breaking at line boundaries.
Line breaks are not included in the resulting list unless keepends
is given and true.
"""
# Py2 unicode.splitlines() takes keepends as an optional parameter,
# not as a keyword argument as in Python 3 str.
parts = super(newstr, self).splitlines(keepends)
return [newstr(part) for part in parts]
def __eq__(self, other):
if (isinstance(other, unicode) or
isinstance(other, bytes) and not isnewbytes(other)):
return super(newstr, self).__eq__(other)
else:
return NotImplemented
def __hash__(self):
if (isinstance(self, unicode) or
isinstance(self, bytes) and not isnewbytes(self)):
return super(newstr, self).__hash__()
else:
raise NotImplementedError()
def __ne__(self, other):
if (isinstance(other, unicode) or
isinstance(other, bytes) and not isnewbytes(other)):
return super(newstr, self).__ne__(other)
else:
return True
unorderable_err = 'unorderable types: str() and {0}'
def __lt__(self, other):
if (isinstance(other, unicode) or
isinstance(other, bytes) and not isnewbytes(other)):
return super(newstr, self).__lt__(other)
raise TypeError(self.unorderable_err.format(type(other)))
def __le__(self, other):
if (isinstance(other, unicode) or
isinstance(other, bytes) and not isnewbytes(other)):
return super(newstr, self).__le__(other)
raise TypeError(self.unorderable_err.format(type(other)))
def __gt__(self, other):
if (isinstance(other, unicode) or
isinstance(other, bytes) and not isnewbytes(other)):
return super(newstr, self).__gt__(other)
raise TypeError(self.unorderable_err.format(type(other)))
def __ge__(self, other):
if (isinstance(other, unicode) or
isinstance(other, bytes) and not isnewbytes(other)):
return super(newstr, self).__ge__(other)
raise TypeError(self.unorderable_err.format(type(other)))
def __getattribute__(self, name):
"""
A trick to cause the ``hasattr`` builtin-fn to return False for
the 'decode' method on Py2.
"""
if name in ['decode', u'decode']:
raise AttributeError("decode method has been disabled in newstr")
return super(newstr, self).__getattribute__(name)
def __native__(self):
"""
A hook for the future.utils.native() function.
"""
return unicode(self)
@staticmethod
def maketrans(x, y=None, z=None):
"""
Return a translation table usable for str.translate().
If there is only one argument, it must be a dictionary mapping Unicode
ordinals (integers) or characters to Unicode ordinals, strings or None.
Character keys will be then converted to ordinals.
If there are two arguments, they must be strings of equal length, and
in the resulting dictionary, each character in x will be mapped to the
character at the same position in y. If there is a third argument, it
must be a string, whose characters will be mapped to None in the result.
"""
if y is None:
assert z is None
if not isinstance(x, dict):
raise TypeError('if you give only one argument to maketrans it must be a dict')
result = {}
for (key, value) in x.items():
if len(key) > 1:
raise ValueError('keys in translate table must be strings or integers')
result[ord(key)] = value
else:
if not isinstance(x, unicode) and isinstance(y, unicode):
raise TypeError('x and y must be unicode strings')
if not len(x) == len(y):
raise ValueError('the first two maketrans arguments must have equal length')
result = {}
for (xi, yi) in zip(x, y):
if len(xi) > 1:
raise ValueError('keys in translate table must be strings or integers')
result[ord(xi)] = ord(yi)
if z is not None:
for char in z:
result[ord(char)] = None
return result
def translate(self, table):
"""
S.translate(table) -> str
Return a copy of the string S, where all characters have been mapped
through the given translation table, which must be a mapping of
Unicode ordinals to Unicode ordinals, strings, or None.
Unmapped characters are left untouched. Characters mapped to None
are deleted.
"""
l = []
for c in self:
if ord(c) in table:
val = table[ord(c)]
if val is None:
continue
elif isinstance(val, unicode):
l.append(val)
else:
l.append(chr(val))
else:
l.append(c)
return ''.join(l)
def isprintable(self):
raise NotImplementedError('fixme')
def isidentifier(self):
raise NotImplementedError('fixme')
def format_map(self):
raise NotImplementedError('fixme')
__all__ = ['newstr']