class prefix: info = (" ", "INFO") debug = (" §b?§/", "§bDEBG§/") start = ("§B>>>§/", "§BEXEC§/") exec = start warn = (" §y#§/", "§yWARN§/") fail = ("§r!!!§/", "§rFAIL§/") done = (" §g*§/", "§gDONE§/") colortags = { "§r": "\x1b[31;01m", "§g": "\x1b[32;01m", "§y": "\x1b[33;01m", "§b": "\x1b[34;01m", "§m": "\x1b[35;01m", "§c": "\x1b[36;01m", "§B": "\x1b[01m", "§/": "\x1b[39;49;00m" } def render(text, colors=True): """ Processes text with color tags and either removes them (with colors=False) or replaces them with terminal color codes. """ text = str(text) if colors: tags = re.findall('§[^§]', text) for tag in tags: try: text = text.replace(tag, colortags[tag]) except KeyError: pass else: text = re.sub('§[^§]', '', text) # unescape actual paragraphs text = re.sub('§§', '§', text) return text def char_length(string): """ Returns the length of a string minus all formatting tags. """ plain_string = render(string, colors=False) return len(plain_string) class Output: """ Text UI output renderer. Prints messages to the stdout with optional eye candy like colors and timestamps. Usage: >>> from over import Output, prefix >>> say = Output("test", timestamp=True) >>> say("system initialized") [2013-02-28 16:41:28] INFO -- test, system initialized >>> say("system is FUBAR", prefix.fail) [2013-02-28 16:41:46] FAIL -- test, system is FUBAR >>> say("I just realized this will not work", prefix.fail, timestamp=False) !!! I just realized this will not work TODO initialize with target output streams (which only need to implement .write) """ def __init__(self, name, default_suffix="\n", timestamp=False, colors=True, default_prefix=prefix.info, tb=False): self.name = name self.timestamp = timestamp self.colors = colors self.default_prefix = default_prefix self.default_suffix = default_suffix self.tb = tb def __call__(self, text, prefix=None, suffix=None, indent=0, timestamp=None, colors=None, display_name=True, exc=None, tb=None): if prefix is None: prefix = self.default_prefix if type(prefix) is str: prefix = (prefix, prefix) if suffix is None: suffix = self.default_suffix if timestamp is None: timestamp = self.timestamp if colors is None: colors = self.colors if tb is None: tb = self.tb output = [] # [2012-11-11 16:52:06] INFO -- ahoj if timestamp: output.append(time.strftime('[%Y-%m-%d %H:%M:%S] ')) output.append(prefix[1]) output.append(" -- ") elif prefix: output.append(prefix[0]) output.append(" ") if display_name and self.name: output.append("%s, " %(self.name)) #output.append(paragraph(str(text), indent=indent)) output.append(str(text)) if suffix: output.append(suffix) output = "".join(output) sys.stdout.write(render(output, colors)) sys.stdout.flush() if exc: if exc is True: if tb: raise else: if sys.exc_info()[0]: self.__call__("unhandled exception %s raised" %(sys.exc_info()[0].__name__), timestamp=True) #sys.exit(1) raise else: if tb: raise exc(render(text, colors=False)) else: self.__call__("unhandled exception %s raised" %(exc.__name__), timestamp=True) raise exc #sys.exit(1) def get_terminal_size(): """ Returns current terminal's (rows, cols). """ terminal = sys.stdout.fileno() try: return struct.unpack("HHHH", fcntl.ioctl(terminal, termios.TIOCGWINSZ, struct.pack("HHHH", 0, 0, 0, 0))) except IOError: return (40, 80)