# -*- coding: utf-8 -*- """ Helper functions for writing to terminals and files. XXX: Currently in progress, NOT IN WORKING STATE. """ import sys, os def _getdimensions(): import termios,fcntl,struct call = fcntl.ioctl(0,termios.TIOCGWINSZ,"\000"*8) height,width = struct.unpack( "hhhh", call ) [:2] return height, width def get_terminal_width(): try: height, width = _getdimensions() except (SystemExit, KeyboardInterrupt): raise except: # FALLBACK width = int(os.environ.get('COLUMNS', 80))-1 # XXX the windows getdimensions may be bogus, let's sanify a bit width = max(width, 40) # we alaways need 40 chars return width terminal_width = get_terminal_width() # XXX unify with _escaped func below def ansi_print(text, file=None, newline=True, flush=False): if file is None: file = sys.stderr text = text.strip() if newline: text += '\n' file.write(text) if flush: file.flush() if file: file.close() def should_do_markup(file): return hasattr(file, 'isatty') and file.isatty() \ and os.environ.get('TERM') != 'dumb' class TerminalWriter(object): _esctable = dict(black=30, red=31, green=32, yellow=33, blue=34, purple=35, cyan=36, white=37, Black=40, Red=41, Green=42, Yellow=43, Blue=44, Purple=45, Cyan=46, White=47, bold=1, light=2, blink=5, invert=7) def __init__(self, file=None, encoding=None): self.encoding = encoding if file is None: file = sys.stdout elif hasattr(file, '__call__'): file = WriteFile(file, encoding=encoding) self._file = file self.fullwidth = get_terminal_width() self.hasmarkup = should_do_markup(file) def _escaped(self, text, esc): if esc and self.hasmarkup: text = (''.join(['\x1b[%sm' % cod for cod in esc]) + text +'\x1b[0m') return text def markup(self, text, **kw): esc = [] for name in kw: if name not in self._esctable: raise ValueError("unknown markup: %r" %(name,)) if kw[name]: esc.append(self._esctable[name]) return self._escaped(text, tuple(esc)) def sep(self, sepchar, title=None, fullwidth=None, **kw): if fullwidth is None: fullwidth = self.fullwidth # the goal is to have the line be as long as possible # under the condition that len(line) <= fullwidth if title is not None: # we want 2 + 2*len(fill) + len(title) <= fullwidth # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth # 2*len(sepchar)*N <= fullwidth - len(title) - 2 # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) N = (fullwidth - len(title) - 2) // (2*len(sepchar)) fill = sepchar * N line = "%s %s %s" % (fill, title, fill) else: # we want len(sepchar)*N <= fullwidth # i.e. N <= fullwidth // len(sepchar) line = sepchar * (fullwidth // len(sepchar)) # in some situations there is room for an extra sepchar at the right, # in particular if we consider that with a sepchar like "_ " the # trailing space is not important at the end of the line if len(line) + len(sepchar.rstrip()) <= fullwidth: line += sepchar.rstrip() self.line(line, **kw) def write(self, s, **kw): if s: s = self._getbytestring(s) if self.hasmarkup and kw: s = self.markup(s, **kw) self._file.write(s) self._file.flush() def _getbytestring(self, s): # XXX review this and the whole logic if self.encoding and sys.version_info < (3,0) and isinstance(s, unicode): return s.encode(self.encoding) elif not isinstance(s, str): return str(s) return s def line(self, s='', **kw): self.write(s, **kw) self.write('\n') class WriteFile(object): def __init__(self, writemethod, encoding=None): self.encoding = encoding self._writemethod = writemethod def write(self, data): if self.encoding: data = data.encode(self.encoding) self._writemethod(data) def flush(self): return