__init__.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. """
  2. compat
  3. ======
  4. Cross-compatible functions for Python 2 and 3.
  5. Key items to import for 2/3 compatible code:
  6. * iterators: range(), map(), zip(), filter(), reduce()
  7. * lists: lrange(), lmap(), lzip(), lfilter()
  8. * unicode: u() [no unicode builtin in Python 3]
  9. * longs: long (int in Python 3)
  10. * callable
  11. * iterable method compatibility: iteritems, iterkeys, itervalues
  12. * Uses the original method if available, otherwise uses items, keys, values.
  13. * types:
  14. * text_type: unicode in Python 2, str in Python 3
  15. * binary_type: str in Python 2, bytes in Python 3
  16. * string_types: basestring in Python 2, str in Python 3
  17. * bind_method: binds functions to classes
  18. * add_metaclass(metaclass) - class decorator that recreates class with with the
  19. given metaclass instead (and avoids intermediary class creation)
  20. Other items:
  21. * platform checker
  22. """
  23. # pylint disable=W0611
  24. # flake8: noqa
  25. import re
  26. import functools
  27. import itertools
  28. from distutils.version import LooseVersion
  29. from itertools import product
  30. import sys
  31. import platform
  32. import types
  33. from unicodedata import east_asian_width
  34. import struct
  35. import inspect
  36. from collections import namedtuple
  37. import collections
  38. PY2 = sys.version_info[0] == 2
  39. PY3 = sys.version_info[0] >= 3
  40. PY35 = sys.version_info >= (3, 5)
  41. PY36 = sys.version_info >= (3, 6)
  42. PY37 = sys.version_info >= (3, 7)
  43. PYPY = platform.python_implementation() == 'PyPy'
  44. try:
  45. import __builtin__ as builtins
  46. # not writeable when instantiated with string, doesn't handle unicode well
  47. from cStringIO import StringIO as cStringIO
  48. # always writeable
  49. from StringIO import StringIO
  50. BytesIO = StringIO
  51. import cPickle
  52. import httplib
  53. except ImportError:
  54. import builtins
  55. from io import StringIO, BytesIO
  56. cStringIO = StringIO
  57. import pickle as cPickle
  58. import http.client as httplib
  59. from pandas.compat.chainmap import DeepChainMap
  60. if PY3:
  61. def isidentifier(s):
  62. return s.isidentifier()
  63. def str_to_bytes(s, encoding=None):
  64. return s.encode(encoding or 'ascii')
  65. def bytes_to_str(b, encoding=None):
  66. return b.decode(encoding or 'utf-8')
  67. # The signature version below is directly copied from Django,
  68. # https://github.com/django/django/pull/4846
  69. def signature(f):
  70. sig = inspect.signature(f)
  71. args = [
  72. p.name for p in sig.parameters.values()
  73. if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
  74. ]
  75. varargs = [
  76. p.name for p in sig.parameters.values()
  77. if p.kind == inspect.Parameter.VAR_POSITIONAL
  78. ]
  79. varargs = varargs[0] if varargs else None
  80. keywords = [
  81. p.name for p in sig.parameters.values()
  82. if p.kind == inspect.Parameter.VAR_KEYWORD
  83. ]
  84. keywords = keywords[0] if keywords else None
  85. defaults = [
  86. p.default for p in sig.parameters.values()
  87. if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
  88. and p.default is not p.empty
  89. ] or None
  90. argspec = namedtuple('Signature', ['args', 'defaults',
  91. 'varargs', 'keywords'])
  92. return argspec(args, defaults, varargs, keywords)
  93. def get_range_parameters(data):
  94. """Gets the start, stop, and step parameters from a range object"""
  95. return data.start, data.stop, data.step
  96. # have to explicitly put builtins into the namespace
  97. range = range
  98. map = map
  99. zip = zip
  100. filter = filter
  101. intern = sys.intern
  102. reduce = functools.reduce
  103. long = int
  104. unichr = chr
  105. # This was introduced in Python 3.3, but we don't support
  106. # Python 3.x < 3.5, so checking PY3 is safe.
  107. FileNotFoundError = FileNotFoundError
  108. # list-producing versions of the major Python iterating functions
  109. def lrange(*args, **kwargs):
  110. return list(range(*args, **kwargs))
  111. def lzip(*args, **kwargs):
  112. return list(zip(*args, **kwargs))
  113. def lmap(*args, **kwargs):
  114. return list(map(*args, **kwargs))
  115. def lfilter(*args, **kwargs):
  116. return list(filter(*args, **kwargs))
  117. from importlib import reload
  118. reload = reload
  119. Hashable = collections.abc.Hashable
  120. Iterable = collections.abc.Iterable
  121. Mapping = collections.abc.Mapping
  122. MutableMapping = collections.abc.MutableMapping
  123. Sequence = collections.abc.Sequence
  124. Sized = collections.abc.Sized
  125. Set = collections.abc.Set
  126. else:
  127. # Python 2
  128. _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
  129. FileNotFoundError = IOError
  130. def isidentifier(s, dotted=False):
  131. return bool(_name_re.match(s))
  132. def str_to_bytes(s, encoding='ascii'):
  133. return s
  134. def bytes_to_str(b, encoding='ascii'):
  135. return b
  136. def signature(f):
  137. return inspect.getargspec(f)
  138. def get_range_parameters(data):
  139. """Gets the start, stop, and step parameters from a range object"""
  140. # seems we only have indexing ops to infer
  141. # rather than direct accessors
  142. if len(data) > 1:
  143. step = data[1] - data[0]
  144. stop = data[-1] + step
  145. start = data[0]
  146. elif len(data):
  147. start = data[0]
  148. stop = data[0] + 1
  149. step = 1
  150. else:
  151. start = stop = 0
  152. step = 1
  153. return start, stop, step
  154. # import iterator versions of these functions
  155. range = xrange
  156. intern = intern
  157. zip = itertools.izip
  158. filter = itertools.ifilter
  159. map = itertools.imap
  160. reduce = reduce
  161. long = long
  162. unichr = unichr
  163. # Python 2-builtin ranges produce lists
  164. lrange = builtins.range
  165. lzip = builtins.zip
  166. lmap = builtins.map
  167. lfilter = builtins.filter
  168. reload = builtins.reload
  169. Hashable = collections.Hashable
  170. Iterable = collections.Iterable
  171. Mapping = collections.Mapping
  172. MutableMapping = collections.MutableMapping
  173. Sequence = collections.Sequence
  174. Sized = collections.Sized
  175. Set = collections.Set
  176. if PY2:
  177. def iteritems(obj, **kw):
  178. return obj.iteritems(**kw)
  179. def iterkeys(obj, **kw):
  180. return obj.iterkeys(**kw)
  181. def itervalues(obj, **kw):
  182. return obj.itervalues(**kw)
  183. next = lambda it: it.next()
  184. else:
  185. def iteritems(obj, **kw):
  186. return iter(obj.items(**kw))
  187. def iterkeys(obj, **kw):
  188. return iter(obj.keys(**kw))
  189. def itervalues(obj, **kw):
  190. return iter(obj.values(**kw))
  191. next = next
  192. def bind_method(cls, name, func):
  193. """Bind a method to class, python 2 and python 3 compatible.
  194. Parameters
  195. ----------
  196. cls : type
  197. class to receive bound method
  198. name : basestring
  199. name of method on class instance
  200. func : function
  201. function to be bound as method
  202. Returns
  203. -------
  204. None
  205. """
  206. # only python 2 has bound/unbound method issue
  207. if not PY3:
  208. setattr(cls, name, types.MethodType(func, None, cls))
  209. else:
  210. setattr(cls, name, func)
  211. # ----------------------------------------------------------------------------
  212. # functions largely based / taken from the six module
  213. # Much of the code in this module comes from Benjamin Peterson's six library.
  214. # The license for this library can be found in LICENSES/SIX and the code can be
  215. # found at https://bitbucket.org/gutworth/six
  216. # Definition of East Asian Width
  217. # http://unicode.org/reports/tr11/
  218. # Ambiguous width can be changed by option
  219. _EAW_MAP = {'Na': 1, 'N': 1, 'W': 2, 'F': 2, 'H': 1}
  220. if PY3:
  221. string_types = str,
  222. integer_types = int,
  223. class_types = type,
  224. text_type = str
  225. binary_type = bytes
  226. def u(s):
  227. return s
  228. def u_safe(s):
  229. return s
  230. def to_str(s):
  231. """
  232. Convert bytes and non-string into Python 3 str
  233. """
  234. if isinstance(s, binary_type):
  235. s = bytes_to_str(s)
  236. elif not isinstance(s, string_types):
  237. s = str(s)
  238. return s
  239. def strlen(data, encoding=None):
  240. # encoding is for compat with PY2
  241. return len(data)
  242. def east_asian_len(data, encoding=None, ambiguous_width=1):
  243. """
  244. Calculate display width considering unicode East Asian Width
  245. """
  246. if isinstance(data, text_type):
  247. return sum(_EAW_MAP.get(east_asian_width(c), ambiguous_width) for c in data)
  248. else:
  249. return len(data)
  250. def import_lzma():
  251. """ import lzma from the std library """
  252. import lzma
  253. return lzma
  254. def set_function_name(f, name, cls):
  255. """ Bind the name/qualname attributes of the function """
  256. f.__name__ = name
  257. f.__qualname__ = '{klass}.{name}'.format(
  258. klass=cls.__name__,
  259. name=name)
  260. f.__module__ = cls.__module__
  261. return f
  262. ResourceWarning = ResourceWarning
  263. else:
  264. string_types = basestring,
  265. integer_types = (int, long)
  266. class_types = (type, types.ClassType)
  267. text_type = unicode
  268. binary_type = str
  269. def u(s):
  270. return unicode(s, "unicode_escape")
  271. def u_safe(s):
  272. try:
  273. return unicode(s, "unicode_escape")
  274. except:
  275. return s
  276. def to_str(s):
  277. """
  278. Convert unicode and non-string into Python 2 str
  279. """
  280. if not isinstance(s, string_types):
  281. s = str(s)
  282. return s
  283. def strlen(data, encoding=None):
  284. try:
  285. data = data.decode(encoding)
  286. except UnicodeError:
  287. pass
  288. return len(data)
  289. def east_asian_len(data, encoding=None, ambiguous_width=1):
  290. """
  291. Calculate display width considering unicode East Asian Width
  292. """
  293. if isinstance(data, text_type):
  294. try:
  295. data = data.decode(encoding)
  296. except UnicodeError:
  297. pass
  298. return sum(_EAW_MAP.get(east_asian_width(c), ambiguous_width) for c in data)
  299. else:
  300. return len(data)
  301. def import_lzma():
  302. """ import the backported lzma library
  303. or raise ImportError if not available """
  304. from backports import lzma
  305. return lzma
  306. def set_function_name(f, name, cls):
  307. """ Bind the name attributes of the function """
  308. f.__name__ = name
  309. return f
  310. class ResourceWarning(Warning):
  311. pass
  312. string_and_binary_types = string_types + (binary_type,)
  313. try:
  314. # callable reintroduced in later versions of Python
  315. callable = callable
  316. except NameError:
  317. def callable(obj):
  318. return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
  319. if PY2:
  320. # In PY2 functools.wraps doesn't provide metadata pytest needs to generate
  321. # decorated tests using parametrization. See pytest GH issue #2782
  322. def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
  323. updated=functools.WRAPPER_UPDATES):
  324. def wrapper(f):
  325. f = functools.wraps(wrapped, assigned, updated)(f)
  326. f.__wrapped__ = wrapped
  327. return f
  328. return wrapper
  329. else:
  330. wraps = functools.wraps
  331. def add_metaclass(metaclass):
  332. """Class decorator for creating a class with a metaclass."""
  333. def wrapper(cls):
  334. orig_vars = cls.__dict__.copy()
  335. orig_vars.pop('__dict__', None)
  336. orig_vars.pop('__weakref__', None)
  337. for slots_var in orig_vars.get('__slots__', ()):
  338. orig_vars.pop(slots_var)
  339. return metaclass(cls.__name__, cls.__bases__, orig_vars)
  340. return wrapper
  341. from collections import OrderedDict, Counter
  342. if PY3:
  343. def raise_with_traceback(exc, traceback=Ellipsis):
  344. if traceback == Ellipsis:
  345. _, _, traceback = sys.exc_info()
  346. raise exc.with_traceback(traceback)
  347. else:
  348. # this version of raise is a syntax error in Python 3
  349. exec("""
  350. def raise_with_traceback(exc, traceback=Ellipsis):
  351. if traceback == Ellipsis:
  352. _, _, traceback = sys.exc_info()
  353. raise exc, None, traceback
  354. """)
  355. raise_with_traceback.__doc__ = """Raise exception with existing traceback.
  356. If traceback is not passed, uses sys.exc_info() to get traceback."""
  357. # dateutil minimum version
  358. import dateutil
  359. if LooseVersion(dateutil.__version__) < LooseVersion('2.5'):
  360. raise ImportError('dateutil 2.5.0 is the minimum required version')
  361. from dateutil import parser as _date_parser
  362. parse_date = _date_parser.parse
  363. # In Python 3.7, the private re._pattern_type is removed.
  364. # Python 3.5+ have typing.re.Pattern
  365. if PY36:
  366. import typing
  367. re_type = typing.re.Pattern
  368. else:
  369. re_type = type(re.compile(''))
  370. # https://github.com/pandas-dev/pandas/pull/9123
  371. def is_platform_little_endian():
  372. """ am I little endian """
  373. return sys.byteorder == 'little'
  374. def is_platform_windows():
  375. return sys.platform == 'win32' or sys.platform == 'cygwin'
  376. def is_platform_linux():
  377. return sys.platform == 'linux2'
  378. def is_platform_mac():
  379. return sys.platform == 'darwin'
  380. def is_platform_32bit():
  381. return struct.calcsize("P") * 8 < 64