Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2"""Python 2/3 compatibility. 

3 

4Compatibility implementations of features 

5only available in newer Python versions. 

6""" 

7from __future__ import absolute_import, unicode_literals 

8 

9import errno 

10import io 

11import sys 

12 

13try: 

14 from collections import Counter 

15except ImportError: # pragma: no cover 

16 from collections import defaultdict 

17 

18 def Counter(): # noqa 

19 """Create counter.""" 

20 return defaultdict(int) 

21 

22try: 

23 buffer_t = buffer 

24except NameError: # pragma: no cover 

25 # Py3 does not have buffer, only use this for isa checks. 

26 

27 class buffer_t(object): # noqa 

28 """Python 3 does not have a buffer type.""" 

29 

30bytes_t = bytes 

31 

32__all__ = [ 

33 'Counter', 'reload', 'UserList', 'UserDict', 

34 'Callable', 'Iterable', 'Mapping', 

35 'Queue', 'Empty', 'Full', 'LifoQueue', 'builtins', 'array', 

36 'zip_longest', 'map', 'zip', 'string', 'string_t', 'bytes_t', 

37 'bytes_if_py2', 'long_t', 'text_t', 'int_types', 'module_name_t', 

38 'range', 'items', 'keys', 'values', 'nextfun', 'reraise', 

39 'WhateverIO', 'with_metaclass', 'StringIO', 'getfullargspec', 

40 'THREAD_TIMEOUT_MAX', 'format_d', 'monotonic', 'buffer_t', 

41 'python_2_unicode_compatible', 

42] 

43 

44 

45# ############# py3k ######################################################## 

46PY3 = sys.version_info[0] >= 3 

47PY2 = sys.version_info[0] < 3 

48 

49try: 

50 reload = reload # noqa 

51except NameError: # pragma: no cover 

52 try: 

53 from importlib import reload # noqa 

54 except ImportError: # pragma: no cover 

55 from imp import reload # noqa 

56 

57try: 

58 from collections import UserList # noqa 

59except ImportError: # pragma: no cover 

60 from UserList import UserList # noqa 

61 

62try: 

63 from collections import UserDict # noqa 

64except ImportError: # pragma: no cover 

65 from UserDict import UserDict # noqa 

66 

67try: 

68 from collections.abc import Callable # noqa 

69except ImportError: # pragma: no cover 

70 from collections import Callable # noqa 

71 

72try: 

73 from collections.abc import Iterable # noqa 

74except ImportError: # pragma: no cover 

75 from collections import Iterable # noqa 

76 

77try: 

78 from collections.abc import Mapping # noqa 

79except ImportError: # pragma: no cover 

80 from collections import Mapping # noqa 

81 

82# ############# time.monotonic ############################################# 

83 

84if sys.version_info < (3, 3): 

85 

86 import platform 

87 SYSTEM = platform.system() 

88 

89 try: 

90 import ctypes 

91 except ImportError: # pragma: no cover 

92 ctypes = None # noqa 

93 

94 if SYSTEM == 'Darwin' and ctypes is not None: 

95 from ctypes.util import find_library 

96 libSystem = ctypes.CDLL(find_library('libSystem.dylib')) 

97 CoreServices = ctypes.CDLL(find_library('CoreServices'), 

98 use_errno=True) 

99 mach_absolute_time = libSystem.mach_absolute_time 

100 mach_absolute_time.restype = ctypes.c_uint64 

101 absolute_to_nanoseconds = CoreServices.AbsoluteToNanoseconds 

102 absolute_to_nanoseconds.restype = ctypes.c_uint64 

103 absolute_to_nanoseconds.argtypes = [ctypes.c_uint64] 

104 

105 def _monotonic(): 

106 return absolute_to_nanoseconds(mach_absolute_time()) * 1e-9 

107 

108 elif SYSTEM == 'Linux' and ctypes is not None: 

109 # from stackoverflow: 

110 # questions/1205722/how-do-i-get-monotonic-time-durations-in-python 

111 import os 

112 

113 CLOCK_MONOTONIC = 1 # see <linux/time.h> 

114 

115 class timespec(ctypes.Structure): 

116 _fields_ = [ 

117 ('tv_sec', ctypes.c_long), 

118 ('tv_nsec', ctypes.c_long), 

119 ] 

