Source code for future.utils

"""
A selection of cross-compatible functions for Python 2 and 3.

This module exports useful functions for 2/3 compatible code:

    * bind_method: binds functions to classes
    * ``native_str_to_bytes`` and ``bytes_to_native_str``
    * ``native_str``: always equal to the native platform string object (because
      this may be shadowed by imports from future.builtins)
    * lists: lrange(), lmap(), lzip(), lfilter()
    * iterable method compatibility:
        - iteritems, iterkeys, itervalues
        - viewitems, viewkeys, viewvalues

        These use the original method if available, otherwise they use items,
        keys, values.

    * types:

        * text_type: unicode in Python 2, str in Python 3
        * string_types: basestring in Python 2, str in Python 3
        * binary_type: str in Python 2, bytes in Python 3
        * integer_types: (int, long) in Python 2, int in Python 3
        * class_types: (type, types.ClassType) in Python 2, type in Python 3

    * bchr(c):
        Take an integer and make a 1-character byte string
    * bord(c)
        Take the result of indexing on a byte string and make an integer
    * tobytes(s)
        Take a text string, a byte string, or a sequence of characters taken
        from a byte string, and make a byte string.

    * raise_from()
    * raise_with_traceback()

This module also defines these decorators:

    * ``python_2_unicode_compatible``
    * ``with_metaclass``
    * ``implements_iterator``

Some of the functions in this module come from the following sources:

    * Jinja2 (BSD licensed: see
      https://github.com/mitsuhiko/jinja2/blob/master/LICENSE)
    * Pandas compatibility module pandas.compat
    * six.py by Benjamin Peterson
    * Django
"""

import types
import sys
import numbers
import functools
import copy
import inspect


PY3 = sys.version_info[0] >= 3
PY34_PLUS = sys.version_info[0:2] >= (3, 4)
PY35_PLUS = sys.version_info[0:2] >= (3, 5)
PY36_PLUS = sys.version_info[0:2] >= (3, 6)
PY37_PLUS = sys.version_info[0:2] >= (3, 7)
PY38_PLUS = sys.version_info[0:2] >= (3, 8)
PY39_PLUS = sys.version_info[0:2] >= (3, 9)
PY2 = sys.version_info[0] == 2
PY26 = sys.version_info[0:2] == (2, 6)
PY27 = sys.version_info[0:2] == (2, 7)
PYPY = hasattr(sys, 'pypy_translation_info')


[docs] def python_2_unicode_compatible(cls): """ A decorator that defines __unicode__ and __str__ methods under Python 2. Under Python 3, this decorator is a no-op. To support Python 2 and 3 with a single code base, define a __str__ method returning unicode text and apply this decorator to the class, like this:: >>> from future.utils import python_2_unicode_compatible >>> @python_2_unicode_compatible ... class MyClass(object): ... def __str__(self): ... return u'Unicode string: \u5b54\u5b50' >>> a = MyClass() Then, after this import: >>> from future.builtins import str the following is ``True`` on both Python 3 and 2:: >>> str(a) == a.encode('utf-8').decode('utf-8') True and, on a Unicode-enabled terminal with the right fonts, these both print the Chinese characters for Confucius:: >>> print(a) >>> print(str(a)) The implementation comes from django.utils.encoding. """ if not PY3: cls.__unicode__ = cls.__str__ cls.__str__ = lambda self: self.__unicode__().encode('utf-8') return cls
[docs] def with_metaclass(meta, *bases): """ Function from jinja2/_compat.py. License: BSD. Use it like this:: class BaseForm(object): pass class FormType(type): pass class Form(with_metaclass(FormType, BaseForm)): pass This requires a bit of explanation: the basic idea is to make a dummy metaclass for one level of class instantiation that replaces itself with the actual metaclass. Because of internal type checks we also need to make sure that we downgrade the custom metaclass for one level to something closer to type (that's why __call__ and __init__ comes back from type etc.). This has the advantage over six.with_metaclass of not introducing dummy classes into the final MRO. """ class metaclass(meta): __call__ = type.__call__ __init__ = type.__init__ def __new__(cls, name, this_bases, d): if this_bases is None: return type.__new__(cls, name, (), d) return meta(name, bases, d) return metaclass('temporary_class', None, {})
# Definitions from pandas.compat and six.py follow: if PY3: def bchr(s): return bytes([s]) def bstr(s): if isinstance(s, str): return bytes(s, 'latin-1') else: return bytes(s) def bord(s): return s string_types = str, integer_types = int, class_types = type, text_type = str binary_type = bytes else: # Python 2 def bchr(s): return chr(s) def bstr(s): return str(s) def bord(s): return ord(s) string_types = basestring, integer_types = (int, long) class_types = (type, types.ClassType) text_type = unicode binary_type = str ### if PY3: def tobytes(s): if isinstance(s, bytes): return s else: if isinstance(s, str): return s.encode('latin-1') else: return bytes(s) else: # Python 2
[docs] def tobytes(s): if isinstance(s, unicode): return s.encode('latin-1') else: return ''.join(s)
tobytes.__doc__ = """ Encodes to latin-1 (where the first 256 chars are the same as ASCII.) """ if PY3: def native_str_to_bytes(s, encoding='utf-8'): return s.encode(encoding) def bytes_to_native_str(b, encoding='utf-8'): return b.decode(encoding) def text_to_native_str(t, encoding=None): return t else: # Python 2
[docs] def native_str_to_bytes(s, encoding=None): from future.types import newbytes # to avoid a circular import return newbytes(s)
def bytes_to_native_str(b, encoding=None): return native(b) def text_to_native_str(t, encoding='ascii'): """ Use this to create a Py2 native string when "from __future__ import unicode_literals" is in effect. """ return unicode(t).encode(encoding) native_str_to_bytes.__doc__ = """ On Py3, returns an encoded string. On Py2, returns a newbytes type, ignoring the ``encoding`` argument. """ if PY3: # list-producing versions of the major Python iterating functions def lrange(*args, **kwargs): return list(range(*args, **kwargs)) def lzip(*args, **kwargs): return list(zip(*args, **kwargs)) def lmap(*args, **kwargs): return list(map(*args, **kwargs)) def lfilter(*args, **kwargs): return list(filter(*args, **kwargs)) else: import __builtin__ # Python 2-builtin ranges produce lists lrange = __builtin__.range lzip = __builtin__.zip lmap = __builtin__.map lfilter = __builtin__.filter
[docs] def isidentifier(s, dotted=False): ''' A function equivalent to the str.isidentifier method on Py3 ''' if dotted: return all(isidentifier(a) for a in s.split('.')) if PY3: return s.isidentifier() else: import re _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$") return bool(_name_re.match