import os import pwd import re import regex from subprocess import Popen, PIPE, STDOUT # character buffers use sequences like [r:d]foo[d:d] to indicate that foo # should be fg=red,bg=default. as such, we have to escape \, [ and ]. cbuf_re = re.compile(r'[\[\]\\]') def cbuf_escape(s): '''escape characters which have special meaning in color buffers''' return cbuf_re.sub(lambda m: '\\' + m.group(0), s) def flatzip(a, b): '''interleave two lists, e.g. ((a,b),(1,2)) -> (a,1,b,2)''' l = [] for x, y in zip(a, b): l.append(x) l.append(y) return l def should_ignore_path(path, suffixes): '''whether file (or any parent) should be ignored based on suffixes ''' for name in path.split('/'): for suffix in suffixes: if name.endswith(suffix): return True return False def literal_path(path): '''return the system path for the given user path''' path = os.path.realpath(expand_tilde(path)) return os.path.abspath(path) def normal_path(path): '''return the user path for the given system path ''' path = os.path.normpath(path) home = os.getenv('HOME') isdir = os.path.isdir(path) if path.startswith(home): path = path.replace(home, '~', 1) if isdir and not path.endswith('/'): path += '/' return path def expand_tilde(path): '''correctly expand the tilde in the provided path''' isdir = path.endswith('/') if not path.startswith('~'): return path parts = path.split('/', 1) if parts[0] == '~': parts[0] = os.getenv('HOME') elif parts[0].startswith('~'): try: parts[0] = pwd.getpwnam(parts[0][1:])[5] except KeyError: pass if len(parts) > 1: s = os.path.join(parts[0], parts[1]) else: s = parts[0] s = os.path.realpath(s) if os.path.isdir(s) and isdir: s += '/' return s def count_leading_whitespace(s): '''return the amount of leading whitespace of the provided string''' m = regex.leading_whitespace.match(s) assert m, "count leading whitespace failed somehow" return m.end() - m.start() def make_name(name, testf): '''generate a new name, using testf() to see which names are taken''' if testf(name): i = 1 auxname = '%s/%d' % (name, i) while testf(auxname): i += 1 auxname = '%s/%d' % (name, i) name = auxname return name # TODO: move these somewhere more sensible. they aren't really utilities. def get_margin_limit(w, def_limit=80): lname = '%s.margin' % w.mode.name.lower() if lname in w.application.config: return w.application.config[lname] else: return w.application.config.get('margin', def_limit) def get_margin_color(w, def_color='blue'): lname = '%s.margin_color' % w.mode.name.lower() if lname in w.application.config: return w.application.config[lname] else: return w.application.config.get('margin_color', def_color) # emulate defaultdict for 2.4 try: from collections import defaultdict except: class defaultdict(dict): def __init__(self, default_factory=None, *a, **kw): if (default_factory is not None and not hasattr(default_factory, '__call__')): raise TypeError('first argument must be callable') dict.__init__(self, *a, **kw) self.default_factory = default_factory def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: return self.__missing__(key) def __missing__(self, key): if self.default_factory is None: raise KeyError(key) self[key] = value = self.default_factory() return value def __reduce__(self): if self.default_factory is None: args = tuple() else: args = self.default_factory, return type(self), args, None, None, list(self.items()) def copy(self): return self.__copy__() def __copy__(self): return type(self)(self.default_factory, self) def __deepcopy__(self, memo): import copy return type(self)(self.default_factory, copy.deepcopy(list(self.items()))) def __repr__(self): return 'defaultdict(%s, %s)' % (self.default_factory, dict.__repr__(self)) def decode(s): for coding in ('utf-8', 'latin-1'): try: return s.decode(coding) except: pass return s.decode('ascii', 'replace') def communicate(cmd, stdin=None, stderr=True, shell=False): if stderr: pipe = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=shell) else: pipe = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=shell) out, err = pipe.communicate(input=stdin) result = pipe.wait() return result, decode(out or ''), decode(err or '') def wrap_lines(s, limit): '''given a string, return an iterator of wrapped lines''' i = 0 while i + limit < len(s): j = limit while j > 0 and s[i + j] != ' ': j -= 1 if j == 0: yield s[i:i + limit] i += limit else: yield s[i:i + j] i += j + 1 if s[i:]: yield s[i:]