six.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. # -*- coding: utf-8 -*-
  2. '''Utilities for writing code that runs on Python 2 and 3.'''
  3. from stuf.base import first, docit, identity, getframe, backport, norm
  4. import sys
  5. import types
  6. from functools import partial
  7. from importlib import import_module
  8. from operator import attrgetter, methodcaller, lt, gt
  9. from .base import isfactory
  10. intern = backport('__builtin__.intern', 'sys.intern')
  11. OrderedDict = backport('collections.OrderedDict', 'ordereddict.OrderedDict')
  12. unittest = backport('unittest2', 'unittest')
  13. get_ident = backport(
  14. 'thread.get_ident', 'dummy_thread.get_ident', '_thread.get_ident',
  15. )
  16. pickle = backport('cPickle', 'pickle')
  17. filter = backport('future_builtins.filter', 'builtins.filter')
  18. map = backport('future_builtins.map', 'builtins.map')
  19. zip = backport('future_builtins.zip', 'builtins.zip')
  20. # use next generation regular expression library if available
  21. rcompile = backport('regex.compile', 're.compile')
  22. rescape = backport('regex.escape', 're.escape')
  23. rsub = backport('regex.sub', 're.sub')
  24. subprocess = backport('subprocess32.Popen', 'subprocess.Popen')
  25. # True if we are running on Python 3.
  26. PY3 = first(sys.version_info) == 3
  27. if PY3:
  28. strings = str,
  29. integers = int,
  30. long = int
  31. classes = type,
  32. utfme = native = texts = str
  33. byteme = binaries = bytes
  34. MAXSIZE = sys.maxsize
  35. else:
  36. strings = basestring,
  37. integers = (int, long)
  38. classes = (type, types.ClassType)
  39. utfme = texts = unicode
  40. byteme = native = binaries = str
  41. if sys.platform == 'java':
  42. # Jython always uses 32 bits.
  43. MAXSIZE = int((1 << 31) - 1)
  44. else:
  45. # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
  46. class X(object):
  47. def __len__(self):
  48. return 1 << 31
  49. try:
  50. len(X())
  51. except OverflowError:
  52. # 32-bit
  53. MAXSIZE = int((1 << 31) - 1)
  54. else:
  55. # 64-bit
  56. MAXSIZE = int((1 << 63) - 1)
  57. del X
  58. # numbers
  59. isgtemax = docit(partial(gt, MAXSIZE), 'Less than max size?')
  60. isinteger = docit(isfactory(integers), 'is integer?')
  61. isltemax = docit(partial(lt, MAXSIZE), 'Greater than max size?')
  62. # strings
  63. isstring = docit(isfactory(strings), 'is string')
  64. isunicode = docit(isfactory(utfme), 'is text?')
  65. isnative = docit(isfactory(native), 'is native string')
  66. isbinary = isbytes = docit(isfactory(byteme), 'is binary?')
  67. class _LazyDescr(object):
  68. def __init__(self, name):
  69. self.name = name
  70. def __get__(self, obj, tp):
  71. result = self._resolve()
  72. setattr(obj, self.name, result)
  73. # This is a bit ugly, but it avoids running this again.
  74. delattr(tp, self.name)
  75. return result
  76. class MovedModule(_LazyDescr):
  77. def __init__(self, name, old, new=None):
  78. super(MovedModule, self).__init__(name)
  79. if PY3:
  80. if new is None:
  81. new = name
  82. self.mod = new
  83. else:
  84. self.mod = old
  85. def _resolve(self):
  86. return import_module(self.mod)
  87. class MovedAttribute(_LazyDescr):
  88. def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
  89. super(MovedAttribute, self).__init__(name)
  90. if PY3:
  91. if new_mod is None:
  92. new_mod = name
  93. self.mod = new_mod
  94. if new_attr is None:
  95. new_attr = name if old_attr is None else old_attr
  96. self.attr = new_attr
  97. else:
  98. self.mod = old_mod
  99. if old_attr is None:
  100. old_attr = name
  101. self.attr = old_attr
  102. def _resolve(self):
  103. return getattr(import_module(self.mod), self.attr)
  104. class _MovedItems(types.ModuleType):
  105. '''Lazy loading of moved objects.'''
  106. _moved_attributes = [
  107. MovedAttribute(
  108. 'GeneratorContextManager',
  109. 'contextlib',
  110. 'contextlib',
  111. 'GeneratorContextManager',
  112. '_GeneratorContextManager',
  113. ),
  114. MovedAttribute(
  115. 'filterfalse', 'itertools', 'itertools', 'ifilterfalse', 'filterfalse',
  116. ),
  117. MovedAttribute(
  118. 'zip_longest', 'itertools', 'itertools', 'izip_longest', 'zip_longest',
  119. ),
  120. MovedAttribute('reload_module', '__builtin__', 'imp', 'reload'),
  121. MovedAttribute('reduce', '__builtin__', 'functools'),
  122. MovedAttribute('xrange', '__builtin__', 'builtins', 'xrange', 'range'),
  123. MovedAttribute('parsedate_tz', 'rfc822', 'email.utils', 'parsedate_tz'),
  124. MovedAttribute('formatdate', 'rfc822', 'email.utils', 'formatdate'),
  125. MovedAttribute('parse_qs', 'cgi', 'urllib.parse', 'parse_qs'),
  126. MovedAttribute('urlencode', 'urllib', 'urllib.parse', 'urlencode'),
  127. MovedAttribute('quote', 'urllib', 'urllib.parse'),
  128. MovedAttribute('input', '__builtin__', 'builtins', 'raw_input', 'input'),
  129. MovedModule('builtins', '__builtin__'),
  130. MovedModule('configparser', 'ConfigParser'),
  131. MovedModule('copyreg', 'copy_reg'),
  132. MovedModule('http_cookiejar', 'cookielib', 'http.cookiejar'),
  133. MovedModule('http_cookies', 'Cookie', 'http.cookies'),
  134. MovedModule('html_entities', 'htmlentitydefs', 'html.entities'),
  135. MovedModule('html_parser', 'HTMLParser', 'html.parser'),
  136. MovedModule('http_client', 'httplib', 'http.client'),
  137. MovedModule('BaseHTTPServer', 'BaseHTTPServer', 'http.server'),
  138. MovedModule('CGIHTTPServer', 'CGIHTTPServer', 'http.server'),
  139. MovedModule('SimpleHTTPServer', 'SimpleHTTPServer', 'http.server'),
  140. MovedModule('queue', 'Queue'),
  141. MovedModule('reprlib', 'repr'),
  142. MovedModule('socketserver', 'SocketServer'),
  143. MovedModule('urllib_robotparser', 'robotparser', 'urllib.robotparser'),
  144. MovedModule('winreg', '_winreg'),
  145. MovedModule('xmlrpc', 'xmlrpclib', 'xmlrpc.client'),
  146. ]
  147. for attr in _moved_attributes:
  148. setattr(_MovedItems, attr.name, attr)
  149. del attr
  150. moves = sys.modules['stuf.six.moves'] = _MovedItems('moves')
  151. def add_move(move):
  152. '''Add an item to six.moves.'''
  153. setattr(_MovedItems, move.name, move)
  154. def remove_move(name):
  155. '''Remove item from six.moves.'''
  156. try:
  157. delattr(_MovedItems, name)
  158. except AttributeError:
  159. try:
  160. del moves.__dict__[name]
  161. except KeyError:
  162. raise AttributeError('no such move, {r}'.format(name))
  163. if PY3:
  164. _meth_func = '__func__'
  165. _meth_self = '__self__'
  166. _func_code = '__code__'
  167. _func_defaults = '__defaults__'
  168. _iterkeys = 'keys'
  169. _itervalues = 'values'
  170. _iteritems = 'items'
  171. else:
  172. _meth_func = 'im_func'
  173. _meth_self = 'im_self'
  174. _func_code = 'func_code'
  175. _func_defaults = 'func_defaults'
  176. _iterkeys = 'iterkeys'
  177. _itervalues = 'itervalues'
  178. _iteritems = 'iteritems'
  179. try:
  180. advance_iterator = next
  181. except NameError:
  182. advance_iterator = methodcaller('next')
  183. next = advance_iterator
  184. if PY3:
  185. range = range
  186. unbound_function = identity
  187. Iterator = object
  188. callable = lambda o: any('__call__' in k.__dict__ for k in type(o).__mro__)
  189. else:
  190. range = xrange
  191. callable = callable
  192. unbound_function = attrgetter('im_func')
  193. class Iterator(object):
  194. def next(self):
  195. return type(self).__next__(self)
  196. func_code = attrgetter(_func_code)
  197. func_defaults = attrgetter(_func_defaults)
  198. getitems = attrgetter(_iteritems)
  199. getkeys = attrgetter(_iterkeys)
  200. getvalues = attrgetter(_itervalues)
  201. items = methodcaller(_iteritems)
  202. keys = methodcaller(_iterkeys)
  203. method_func = attrgetter(_meth_func)
  204. method_self = attrgetter(_meth_self)
  205. values = methodcaller(_itervalues)
  206. getdoc = attrgetter('__doc__')
  207. getmod = attrgetter('__module__')
  208. if PY3:
  209. b = methodcaller('encode', 'latin-1')
  210. u = identity
  211. if sys.version_info[1] <= 1:
  212. int2byte = partial(lambda x, i: x((i,)), bytes)
  213. else:
  214. # This is about 2x faster than the implementation above on 3.2+
  215. int2byte = methodcaller('to_bytes', 1, 'big')
  216. import io
  217. StringIO = io.StringIO
  218. BytesIO = io.BytesIO
  219. else:
  220. b = identity
  221. u = partial(lambda x, s: x(s, 'unicode_escape'), unicode)
  222. int2byte = chr
  223. import StringIO
  224. StringIO = BytesIO = StringIO.StringIO
  225. #b = docit(b, 'Byte literal.')
  226. #u = docit(u, 'Text literal.')
  227. if PY3:
  228. import builtins # @UnresolvedImport
  229. exec_ = getattr(builtins, 'exec')
  230. def reraise(tp, value, tb=None):
  231. if value.__traceback__ is not tb:
  232. raise value.with_traceback(tb)
  233. raise value
  234. printf = getattr(builtins, 'print')
  235. del builtins
  236. else:
  237. def exec_(code, globs=None, locs=None):
  238. '''Execute code in a namespace.'''
  239. if globs is None:
  240. frame = getframe()
  241. globs = frame.f_globals
  242. if locs is None:
  243. locs = frame.f_locals
  244. del frame
  245. elif locs is None:
  246. locs = globs
  247. exec('''exec code in globs, locs''')
  248. exec_('def reraise(tp, value, tb=None): raise tp, value, tb')
  249. def printf(*args, **kw):
  250. '''The new-style print function.'''
  251. fp = kw.pop('file', sys.stdout)
  252. if fp is None:
  253. return
  254. def write(data): # @IgnorePep8
  255. if not isstring(data):
  256. data = str(data)
  257. fp.write(data)
  258. want_unicode = False
  259. sep = kw.pop('sep', None)
  260. if sep is not None:
  261. if isunicode(sep):
  262. want_unicode = True
  263. elif not isbytes(sep):
  264. raise TypeError('sep must be None or a string')
  265. end = kw.pop('end', None)
  266. if end is not None:
  267. if isunicode(end):
  268. want_unicode = True
  269. elif not isbytes(end):
  270. raise TypeError('end must be None or a string')
  271. if kw:
  272. raise TypeError('invalid keyword arguments to print()')
  273. if not want_unicode:
  274. for arg in args:
  275. if isunicode(arg):
  276. want_unicode = True
  277. break
  278. if want_unicode:
  279. newline = utfme('\n')
  280. space = utfme(' ')
  281. else:
  282. newline = '\n'
  283. space = ' '
  284. if sep is None:
  285. sep = space
  286. if end is None:
  287. end = newline
  288. for i, arg in enumerate(args):
  289. if i:
  290. write(sep)
  291. write(arg)
  292. write(end)
  293. def with_metaclass(meta, base=object):
  294. '''Create a base class with a metaclass.'''
  295. return meta('NewBase', (base,), {})
  296. def tounicode(thing, encoding='utf-8', errors='strict'):
  297. '''Convert string `thing` to unicode string with `encoding`.'''
  298. if isbytes(thing):
  299. try:
  300. return thing.decode(encoding, errors)
  301. except UnicodeDecodeError:
  302. return norm(thing).decode(encoding, errors)
  303. elif isunicode(thing):
  304. return thing.encode(encoding, errors).decode(encoding, errors)
  305. return utfme(thing).encode(encoding, errors).decode(encoding, errors)
  306. def tobytes(thing, encoding='utf-8', errors='strict'):
  307. '''Convert string `thing` to byte string `encoding`.'''
  308. if isbytes(thing):
  309. return thing
  310. elif isunicode(thing):
  311. return norm(thing).encode(encoding, errors)
  312. return utfme(thing).encode(encoding, errors)