PageRenderTime 60ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/bento/utils/utils.py

https://github.com/rgommers/Bento
Python | 368 lines | 357 code | 9 blank | 2 comment | 9 complexity | 71dcf3986647132fa4211243f6910702 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. import os
  2. import sys
  3. import stat
  4. import re
  5. import glob
  6. import shutil
  7. import errno
  8. import subprocess
  9. import shlex
  10. from six.moves \
  11. import \
  12. cPickle
  13. import os.path as op
  14. from bento.compat.api \
  15. import \
  16. partial
  17. try:
  18. from hashlib import md5
  19. except ImportError:
  20. from md5 import md5
  21. # Color handling for terminals (taken from waf)
  22. COLORS_LST = {
  23. 'USE' : True,
  24. 'BOLD' :'\x1b[01;1m',
  25. 'RED' :'\x1b[01;31m',
  26. 'GREEN' :'\x1b[32m',
  27. 'YELLOW':'\x1b[33m',
  28. 'PINK' :'\x1b[35m',
  29. 'BLUE' :'\x1b[01;34m',
  30. 'CYAN' :'\x1b[36m',
  31. 'NORMAL':'\x1b[0m',
  32. 'cursor_on' :'\x1b[?25h',
  33. 'cursor_off' :'\x1b[?25l',
  34. }
  35. GOT_TTY = not os.environ.get('TERM', 'dumb') in ['dumb', 'emacs']
  36. if GOT_TTY:
  37. try:
  38. GOT_TTY = sys.stderr.isatty()
  39. except AttributeError:
  40. GOT_TTY = False
  41. if not GOT_TTY or 'NOCOLOR' in os.environ:
  42. COLORS_LST['USE'] = False
  43. def get_color(cl):
  44. if not COLORS_LST['USE']:
  45. return ''
  46. return COLORS_LST.get(cl, '')
  47. class foo(object):
  48. def __getattr__(self, a):
  49. return get_color(a)
  50. def __call__(self, a):
  51. return get_color(a)
  52. COLORS = foo()
  53. def pprint(color, s, fout=None):
  54. if fout is None:
  55. fout = sys.stderr
  56. fout.write('%s%s%s\n' % (COLORS(color), s, COLORS('NORMAL')))
  57. _IDPATTERN = "[a-zA-Z_][a-zA-Z_0-9]*"
  58. _DELIM = "$"
  59. def _simple_subst_vars(s, local_vars):
  60. """Like subst_vars, but does not handle escaping."""
  61. def _subst(m):
  62. var_name = m.group(1)
  63. if var_name in local_vars:
  64. return local_vars[var_name]
  65. else:
  66. raise ValueError("%s not defined" % var_name)
  67. def _resolve(d):
  68. ret = {}
  69. for k, v in d.items():
  70. ret[k] = re.sub("\%s(%s)" % (_DELIM, _IDPATTERN), _subst, v)
  71. return ret
  72. ret = _resolve(s)
  73. while not ret == s:
  74. s = ret
  75. ret = _resolve(s)
  76. return ret
  77. def subst_vars (s, local_vars):
  78. """Perform shell/Perl-style variable substitution.
  79. Every occurrence of '$' followed by a name is considered a variable, and
  80. variable is substituted by the value found in the `local_vars' dictionary.
  81. Raise ValueError for any variables not found in `local_vars'.
  82. '$' may be escaped by using '$$'
  83. Parameters
  84. ----------
  85. s: str
  86. variable to substitute
  87. local_vars: dict
  88. dict of variables
  89. """
  90. # Resolve variable substitution within the local_vars dict
  91. local_vars = _simple_subst_vars(local_vars, local_vars)
  92. def _subst (match):
  93. named = match.group("named")
  94. if named is not None:
  95. if named in local_vars:
  96. return str(local_vars[named])
  97. else:
  98. raise ValueError("Invalid variable '%s'" % named)
  99. if match.group("escaped") is not None:
  100. return _DELIM
  101. raise ValueError("This should not happen")
  102. def _do_subst(v):
  103. pattern_s = r"""
  104. %(delim)s(?:
  105. (?P<escaped>%(delim)s) |
  106. (?P<named>%(id)s)
  107. )""" % {"delim": r"\%s" % _DELIM, "id": _IDPATTERN}
  108. pattern = re.compile(pattern_s, re.VERBOSE)
  109. return pattern.sub(_subst, v)
  110. try:
  111. return _do_subst(s)
  112. except KeyError:
  113. raise ValueError("invalid variable '$%s'" % ex_stack())
  114. # Taken from multiprocessing code
  115. def cpu_count():
  116. '''
  117. Returns the number of CPUs in the system
  118. '''
  119. if sys.platform == 'win32':
  120. try:
  121. num = int(os.environ['NUMBER_OF_PROCESSORS'])
  122. except (ValueError, KeyError):
  123. num = 0
  124. elif 'bsd' in sys.platform or sys.platform == 'darwin':
  125. try:
  126. num = int(os.popen('sysctl -n hw.ncpu').read())
  127. except ValueError:
  128. num = 0
  129. else:
  130. try:
  131. num = os.sysconf('SC_NPROCESSORS_ONLN')
  132. except (ValueError, OSError, AttributeError):
  133. num = 0
  134. if num >= 1:
  135. return num
  136. else:
  137. return 1
  138. #raise NotImplementedError('cannot determine number of cpus')
  139. def same_content(f1, f2):
  140. """Return true if files in f1 and f2 has the same content."""
  141. fid1 = open(f1, "rb")
  142. try:
  143. fid2 = open(f2, "rb")
  144. try:
  145. return md5(fid1.read()).digest() == md5(fid2.read()).digest()
  146. finally:
  147. fid2.close()
  148. finally:
  149. fid1.close()
  150. def virtualenv_prefix():
  151. """Return the virtual environment prefix if running python is "virtualized"
  152. (i.e. run inside virtualenv), None otherwise."""
  153. try:
  154. real_prefix = sys.real_prefix
  155. except AttributeError:
  156. return None
  157. else:
  158. if real_prefix != sys.prefix:
  159. return op.normpath(sys.prefix)
  160. else:
  161. return None
  162. def to_camel_case(s):
  163. """Transform a string to camel case convention."""
  164. if len(s) < 1:
  165. return ""
  166. else:
  167. # XXX: could most likely be simpler with regex ?
  168. ret = []
  169. if s[0].isalpha():
  170. ret.append(s[0].upper())
  171. i = 1
  172. elif s[0] == "_":
  173. i = 0
  174. while i < len(s) and s[i] == "_":
  175. ret.append(s[i])
  176. i += 1
  177. if i < len(s) and s[i].isalpha():
  178. ret.append(s[i].upper())
  179. i += 1
  180. else:
  181. i = 0
  182. while i < len(s):
  183. c = s[i]
  184. if c == "_" and i < len(s) - 1 and s[i+1].isalpha():
  185. ret.append(s[i+1].upper())
  186. i += 1
  187. else:
  188. ret.append(c)
  189. i += 1
  190. return "".join(ret)
  191. # We sometimes use keys from json dictionary as key word arguments. This may
  192. # not work because python2.5 and below requires arguments to be string and not
  193. # unicode while json may decode stuff as unicode, or alternatively encoding as
  194. # ascii does not work in python3 which requires string (==unicode) there. Or
  195. # maybe I just don't understand how this works...
  196. def fix_kw(kw):
  197. """Make sure the given dictionary may be used as a kw dict independently on
  198. the python version and encoding of kw's keys."""
  199. return dict([(str(k), v) for k, v in kw.items()])
  200. def cmd_is_runnable(cmd, **kw):
  201. """Test whether the given command can work.
  202. Notes
  203. -----
  204. Arguments are the same as subprocess.Popen, except for stdout/stderr
  205. defaults (to PIPE)
  206. """
  207. for stream in ["stdout", "stderr"]:
  208. if not stream in kw:
  209. kw[stream] = subprocess.PIPE
  210. try:
  211. p = subprocess.Popen(cmd, **kw)
  212. p.communicate()
  213. return p.returncode == 0
  214. except OSError:
  215. e = extract_exception()
  216. if e.errno == 2:
  217. return False
  218. else:
  219. raise
  220. def explode_path(path):
  221. """Split a path into its components.
  222. If the path is absolute, the first value of the returned list will be '/',
  223. or the drive letter for platforms where it is applicable.
  224. Example
  225. -------
  226. >>> explode_path("/Users/joe")
  227. ["/", "Users", "joe"]
  228. """
  229. ret = []
  230. d, p = op.splitdrive(path)
  231. while p:
  232. head, tail = op.split(p)
  233. if head == p:
  234. ret.append(head)
  235. break
  236. if tail:
  237. ret.append(tail)
  238. p = head
  239. if d:
  240. ret.append(d)
  241. return ret[::-1]
  242. if sys.version_info[0] < 3:
  243. def is_string(s):
  244. return isinstance(s, str) or isinstance(s, unicode)
  245. else:
  246. def is_string(s):
  247. return isinstance(s, str)
  248. def extract_exception():
  249. """Extract the last exception.
  250. Used to avoid the except ExceptionType as e, which cannot be written the
  251. same across supported versions. I.e::
  252. try:
  253. ...
  254. except Exception, e:
  255. ...
  256. becomes:
  257. try:
  258. ...
  259. except Exception:
  260. e = extract_exception()
  261. """
  262. return sys.exc_info()[1]
  263. # We cannot use octal literal for compat with python 3.x
  264. MODE_755 = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | \
  265. stat.S_IROTH | stat.S_IXOTH
  266. MODE_777 = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | \
  267. stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | \
  268. stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH
  269. class CommaListLexer(object):
  270. def __init__(self, instream):
  271. self._lexer = shlex.shlex(instream, posix=True)
  272. self._lexer.whitespace += ','
  273. self._lexer.wordchars += './()*-'
  274. self.eof = self._lexer.eof
  275. def get_token(self):
  276. return self._lexer.get_token()
  277. def comma_list_split(s):
  278. lexer = CommaListLexer(s)
  279. ret = []
  280. t = lexer.get_token()
  281. while t != lexer.eof:
  282. ret.append(t)
  283. t = lexer.get_token()
  284. return ret
  285. class memoized(object):
  286. """Decorator that caches a function's return value each time it is called.
  287. If called later with the same arguments, the cached value is returned, and
  288. not re-evaluated.
  289. """
  290. def __init__(self, func):
  291. self.func = func
  292. self.cache = {}
  293. def __call__(self, *args):
  294. try:
  295. return self.cache[args]
  296. except KeyError:
  297. value = self.func(*args)
  298. self.cache[args] = value
  299. return value
  300. except TypeError:
  301. # uncachable -- for instance, passing a list as an argument.
  302. # Better to not cache than to blow up entirely.
  303. return self.func(*args)
  304. def __repr__(self):
  305. """Return the function's docstring."""
  306. return self.func.__doc__
  307. def __get__(self, obj, objtype):
  308. """Support instance methods."""
  309. return partial(self.__call__, obj)
  310. def read_or_create_dict(filename):
  311. if op.exists(filename):
  312. fid = open(filename, "rb")
  313. try:
  314. return cPickle.load(fid)
  315. finally:
  316. fid.close()
  317. else:
  318. return {}