120 

121 try: 

122 librt = ctypes.CDLL('librt.so.1', use_errno=True) 

123 except Exception: 

124 try: 

125 librt = ctypes.CDLL('librt.so.0', use_errno=True) 

126 except Exception as exc: 

127 error = OSError( 

128 "Could not detect working librt library: {0}".format( 

129 exc)) 

130 error.errno = errno.ENOENT 

131 raise error 

132 clock_gettime = librt.clock_gettime 

133 clock_gettime.argtypes = [ 

134 ctypes.c_int, ctypes.POINTER(timespec), 

135 ] 

136 

137 def _monotonic(): # noqa 

138 t = timespec() 

139 if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(t)) != 0: 

140 errno_ = ctypes.get_errno() 

141 raise OSError(errno_, os.strerror(errno_)) 

142 return t.tv_sec + t.tv_nsec * 1e-9 

143 else: 

144 from time import time as _monotonic 

145try: 

146 from time import monotonic 

147except ImportError: 

148 monotonic = _monotonic # noqa 

149 

150# ############# Py3 <-> Py2 ################################################# 

151 

152if PY3: # pragma: no cover 

153 import builtins 

154 

155 from array import array 

156 from queue import Queue, Empty, Full, LifoQueue 

157 from itertools import zip_longest 

158 

159 map = map 

160 zip = zip 

161 string = str 

162 string_t = str 

163 long_t = int 

164 text_t = str 

165 range = range 

166 int_types = (int,) 

167 module_name_t = str 

168 

169 def bytes_if_py2(s): 

170 """Convert str to bytes if running under Python 2.""" 

171 return s 

172 

173 def items(d): 

174 """Get dict items iterator.""" 

175 return d.items() 

176 

177 def keys(d): 

178 """Get dict keys iterator.""" 

179 return d.keys() 

180 

181 def values(d): 

182 """Get dict values iterator.""" 

183 return d.values() 

184 

185 def nextfun(it): 

186 """Get iterator next method.""" 

187 return it.__next__ 

188 

189 exec_ = getattr(builtins, 'exec') 

190 

191 def reraise(tp, value, tb=None): 

192 """Reraise exception.""" 

193 if value.__traceback__ is not tb: 

194 raise value.with_traceback(tb) 

195 raise value 

196 

197else: 

198 import __builtin__ as builtins # noqa 

199 from array import array as _array 

200 from Queue import Queue, Empty, Full, LifoQueue # noqa 

201 from itertools import ( # noqa 

202 imap as map, 

203 izip as zip, 

204 izip_longest as zip_longest, 

205 ) 

206 

207 string = unicode # noqa 

208 string_t = basestring # noqa 

209 text_t = unicode 

210 long_t = long # noqa 

211 range = xrange 

212 module_name_t = str 

213 int_types = (int, long) 

214 

215 def array(typecode, *args, **kwargs): 

216 """Create array.""" 

217 if isinstance(typecode, unicode): 

218 typecode = typecode.encode() 

219 return _array(typecode, *args, **kwargs) 

220 

221 def bytes_if_py2(s): 

222 """Convert str to bytes if running under Python 2.""" 

223 if isinstance(s, unicode): 

224 return s.encode() 

225 return s 

226 

227 def items(d): # noqa 

228 """Return dict items iterator.""" 

229 return d.iteritems() 

230 

231 def keys(d): # noqa 

232 """Return dict key iterator.""" 

233 return d.iterkeys() 

234 

235 def values(d): # noqa 

236 """Return dict values iterator.""" 

237 return d.itervalues() 

238 

239 def nextfun(it): # noqa 

240 """Return iterator next method.""" 

241 return it.next 

242 

243 def exec_(code, globs=None, locs=None): # pragma: no cover 

244 """Execute code in a namespace.""" 

245 if globs is None: 

246 frame = sys._getframe(1) 

247 globs = frame.f_globals 

248 if locs is None: 

249 locs = frame.f_locals 

250 del frame 

251 elif locs is None: 

252 locs = globs 

253 exec("""exec code in globs, locs""") 

254 

255 exec_("""def reraise(tp, value, tb=None): raise tp, value, tb""") 

256 

257 

258def with_metaclass(Type, skip_attrs=None): 

