/bento/utils/utils.py
Python | 368 lines | 357 code | 9 blank | 2 comment | 9 complexity | 71dcf3986647132fa4211243f6910702 MD5 | raw file
Possible License(s): BSD-3-Clause
- import os
- import sys
- import stat
- import re
- import glob
- import shutil
- import errno
- import subprocess
- import shlex
- from six.moves \
- import \
- cPickle
- import os.path as op
- from bento.compat.api \
- import \
- partial
- try:
- from hashlib import md5
- except ImportError:
- from md5 import md5
- # Color handling for terminals (taken from waf)
- COLORS_LST = {
- 'USE' : True,
- 'BOLD' :'\x1b[01;1m',
- 'RED' :'\x1b[01;31m',
- 'GREEN' :'\x1b[32m',
- 'YELLOW':'\x1b[33m',
- 'PINK' :'\x1b[35m',
- 'BLUE' :'\x1b[01;34m',
- 'CYAN' :'\x1b[36m',
- 'NORMAL':'\x1b[0m',
- 'cursor_on' :'\x1b[?25h',
- 'cursor_off' :'\x1b[?25l',
- }
- GOT_TTY = not os.environ.get('TERM', 'dumb') in ['dumb', 'emacs']
- if GOT_TTY:
- try:
- GOT_TTY = sys.stderr.isatty()
- except AttributeError:
- GOT_TTY = False
- if not GOT_TTY or 'NOCOLOR' in os.environ:
- COLORS_LST['USE'] = False
- def get_color(cl):
- if not COLORS_LST['USE']:
- return ''
- return COLORS_LST.get(cl, '')
- class foo(object):
- def __getattr__(self, a):
- return get_color(a)
- def __call__(self, a):
- return get_color(a)
- COLORS = foo()
- def pprint(color, s, fout=None):
- if fout is None:
- fout = sys.stderr
- fout.write('%s%s%s\n' % (COLORS(color), s, COLORS('NORMAL')))
- _IDPATTERN = "[a-zA-Z_][a-zA-Z_0-9]*"
- _DELIM = "$"
- def _simple_subst_vars(s, local_vars):
- """Like subst_vars, but does not handle escaping."""
- def _subst(m):
- var_name = m.group(1)
- if var_name in local_vars:
- return local_vars[var_name]
- else:
- raise ValueError("%s not defined" % var_name)
-
- def _resolve(d):
- ret = {}
- for k, v in d.items():
- ret[k] = re.sub("\%s(%s)" % (_DELIM, _IDPATTERN), _subst, v)
- return ret
- ret = _resolve(s)
- while not ret == s:
- s = ret
- ret = _resolve(s)
- return ret
- def subst_vars (s, local_vars):
- """Perform shell/Perl-style variable substitution.
- Every occurrence of '$' followed by a name is considered a variable, and
- variable is substituted by the value found in the `local_vars' dictionary.
- Raise ValueError for any variables not found in `local_vars'.
- '$' may be escaped by using '$$'
- Parameters
- ----------
- s: str
- variable to substitute
- local_vars: dict
- dict of variables
- """
- # Resolve variable substitution within the local_vars dict
- local_vars = _simple_subst_vars(local_vars, local_vars)
- def _subst (match):
- named = match.group("named")
- if named is not None:
- if named in local_vars:
- return str(local_vars[named])
- else:
- raise ValueError("Invalid variable '%s'" % named)
- if match.group("escaped") is not None:
- return _DELIM
- raise ValueError("This should not happen")
- def _do_subst(v):
- pattern_s = r"""
- %(delim)s(?:
- (?P<escaped>%(delim)s) |
- (?P<named>%(id)s)
- )""" % {"delim": r"\%s" % _DELIM, "id": _IDPATTERN}
- pattern = re.compile(pattern_s, re.VERBOSE)
- return pattern.sub(_subst, v)
- try:
- return _do_subst(s)
- except KeyError:
- raise ValueError("invalid variable '$%s'" % ex_stack())
- # Taken from multiprocessing code
- def cpu_count():
- '''
- Returns the number of CPUs in the system
- '''
- if sys.platform == 'win32':
- try:
- num = int(os.environ['NUMBER_OF_PROCESSORS'])
- except (ValueError, KeyError):
- num = 0
- elif 'bsd' in sys.platform or sys.platform == 'darwin':
- try:
- num = int(os.popen('sysctl -n hw.ncpu').read())
- except ValueError:
- num = 0
- else:
- try:
- num = os.sysconf('SC_NPROCESSORS_ONLN')
- except (ValueError, OSError, AttributeError):
- num = 0
- if num >= 1:
- return num
- else:
- return 1
- #raise NotImplementedError('cannot determine number of cpus')
- def same_content(f1, f2):
- """Return true if files in f1 and f2 has the same content."""
- fid1 = open(f1, "rb")
- try:
- fid2 = open(f2, "rb")
- try:
- return md5(fid1.read()).digest() == md5(fid2.read()).digest()
- finally:
- fid2.close()
- finally:
- fid1.close()
- def virtualenv_prefix():
- """Return the virtual environment prefix if running python is "virtualized"
- (i.e. run inside virtualenv), None otherwise."""
- try:
- real_prefix = sys.real_prefix
- except AttributeError:
- return None
- else:
- if real_prefix != sys.prefix:
- return op.normpath(sys.prefix)
- else:
- return None
- def to_camel_case(s):
- """Transform a string to camel case convention."""
- if len(s) < 1:
- return ""
- else:
- # XXX: could most likely be simpler with regex ?
- ret = []
- if s[0].isalpha():
- ret.append(s[0].upper())
- i = 1
- elif s[0] == "_":
- i = 0
- while i < len(s) and s[i] == "_":
- ret.append(s[i])
- i += 1
- if i < len(s) and s[i].isalpha():
- ret.append(s[i].upper())
- i += 1
- else:
- i = 0
- while i < len(s):
- c = s[i]
- if c == "_" and i < len(s) - 1 and s[i+1].isalpha():
- ret.append(s[i+1].upper())
- i += 1
- else:
- ret.append(c)
- i += 1
- return "".join(ret)
- # We sometimes use keys from json dictionary as key word arguments. This may
- # not work because python2.5 and below requires arguments to be string and not
- # unicode while json may decode stuff as unicode, or alternatively encoding as
- # ascii does not work in python3 which requires string (==unicode) there. Or
- # maybe I just don't understand how this works...
- def fix_kw(kw):
- """Make sure the given dictionary may be used as a kw dict independently on
- the python version and encoding of kw's keys."""
- return dict([(str(k), v) for k, v in kw.items()])
- def cmd_is_runnable(cmd, **kw):
- """Test whether the given command can work.
- Notes
- -----
- Arguments are the same as subprocess.Popen, except for stdout/stderr
- defaults (to PIPE)
- """
- for stream in ["stdout", "stderr"]:
- if not stream in kw:
- kw[stream] = subprocess.PIPE
- try:
- p = subprocess.Popen(cmd, **kw)
- p.communicate()
- return p.returncode == 0
- except OSError:
- e = extract_exception()
- if e.errno == 2:
- return False
- else:
- raise
- def explode_path(path):
- """Split a path into its components.
- If the path is absolute, the first value of the returned list will be '/',
- or the drive letter for platforms where it is applicable.
- Example
- -------
- >>> explode_path("/Users/joe")
- ["/", "Users", "joe"]
- """
- ret = []
- d, p = op.splitdrive(path)
- while p:
- head, tail = op.split(p)
- if head == p:
- ret.append(head)
- break
- if tail:
- ret.append(tail)
- p = head
- if d:
- ret.append(d)
- return ret[::-1]
- if sys.version_info[0] < 3:
- def is_string(s):
- return isinstance(s, str) or isinstance(s, unicode)
- else:
- def is_string(s):
- return isinstance(s, str)
- def extract_exception():
- """Extract the last exception.
- Used to avoid the except ExceptionType as e, which cannot be written the
- same across supported versions. I.e::
- try:
- ...
- except Exception, e:
- ...
- becomes:
- try:
- ...
- except Exception:
- e = extract_exception()
- """
- return sys.exc_info()[1]
- # We cannot use octal literal for compat with python 3.x
- MODE_755 = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | \
- stat.S_IROTH | stat.S_IXOTH
- MODE_777 = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | \
- stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | \
- stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH
- class CommaListLexer(object):
- def __init__(self, instream):
- self._lexer = shlex.shlex(instream, posix=True)
- self._lexer.whitespace += ','
- self._lexer.wordchars += './()*-'
- self.eof = self._lexer.eof
- def get_token(self):
- return self._lexer.get_token()
- def comma_list_split(s):
- lexer = CommaListLexer(s)
- ret = []
- t = lexer.get_token()
- while t != lexer.eof:
- ret.append(t)
- t = lexer.get_token()
- return ret
- class memoized(object):
- """Decorator that caches a function's return value each time it is called.
- If called later with the same arguments, the cached value is returned, and
- not re-evaluated.
- """
- def __init__(self, func):
- self.func = func
- self.cache = {}
- def __call__(self, *args):
- try:
- return self.cache[args]
- except KeyError:
- value = self.func(*args)
- self.cache[args] = value
- return value
- except TypeError:
- # uncachable -- for instance, passing a list as an argument.
- # Better to not cache than to blow up entirely.
- return self.func(*args)
- def __repr__(self):
- """Return the function's docstring."""
- return self.func.__doc__
- def __get__(self, obj, objtype):
- """Support instance methods."""
- return partial(self.__call__, obj)
- def read_or_create_dict(filename):
- if op.exists(filename):
- fid = open(filename, "rb")
- try:
- return cPickle.load(fid)
- finally:
- fid.close()
- else:
- return {}