import sys from datetime import datetime from threading import Lock, Thread from time import sleep class Term: spinner = ["/", "-", "\\", "|", "/", "-", "\\", "|"] logfile = f"logs/{datetime.now().isoformat()}.log" term = sys.stdout _th: Thread _process_name = None _process_state = None _process_spin = False _print_lock = Lock() _logfile = None @staticmethod def begin(): Term._th = Thread(target=Term._term_thread) Term._th.daemon = True Term._th.start() Term._logfile = open(Term.logfile, "a") @staticmethod def _term_thread(): spin_i = 0 while True: sleep(0.2) spin_i += 1 with Term._print_lock: if Term._process_name is not None: Term.mov_ls() Term.write("\u001b[36m[") Term.write(Term.spinner[spin_i % len(Term.spinner)] if Term._process_spin else " ") Term.write(f"] {Term._process_name}") if Term._process_state is not None: Term.write(f"\u001b[35m - {Term._process_state}") Term.write("\u001b[0m") Term.flush() @staticmethod def flush(): Term.term.flush() @staticmethod def write(s: str): Term.term.write(s) @staticmethod def mov_ls(): Term.write("\u001b[2K\r") @staticmethod def newln(): Term.write("\n") @staticmethod def log(logmsg: str, display=True): logmsg = str(logmsg) with Term._print_lock: Term._logfile.write(logmsg + "\n") Term._logfile.flush() if not display: return Term.mov_ls() if Term._process_name is not None: Term.write(" ") Term.write(logmsg) Term.newln() Term.flush() @staticmethod def proc_start(procname: str, spin: bool = False): with Term._print_lock: Term._logfile.write(f"start [{procname}]\n") Term._logfile.flush() if Term._process_name is not None: Term.proc_end("New process started (old may or may no actually have ended)", result=1) if spin: Term._process_spin = True Term._process_name = procname @staticmethod def proc_state(state: str): with Term._print_lock: Term._logfile.write(f"[{Term._process_name}] {state}\n") Term._logfile.flush() if Term._process_name is not None: Term._process_state = state else: Term.log(f"State for unknown process: {state}") @staticmethod def proc_end(msg=None, result=0): """ Result 0=success, 1=error, 2=warning. """ colour = ["\u001b[32m", "\u001b[31m", "\u001b[33m"] with Term._print_lock: if Term._process_name is None: raise Exception("No process to end...") Term._logfile.write(f"end [{Term._process_name}] {msg}\n") Term._logfile.flush() Term.mov_ls() Term.write(colour[result]) symb = " " if result == 0: symb = "+" if result == 1: symb = "-" if result == 2: symb = "*" Term.write(f"[{symb}] ") Term.write("\u001b[0m") Term.write(Term._process_name) if msg is not None: Term.write(f" - {msg}") Term.write("\u001b[0m") Term.newln() Term.flush() Term._process_name = None Term._process_state = None Term._process_spin = False @staticmethod def proc_spin(): Term._process_spin = True @staticmethod def proc_nospin(): Term._process_spin = False if __name__ == "__main__": Term.begin() sleep(2) Term.proc_start("TEST") Term.log("LGOGGGG") sleep(2) Term.proc_spin() Term.log("LGOGGGG")