#!/usr/bin/env python import sys,os from datetime import datetime from datetime import timedelta import re,signal import subprocess VERSION=1 W= '30' R= '31' G= '32' Y= '33' B= '34' M= '35' C= '36' S= '1' E= '0' BR= '41' CLR = '\033[2J' SAVE = '\033[s' LOAD = '\033[u' CLRLN = '\033[K' CLRBLN = '\033[1K' DOWN = '\033[1B' SORTCONVERT = lambda text: float(text) if is_number(text) else -1 SORTKEY = lambda key: SORTCONVERT(key[2][:-1]) def setup_options(): ''' Setup the command line options ''' from argparse import ArgumentParser import argparse parser=ArgumentParser(description=''' Tool to clean up and colorize the output of Anduril. Example: anduril run yourscript.and | %(prog)s You can tap in to an existing log with: tail -f -n +0 log/_global | %(prog)s''',formatter_class=argparse.RawTextHelpFormatter) parser.add_argument("--no-colors",'--nc',action="store_false",dest="colors",default=True, help="Disable colored output") parser.add_argument("--version",action='version', version=VERSION) options=parser.parse_args() return options def c(attribs): ''' ANSI colorizer ''' return '\033['+';'.join(attribs)+'m' def pos(y,x): ''' ANSI absolute position set ''' return "\033["+str(y)+";"+str(x)+"H" color_match={#'line_ends':(re.compile('$'),c.END), 'err':(re.compile('(Failed)'),c([R,S])+'\\1'+c([E])), 'done':(re.compile('(Done)'),c([G,S])+'\\1'+c([E])), 'percent':(re.compile('([0-9]+%)'),c([Y,S])+'\\1'+c([E])), } def colorize(string): ''' colorizes a string based on color_match ''' if not options.colors: return string for c in color_match: string=color_match[c][0].sub(color_match[c][1],string) return string def count_running(string, stats): ''' Counts the running executions ''' spl=[i.strip() for i in string.split('|')] if len(spl)!=4: return stats spl.append(datetime.now()) if spl[3] in stats['files']: index=stats['files'].index(spl[3]) stats['running'][index]=spl else: stats['running'].append(spl) stats['running'].sort(key=SORTKEY, reverse=True) stats['files']=[i[3] for i in stats['running']] return stats class EndProgram( Exception ): ''' Nice way of exiting the program ''' pass def remove_running(stats): ''' Remove Done files ''' if not stats['running']: return stats # Remove Done/Fail older than 60sec for e in enumerate(stats['running']): if (datetime.now() - e[1][4] > timedelta(seconds=60)): if e[1][2]=='Done' or e[1][2]=='Failed': stats['running'].pop(e[0]) stats['files'].pop(e[0]) # Remove Done/Fail if there are too many: if len(stats['running'])>(stats['size'][0]-3): for e in enumerate(stats['running']): if e[1][2]=='Done': stats['running'].pop(e[0]) stats['files'].pop(e[0]) return stats if e[1][2]=='Failed': stats['running'].pop(e[0]) stats['files'].pop(e[0]) return stats return stats def is_number(s): ''' Check if string is float ''' try: out=float(s) return True except: return False def str_short(s,stats): ''' shorten text to fit screen ''' maxL=stats['size'][1] - 16 if len(s)