compat.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. # util/compat.py
  2. # Copyright (C) 2005-2017 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  7. """Handle Python version/platform incompatibilities."""
  8. import sys
  9. from contextlib import contextmanager
  10. try:
  11. import threading
  12. except ImportError:
  13. import dummy_threading as threading
  14. py36 = sys.version_info >= (3, 6)
  15. py33 = sys.version_info >= (3, 3)
  16. py32 = sys.version_info >= (3, 2)
  17. py3k = sys.version_info >= (3, 0)
  18. py2k = sys.version_info < (3, 0)
  19. py265 = sys.version_info >= (2, 6, 5)
  20. jython = sys.platform.startswith('java')
  21. pypy = hasattr(sys, 'pypy_version_info')
  22. win32 = sys.platform.startswith('win')
  23. cpython = not pypy and not jython # TODO: something better for this ?
  24. import collections
  25. next = next
  26. if py3k:
  27. import pickle
  28. else:
  29. try:
  30. import cPickle as pickle
  31. except ImportError:
  32. import pickle
  33. # work around http://bugs.python.org/issue2646
  34. if py265:
  35. safe_kwarg = lambda arg: arg
  36. else:
  37. safe_kwarg = str
  38. ArgSpec = collections.namedtuple("ArgSpec",
  39. ["args", "varargs", "keywords", "defaults"])
  40. if py3k:
  41. import builtins
  42. from inspect import getfullargspec as inspect_getfullargspec
  43. from urllib.parse import (quote_plus, unquote_plus,
  44. parse_qsl, quote, unquote)
  45. import configparser
  46. from io import StringIO
  47. from io import BytesIO as byte_buffer
  48. def inspect_getargspec(func):
  49. return ArgSpec(
  50. *inspect_getfullargspec(func)[0:4]
  51. )
  52. string_types = str,
  53. binary_types = bytes,
  54. binary_type = bytes
  55. text_type = str
  56. int_types = int,
  57. iterbytes = iter
  58. def u(s):
  59. return s
  60. def ue(s):
  61. return s
  62. def b(s):
  63. return s.encode("latin-1")
  64. if py32:
  65. callable = callable
  66. else:
  67. def callable(fn):
  68. return hasattr(fn, '__call__')
  69. def cmp(a, b):
  70. return (a > b) - (a < b)
  71. from functools import reduce
  72. print_ = getattr(builtins, "print")
  73. import_ = getattr(builtins, '__import__')
  74. import itertools
  75. itertools_filterfalse = itertools.filterfalse
  76. itertools_filter = filter
  77. itertools_imap = map
  78. from itertools import zip_longest
  79. import base64
  80. def b64encode(x):
  81. return base64.b64encode(x).decode('ascii')
  82. def b64decode(x):
  83. return base64.b64decode(x.encode('ascii'))
  84. else:
  85. from inspect import getargspec as inspect_getfullargspec
  86. inspect_getargspec = inspect_getfullargspec
  87. from urllib import quote_plus, unquote_plus, quote, unquote
  88. from urlparse import parse_qsl
  89. import ConfigParser as configparser
  90. from StringIO import StringIO
  91. from cStringIO import StringIO as byte_buffer
  92. string_types = basestring,
  93. binary_types = bytes,
  94. binary_type = str
  95. text_type = unicode
  96. int_types = int, long
  97. def iterbytes(buf):
  98. return (ord(byte) for byte in buf)
  99. def u(s):
  100. # this differs from what six does, which doesn't support non-ASCII
  101. # strings - we only use u() with
  102. # literal source strings, and all our source files with non-ascii
  103. # in them (all are tests) are utf-8 encoded.
  104. return unicode(s, "utf-8")
  105. def ue(s):
  106. return unicode(s, "unicode_escape")
  107. def b(s):
  108. return s
  109. def import_(*args):
  110. if len(args) == 4:
  111. args = args[0:3] + ([str(arg) for arg in args[3]],)
  112. return __import__(*args)
  113. callable = callable
  114. cmp = cmp
  115. reduce = reduce
  116. import base64
  117. b64encode = base64.b64encode
  118. b64decode = base64.b64decode
  119. def print_(*args, **kwargs):
  120. fp = kwargs.pop("file", sys.stdout)
  121. if fp is None:
  122. return
  123. for arg in enumerate(args):
  124. if not isinstance(arg, basestring):
  125. arg = str(arg)
  126. fp.write(arg)
  127. import itertools
  128. itertools_filterfalse = itertools.ifilterfalse
  129. itertools_filter = itertools.ifilter
  130. itertools_imap = itertools.imap
  131. from itertools import izip_longest as zip_longest
  132. import time
  133. if win32 or jython:
  134. time_func = time.clock
  135. else:
  136. time_func = time.time
  137. from collections import namedtuple
  138. from operator import attrgetter as dottedgetter
  139. if py3k:
  140. def reraise(tp, value, tb=None, cause=None):
  141. if cause is not None:
  142. assert cause is not value, "Same cause emitted"
  143. value.__cause__ = cause
  144. if value.__traceback__ is not tb:
  145. raise value.with_traceback(tb)
  146. raise value
  147. else:
  148. # not as nice as that of Py3K, but at least preserves
  149. # the code line where the issue occurred
  150. exec("def reraise(tp, value, tb=None, cause=None):\n"
  151. " if cause is not None:\n"
  152. " assert cause is not value, 'Same cause emitted'\n"
  153. " raise tp, value, tb\n")
  154. def raise_from_cause(exception, exc_info=None):
  155. if exc_info is None:
  156. exc_info = sys.exc_info()
  157. exc_type, exc_value, exc_tb = exc_info
  158. cause = exc_value if exc_value is not exception else None
  159. reraise(type(exception), exception, tb=exc_tb, cause=cause)
  160. if py3k:
  161. exec_ = getattr(builtins, 'exec')
  162. else:
  163. def exec_(func_text, globals_, lcl=None):
  164. if lcl is None:
  165. exec('exec func_text in globals_')
  166. else:
  167. exec('exec func_text in globals_, lcl')
  168. def with_metaclass(meta, *bases):
  169. """Create a base class with a metaclass.
  170. Drops the middle class upon creation.
  171. Source: http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/
  172. """
  173. class metaclass(meta):
  174. __call__ = type.__call__
  175. __init__ = type.__init__
  176. def __new__(cls, name, this_bases, d):
  177. if this_bases is None:
  178. return type.__new__(cls, name, (), d)
  179. return meta(name, bases, d)
  180. return metaclass('temporary_class', None, {})
  181. @contextmanager
  182. def nested(*managers):
  183. """Implement contextlib.nested, mostly for unit tests.
  184. As tests still need to run on py2.6 we can't use multiple-with yet.
  185. Function is removed in py3k but also emits deprecation warning in 2.7
  186. so just roll it here for everyone.
  187. """
  188. exits = []
  189. vars = []
  190. exc = (None, None, None)
  191. try:
  192. for mgr in managers:
  193. exit = mgr.__exit__
  194. enter = mgr.__enter__
  195. vars.append(enter())
  196. exits.append(exit)
  197. yield vars
  198. except:
  199. exc = sys.exc_info()
  200. finally:
  201. while exits:
  202. exit = exits.pop()
  203. try:
  204. if exit(*exc):
  205. exc = (None, None, None)
  206. except:
  207. exc = sys.exc_info()
  208. if exc != (None, None, None):
  209. reraise(exc[0], exc[1], exc[2])