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