123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- """
- compat
- ======
- Cross-compatible functions for Python 2 and 3.
- Key items to import for 2/3 compatible code:
- * iterators: range(), map(), zip(), filter(), reduce()
- * lists: lrange(), lmap(), lzip(), lfilter()
- * unicode: u() [no unicode builtin in Python 3]
- * longs: long (int in Python 3)
- * callable
- * iterable method compatibility: iteritems, iterkeys, itervalues
- * Uses the original method if available, otherwise uses items, keys, values.
- * types:
- * text_type: unicode in Python 2, str in Python 3
- * binary_type: str in Python 2, bytes in Python 3
- * string_types: basestring in Python 2, str in Python 3
- * bind_method: binds functions to classes
- * add_metaclass(metaclass) - class decorator that recreates class with with the
- given metaclass instead (and avoids intermediary class creation)
- Other items:
- * platform checker
- """
- # pylint disable=W0611
- # flake8: noqa
- import re
- import functools
- import itertools
- from distutils.version import LooseVersion
- from itertools import product
- import sys
- import platform
- import types
- from unicodedata import east_asian_width
- import struct
- import inspect
- from collections import namedtuple
- import collections
- PY2 = sys.version_info[0] == 2
- PY3 = sys.version_info[0] >= 3
- PY35 = sys.version_info >= (3, 5)
- PY36 = sys.version_info >= (3, 6)
- PY37 = sys.version_info >= (3, 7)
- PYPY = platform.python_implementation() == 'PyPy'
- try:
- import __builtin__ as builtins
- # not writeable when instantiated with string, doesn't handle unicode well
- from cStringIO import StringIO as cStringIO
- # always writeable
- from StringIO import StringIO
- BytesIO = StringIO
- import cPickle
- import httplib
- except ImportError:
- import builtins
- from io import StringIO, BytesIO
- cStringIO = StringIO
- import pickle as cPickle
- import http.client as httplib
- from pandas.compat.chainmap import DeepChainMap
- if PY3:
- def isidentifier(s):
- return s.isidentifier()
- def str_to_bytes(s, encoding=None):
- return s.encode(encoding or 'ascii')
- def bytes_to_str(b, encoding=None):
- return b.decode(encoding or 'utf-8')
- # The signature version below is directly copied from Django,
- # https://github.com/django/django/pull/4846
- def signature(f):
- sig = inspect.signature(f)
- args = [
- p.name for p in sig.parameters.values()
- if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
- ]
- varargs = [
- p.name for p in sig.parameters.values()
- if p.kind == inspect.Parameter.VAR_POSITIONAL
- ]
- varargs = varargs[0] if varargs else None
- keywords = [
- p.name for p in sig.parameters.values()
- if p.kind == inspect.Parameter.VAR_KEYWORD
- ]
- keywords = keywords[0] if keywords else None
- defaults = [
- p.default for p in sig.parameters.values()
- if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
- and p.default is not p.empty
- ] or None
- argspec = namedtuple('Signature', ['args', 'defaults',
- 'varargs', 'keywords'])
- return argspec(args, defaults, varargs, keywords)
- def get_range_parameters(data):
- """Gets the start, stop, and step parameters from a range object"""
- return data.start, data.stop, data.step
- # have to explicitly put builtins into the namespace
- range = range
- map = map
- zip = zip
- filter = filter
- intern = sys.intern
- reduce = functools.reduce
- long = int
- unichr = chr
- # This was introduced in Python 3.3, but we don't support
- # Python 3.x < 3.5, so checking PY3 is safe.
- FileNotFoundError = FileNotFoundError
- # 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))
- from importlib import reload
- reload = reload
- Hashable = collections.abc.Hashable
- Iterable = collections.abc.Iterable
- Mapping = collections.abc.Mapping
- MutableMapping = collections.abc.MutableMapping
- Sequence = collections.abc.Sequence
- Sized = collections.abc.Sized
- Set = collections.abc.Set
- else:
- # Python 2
- _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
- FileNotFoundError = IOError
- def isidentifier(s, dotted=False):
- return bool(_name_re.match(s))
- def str_to_bytes(s, encoding='ascii'):
- return s
- def bytes_to_str(b, encoding='ascii'):
- return b
- def signature(f):
- return inspect.getargspec(f)
- def get_range_parameters(data):
- """Gets the start, stop, and step parameters from a range object"""
- # seems we only have indexing ops to infer
- # rather than direct accessors
- if len(data) > 1:
- step = data[1] - data[0]
- stop = data[-1] + step
- start = data[0]
- elif len(data):
- start = data[0]
- stop = data[0] + 1
- step = 1
- else:
- start = stop = 0
- step = 1
- return start, stop, step
- # import iterator versions of these functions
- range = xrange
- intern = intern
- zip = itertools.izip
- filter = itertools.ifilter
- map = itertools.imap
- reduce = reduce
- long = long
- unichr = unichr
- # Python 2-builtin ranges produce lists
- lrange = builtins.range
- lzip = builtins.zip
- lmap = builtins.map
- lfilter = builtins.filter
- reload = builtins.reload
- Hashable = collections.Hashable
- Iterable = collections.Iterable
- Mapping = collections.Mapping
- MutableMapping = collections.MutableMapping
- Sequence = collections.Sequence
- Sized = collections.Sized
- Set = collections.Set
- if PY2:
- def iteritems(obj, **kw):
- return obj.iteritems(**kw)
- def iterkeys(obj, **kw):
- return obj.iterkeys(**kw)
- def itervalues(obj, **kw):
- return obj.itervalues(**kw)
- next = lambda it: it.next()
- else:
- def iteritems(obj, **kw):
- return iter(obj.items(**kw))
- def iterkeys(obj, **kw):
- return iter(obj.keys(**kw))
- def itervalues(obj, **kw):
- return iter(obj.values(**kw))
- next = next
- def bind_method(cls, name, func):
- """Bind a method to class, python 2 and python 3 compatible.
- Parameters
- ----------
- cls : type
- class to receive bound method
- name : basestring
- name of method on class instance
- func : function
- function to be bound as method
- Returns
- -------
- None
- """
- # only python 2 has bound/unbound method issue
- if not PY3:
- setattr(cls, name, types.MethodType(func, None, cls))
- else:
- setattr(cls, name, func)
- # ----------------------------------------------------------------------------
- # functions largely based / taken from the six module
- # Much of the code in this module comes from Benjamin Peterson's six library.
- # The license for this library can be found in LICENSES/SIX and the code can be
- # found at https://bitbucket.org/gutworth/six
- # Definition of East Asian Width
- # http://unicode.org/reports/tr11/
- # Ambiguous width can be changed by option
- _EAW_MAP = {'Na': 1, 'N': 1, 'W': 2, 'F': 2, 'H': 1}
- if PY3:
- string_types = str,
- integer_types = int,
- class_types = type,
- text_type = str
- binary_type = bytes
- def u(s):
- return s
- def u_safe(s):
- return s
- def to_str(s):
- """
- Convert bytes and non-string into Python 3 str
- """
- if isinstance(s, binary_type):
- s = bytes_to_str(s)
- elif not isinstance(s, string_types):
- s = str(s)
- return s
- def strlen(data, encoding=None):
- # encoding is for compat with PY2
- return len(data)
- def east_asian_len(data, encoding=None, ambiguous_width=1):
- """
- Calculate display width considering unicode East Asian Width
- """
- if isinstance(data, text_type):
- return sum(_EAW_MAP.get(east_asian_width(c), ambiguous_width) for c in data)
- else:
- return len(data)
- def import_lzma():
- """ import lzma from the std library """
- import lzma
- return lzma
- def set_function_name(f, name, cls):
- """ Bind the name/qualname attributes of the function """
- f.__name__ = name
- f.__qualname__ = '{klass}.{name}'.format(
- klass=cls.__name__,
- name=name)
- f.__module__ = cls.__module__
- return f
- ResourceWarning = ResourceWarning
- else:
- string_types = basestring,
- integer_types = (int, long)
- class_types = (type, types.ClassType)
- text_type = unicode
- binary_type = str
- def u(s):
- return unicode(s, "unicode_escape")
- def u_safe(s):
- try:
- return unicode(s, "unicode_escape")
- except:
- return s
- def to_str(s):
- """
- Convert unicode and non-string into Python 2 str
- """
- if not isinstance(s, string_types):
- s = str(s)
- return s
- def strlen(data, encoding=None):
- try:
- data = data.decode(encoding)
- except UnicodeError:
- pass
- return len(data)
- def east_asian_len(data, encoding=None, ambiguous_width=1):
- """
- Calculate display width considering unicode East Asian Width
- """
- if isinstance(data, text_type):
- try:
- data = data.decode(encoding)
- except UnicodeError:
- pass
- return sum(_EAW_MAP.get(east_asian_width(c), ambiguous_width) for c in data)
- else:
- return len(data)
- def import_lzma():
- """ import the backported lzma library
- or raise ImportError if not available """
- from backports import lzma
- return lzma
- def set_function_name(f, name, cls):
- """ Bind the name attributes of the function """
- f.__name__ = name
- return f
- class ResourceWarning(Warning):
- pass
- string_and_binary_types = string_types + (binary_type,)
- try:
- # callable reintroduced in later versions of Python
- callable = callable
- except NameError:
- def callable(obj):
- return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
- if PY2:
- # In PY2 functools.wraps doesn't provide metadata pytest needs to generate
- # decorated tests using parametrization. See pytest GH issue #2782
- def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
- updated=functools.WRAPPER_UPDATES):
- def wrapper(f):
- f = functools.wraps(wrapped, assigned, updated)(f)
- f.__wrapped__ = wrapped
- return f
- return wrapper
- else:
- wraps = functools.wraps
- def add_metaclass(metaclass):
- """Class decorator for creating a class with a metaclass."""
- def wrapper(cls):
- orig_vars = cls.__dict__.copy()
- orig_vars.pop('__dict__', None)
- orig_vars.pop('__weakref__', None)
- for slots_var in orig_vars.get('__slots__', ()):
- orig_vars.pop(slots_var)
- return metaclass(cls.__name__, cls.__bases__, orig_vars)
- return wrapper
- from collections import OrderedDict, Counter
- if PY3:
- def raise_with_traceback(exc, traceback=Ellipsis):
- if traceback == Ellipsis:
- _, _, traceback = sys.exc_info()
- raise exc.with_traceback(traceback)
- else:
- # this version of raise is a syntax error in Python 3
- exec("""
- def raise_with_traceback(exc, traceback=Ellipsis):
- if traceback == Ellipsis:
- _, _, traceback = sys.exc_info()
- raise exc, None, traceback
- """)
- raise_with_traceback.__doc__ = """Raise exception with existing traceback.
- If traceback is not passed, uses sys.exc_info() to get traceback."""
- # dateutil minimum version
- import dateutil
- if LooseVersion(dateutil.__version__) < LooseVersion('2.5'):
- raise ImportError('dateutil 2.5.0 is the minimum required version')
- from dateutil import parser as _date_parser
- parse_date = _date_parser.parse
- # In Python 3.7, the private re._pattern_type is removed.
- # Python 3.5+ have typing.re.Pattern
- if PY36:
- import typing
- re_type = typing.re.Pattern
- else:
- re_type = type(re.compile(''))
- # https://github.com/pandas-dev/pandas/pull/9123
- def is_platform_little_endian():
- """ am I little endian """
- return sys.byteorder == 'little'
- def is_platform_windows():
- return sys.platform == 'win32' or sys.platform == 'cygwin'
- def is_platform_linux():
- return sys.platform == 'linux2'
- def is_platform_mac():
- return sys.platform == 'darwin'
- def is_platform_32bit():
- return struct.calcsize("P") * 8 < 64
|