text: """
  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)
  * 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
      Iterator = collections.abc.Iterator
      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
      Iterator = collections.Iterator
      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,)
  
  
  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
  
  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
  