259 """Class decorator to set metaclass. 

260 

261 Works with both Python 2 and Python 3 and it does not add 

262 an extra class in the lookup order like ``six.with_metaclass`` does 

263 (that is -- it copies the original class instead of using inheritance). 

264 

265 """ 

266 if skip_attrs is None: 

267 skip_attrs = {'__dict__', '__weakref__'} 

268 

269 def _clone_with_metaclass(Class): 

270 attrs = {key: value for key, value in items(vars(Class)) 

271 if key not in skip_attrs} 

272 return Type(Class.__name__, Class.__bases__, attrs) 

273 

274 return _clone_with_metaclass 

275 

276 

277# ############# threading.TIMEOUT_MAX ######################################## 

278try: 

279 from threading import TIMEOUT_MAX as THREAD_TIMEOUT_MAX 

280except ImportError: 

281 THREAD_TIMEOUT_MAX = 1e10 # noqa 

282 

283# ############# format(int, ',d') ############################################ 

284 

285if sys.version_info >= (2, 7): # pragma: no cover 

286 def format_d(i): 

287 """Format number.""" 

288 return format(i, ',d') 

289else: # pragma: no cover 

290 def format_d(i): # noqa 

291 """Format number.""" 

292 s = '%d' % i 

293 groups = [] 

294 while s and s[-1].isdigit(): 

295 groups.append(s[-3:]) 

296 s = s[:-3] 

297 return s + ','.join(reversed(groups)) 

298 

299StringIO = io.StringIO 

300_SIO_write = StringIO.write 

301_SIO_init = StringIO.__init__ 

302 

303 

304class WhateverIO(StringIO): 

305 """StringIO that takes bytes or str.""" 

306 

307 def __init__(self, v=None, *a, **kw): 

308 _SIO_init(self, v.decode() if isinstance(v, bytes) else v, *a, **kw) 

309 

310 def write(self, data): 

311 _SIO_write(self, data.decode() if isinstance(data, bytes) else data) 

312 

313 

314def python_2_unicode_compatible(cls): 

315 """Class decorator to ensure class is compatible with Python 2.""" 

316 return python_2_non_unicode_str(python_2_non_unicode_repr(cls)) 

317 

318 

319def python_2_non_unicode_repr(cls): 

320 """Ensure cls.__repr__ returns unicode. 

321 

322 A class decorator that ensures ``__repr__`` returns non-unicode 

323 when running under Python 2. 

324 """ 

325 if PY2: 

326 try: 

327 cls.__dict__['__repr__'] 

328 except KeyError: 

329 pass 

330 else: 

331 def __repr__(self, *args, **kwargs): 

332 return self.__unicode_repr__(*args, **kwargs).encode( 

333 'utf-8', 'replace') 

334 cls.__unicode_repr__, cls.__repr__ = cls.__repr__, __repr__ 

335 return cls 

336 

337 

338def python_2_non_unicode_str(cls): 

339 """Python 2 class string compatibility. 

340 

341 A class decorator that defines ``__unicode__`` and ``__str__`` methods 

342 under Python 2. Under Python 3 it does nothing. 

343 

344 To support Python 2 and 3 with a single code base, define a ``__str__`` 

345 method returning text and apply this decorator to the class. 

346 """ 

347 if PY2: 

348 try: 

349 cls.__dict__['__str__'] 

350 except KeyError: 

351 pass 

352 else: 

353 def __str__(self, *args, **kwargs): 

354 return self.__unicode__(*args, **kwargs).encode( 

355 'utf-8', 'replace') 

356 cls.__unicode__, cls.__str__ = cls.__str__, __str__ 

357 return cls 

358 

359 

360try: # pragma: no cover 

361 from inspect import formatargspec, getfullargspec 

362except ImportError: # Py2 

363 from collections import namedtuple 

364 from inspect import formatargspec, getargspec as _getargspec # noqa 

365 

366 FullArgSpec = namedtuple('FullArgSpec', ( 

367 'args', 'varargs', 'varkw', 'defaults', 

368 'kwonlyargs', 'kwonlydefaults', 'annotations', 

369 )) 

370 

371 def getfullargspec(fun, _fill=(None, ) * 3): # noqa 

372 """For compatibility with Python 3.""" 

373 s = _getargspec(fun) 

374 return FullArgSpec(*s + _fill)