py3k.py 6.93 KB
Newer Older
xuebingbing's avatar
xuebingbing committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
"""
Python 3.X compatibility tools.

While this file was originally intented for Python 2 -> 3 transition,
it is now used to create a compatibility layer between different
minor versions of Python 3.

While the active version of numpy may not support a given version of python, we
allow downstream libraries to continue to use these shims for forward
compatibility with numpy while they transition their code to newer versions of
Python.
"""
__all__ = ['bytes', 'asbytes', 'isfileobj', 'getexception', 'strchar',
           'unicode', 'asunicode', 'asbytes_nested', 'asunicode_nested',
           'asstr', 'open_latin1', 'long', 'basestring', 'sixu',
           'integer_types', 'is_pathlib_path', 'npy_load_module', 'Path',
           'pickle', 'contextlib_nullcontext', 'os_fspath', 'os_PathLike']

import sys
import os
try:
    from pathlib import Path, PurePath
except ImportError:
    Path = PurePath = None

if sys.version_info[0] >= 3:
    import io

    try:
        import pickle5 as pickle
    except ImportError:
        import pickle

    long = int
    integer_types = (int,)
    basestring = str
    unicode = str
    bytes = bytes

    def asunicode(s):
        if isinstance(s, bytes):
            return s.decode('latin1')
        return str(s)

    def asbytes(s):
        if isinstance(s, bytes):
            return s
        return str(s).encode('latin1')

    def asstr(s):
        if isinstance(s, bytes):
            return s.decode('latin1')
        return str(s)

    def isfileobj(f):
        return isinstance(f, (io.FileIO, io.BufferedReader, io.BufferedWriter))

    def open_latin1(filename, mode='r'):
        return open(filename, mode=mode, encoding='iso-8859-1')

    def sixu(s):
        return s

    strchar = 'U'

else:
    import cpickle as pickle

    bytes = str
    long = long
    basestring = basestring
    unicode = unicode
    integer_types = (int, long)
    asbytes = str
    asstr = str
    strchar = 'S'

    def isfileobj(f):
        return isinstance(f, file)

    def asunicode(s):
        if isinstance(s, unicode):
            return s
        return str(s).decode('ascii')

    def open_latin1(filename, mode='r'):
        return open(filename, mode=mode)

    def sixu(s):
        return unicode(s, 'unicode_escape')

def getexception():
    return sys.exc_info()[1]

def asbytes_nested(x):
    if hasattr(x, '__iter__') and not isinstance(x, (bytes, unicode)):
        return [asbytes_nested(y) for y in x]
    else:
        return asbytes(x)

def asunicode_nested(x):
    if hasattr(x, '__iter__') and not isinstance(x, (bytes, unicode)):
        return [asunicode_nested(y) for y in x]
    else:
        return asunicode(x)

def is_pathlib_path(obj):
    """
    Check whether obj is a pathlib.Path object.

    Prefer using `isinstance(obj, os_PathLike)` instead of this function.
    """
    return Path is not None and isinstance(obj, Path)

# from Python 3.7
class contextlib_nullcontext(object):
    """Context manager that does no additional processing.

    Used as a stand-in for a normal context manager, when a particular
    block of code is only sometimes used with a normal context manager:

    cm = optional_cm if condition else nullcontext()
    with cm:
        # Perform operation, using optional_cm if condition is True
    """

    def __init__(self, enter_result=None):
        self.enter_result = enter_result

    def __enter__(self):
        return self.enter_result

    def __exit__(self, *excinfo):
        pass


if sys.version_info[0] >= 3 and sys.version_info[1] >= 4:
    def npy_load_module(name, fn, info=None):
        """
        Load a module.

        .. versionadded:: 1.11.2

        Parameters
        ----------
        name : str
            Full module name.
        fn : str
            Path to module file.
        info : tuple, optional
            Only here for backward compatibility with Python 2.*.

        Returns
        -------
        mod : module

        """
        import importlib.machinery
        return importlib.machinery.SourceFileLoader(name, fn).load_module()
else:
    def npy_load_module(name, fn, info=None):
        """
        Load a module.

        .. versionadded:: 1.11.2

        Parameters
        ----------
        name : str
            Full module name.
        fn : str
            Path to module file.
        info : tuple, optional
            Information as returned by `imp.find_module`
            (suffix, mode, type).

        Returns
        -------
        mod : module

        """
        import imp
        if info is None:
            path = os.path.dirname(fn)
            fo, fn, info = imp.find_module(name, [path])
        else:
            fo = open(fn, info[1])
        try:
            mod = imp.load_module(name, fo, fn, info)
        finally:
            fo.close()
        return mod

# backport abc.ABC
import abc
if sys.version_info[:2] >= (3, 4):
    abc_ABC = abc.ABC
else:
    abc_ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()})


# Backport os.fs_path, os.PathLike, and PurePath.__fspath__
if sys.version_info[:2] >= (3, 6):
    os_fspath = os.fspath
    os_PathLike = os.PathLike
else:
    def _PurePath__fspath__(self):
        return str(self)

    class os_PathLike(abc_ABC):
        """Abstract base class for implementing the file system path protocol."""

        @abc.abstractmethod
        def __fspath__(self):
            """Return the file system path representation of the object."""
            raise NotImplementedError

        @classmethod
        def __subclasshook__(cls, subclass):
            if PurePath is not None and issubclass(subclass, PurePath):
                return True
            return hasattr(subclass, '__fspath__')


    def os_fspath(path):
        """Return the path representation of a path-like object.
        If str or bytes is passed in, it is returned unchanged. Otherwise the
        os.PathLike interface is used to get the path representation. If the
        path representation is not str or bytes, TypeError is raised. If the
        provided path is not str, bytes, or os.PathLike, TypeError is raised.
        """
        if isinstance(path, (str, bytes)):
            return path

        # Work from the object's type to match method resolution of other magic
        # methods.
        path_type = type(path)
        try:
            path_repr = path_type.__fspath__(path)
        except AttributeError:
            if hasattr(path_type, '__fspath__'):
                raise
            elif PurePath is not None and issubclass(path_type, PurePath):
                return _PurePath__fspath__(path)
            else:
                raise TypeError("expected str, bytes or os.PathLike object, "
                                "not " + path_type.__name__)
        if isinstance(path_repr, (str, bytes)):
            return path_repr
        else:
            raise TypeError("expected {}.__fspath__() to return str or bytes, "
                            "not {}".format(path_type.__name__,
                                            type(path_repr).__name__))