import shlex import inspect import sys import importlib import logging from nullptr.util import AppError def func_supports_argcount(f, cnt): argspec = inspect.getargspec(f) posargs = 0 if argspec.args is None else len(argspec.args) if argspec.args[0] == 'self': posargs = posargs - 1 defargs = 0 if argspec.defaults is None else len(argspec.defaults) varargs = 0 if argspec.varargs is None else len(argspec.varargs) minargs = posargs - defargs maxargs = posargs if cnt < minargs: return False if cnt > maxargs: return varargs > 0 return True class CommandLine: def __init__(self): self.reloading = False self.stopping = False def ask_multichoice(self, choices, prompt): choice = None while choice not in choices: choice = input(prompt).lower() return choice def stop(self): self.stopping = True def prompt(self): return '> ' def handle_not_found(self, c, args): print(f'command not found; {c}') def handle_error(self, cmd, args, e): logging.error(e, exc_info=not issubclass(type(e), AppError)) def handle_empty(self): pass def after_cmd(self): pass def handle_eof(self): print('EOF') def do_quit(self): print('byebye!') self.stopping = True def do_reload(self): self.reloading = True def handle_cmd(self, c): if c == '': return self.handle_empty() args = shlex.split(c) cmd = args.pop(0) str_handler = f'do_{cmd}' if not hasattr(self, str_handler): try: self.handle_not_found(cmd, args) return except Exception as e: self.handle_error(cmd, args, e) handler = getattr(self, str_handler) if not func_supports_argcount(handler, len(args)): expect_args = ', '.join(inspect.getargspec(handler).args[1:]) print('expected args: ' + expect_args) return try: handler(*args) except Exception as e: self.handle_error(cmd, args, e) self.after_cmd() def run(self): while not self.stopping and not self.reloading: p = self.prompt() try: c = input(p) except (EOFError, KeyboardInterrupt): self.handle_eof() break try: self.handle_cmd(c) except KeyboardInterrupt: print("Interrupted") except (Exception) as e: logging.error(e, exc_info=True)