diff --git a/bin/cc-cli b/bin/cc-cli index 9a48f998d7e0cb5431983f4f0ed4032df2957293..7b01215361555d6a3137ff57c04ec06461f077cc 100755 --- a/bin/cc-cli +++ b/bin/cc-cli @@ -31,11 +31,11 @@ settings = { try: # Early debug loading if "CC_DEBUG" in os.environ: - settings["debug"] = "True" cccli.debug = True + settings["debug"] = "True" # load a printer - printer = Printer(False) + printer = Printer() # Parse line argument oparser = optparse.OptionParser(usage="usage: %prog [options] [commands]", @@ -127,12 +127,12 @@ try: raise BadArgument("Invalid %s number"%i) # check login if "login" not in settings: - printer.setinteractive() + printer.set_interactive() settings["login"] = printer.ask("Login: ") # check password if "pass" not in settings: - printer.setinteractive() + printer.set_interactive() settings["pass"] = printer.getpass("Password: ") # print settings @@ -150,7 +150,8 @@ except cliError as e: printer.error("cliError: %s"%str(e)) except Exception as e: if cccli.debug: - printer.fatal("%s: %s"%(type(e), str(e)), quit=False) + if "printer" in locals(): + printer.fatal("%s: %s"%(type(e), str(e)), quit=False) raise printer.warn("This is a not expected error, please report it!") printer.fatal(str(e)) diff --git a/cccli/cli.py b/cccli/cli.py index 9889450c4ee030557605700efc48cebc78a9265e..8d0941419f597d0b0692c88000ce35230f388246 100644 --- a/cccli/cli.py +++ b/cccli/cli.py @@ -33,24 +33,11 @@ class Cli(object): # line stuff if line: sys.stdin = StringIO.StringIO(line) + # start printer + self.printer = Printer() # set interactive mode - self.interactive = sys.stderr.isatty() and sys.stdin.isatty() - # start printer and load history - self.printer = Printer(self.interactive, - historyfile=self.settings.get("history", ""), - historysize=self.settings.get("hsize", None)) - # load alias - self.alias.load(self.settings.get("alias", "")) - # print debug - self.printer.debug("Interactive: %s"%self.interactive) - self.printer.debug("Alias: %s"%self.alias) - self.printer.debug("Loaded history: %s"%len(self.printer.history)) - # connecting - self._connect() - # auth - self._auth() - # set prompt - if self.interactive: + if sys.stderr.isatty() and sys.stdin.isatty(): + self.printer.debug("Interactive mode") self.prompt = "%s%s%s%s>%s%s%s "%(color["["], color["light"], color["]"], @@ -58,6 +45,21 @@ class Cli(object): color["["], color["reset"], color["]"]) + self.printer.set_interactive() + # load history + self.printer.history.load(self.settings.get("history", "")) + self.printer.history.maxsize(self.settings.get("hsize", None)) + self.printer.debug("Loaded history: %s"%len(self.printer.history)) + # enable completion + self.printer.completion.set_completer(self._completer) + # set prompt + # load alias + self.alias.load(self.settings.get("alias", "")) + self.printer.debug("Alias: %s"%self.alias) + # connecting + self._connect() + # auth + self._auth() # parsing self._parse() # save history @@ -114,8 +116,7 @@ class Cli(object): break except SystemExit: break - if self.interactive: - self.printer.out("tcho!") + self.printer.interactive("tcho!") def _exec_command(self, argv): '''Execute command''' @@ -154,6 +155,18 @@ class Cli(object): self.printer.error("%s: %s"%(type(e), str(e))) self.printer.warn("This is a not expected error, please report it!") + def _completer(self, texte, state): + '''Return the list of completion''' + comp = self.printer.completion + stripped = comp.get_buf()[:comp.get_begin() + 1].lstrip() + if state == 0 and texte == "" and stripped != "": + return None + cl = [ c for c in Command.list() if c.startswith(texte) ] + if state < len(cl): + return cl[state] + return None + + class CliHandler(RpcHandler): '''Handle RPC incoming request''' diff --git a/cccli/printer.py b/cccli/printer.py index 0d9c2206bbdf279de8bb0378a59364eefa621ca8..c70221f11e059910b9f07c47feedcba2b8c46c97 100644 --- a/cccli/printer.py +++ b/cccli/printer.py @@ -48,19 +48,13 @@ color = { class Printer(object): '''Print relative class''' - def __init__(self, interactive=False, historyfile=None, historysize=None): + def __init__(self): self.readline = None self.history = History() - self.historyfile = historyfile - self.historysize = historysize - if interactive: - self.setinteractive() - - def isinteractive(self): - '''Return if printer is in interactive mode''' - return self.readline is not None + self.completion = Completion() - def setinteractive(self): + def set_interactive(self): + '''Set interactive mode''' if self.readline is not None: return try: @@ -70,12 +64,8 @@ class Printer(object): self.readline = readline # enable history self.history.readline = readline - # load history - self.history.read(self.historyfile) - self.history.maxsize(self.historysize) - # start prompt completer - self.readline.set_completer(self.completer) - self.readline.parse_and_bind("tab: complete") + # enable completion + self.completion.readline = readline def out(self, message="", fd=sys.stdout, nl=os.linesep, flush=True): '''Print a message in fd ended by nl''' @@ -107,6 +97,11 @@ class Printer(object): if cccli.debug: self.out(message, fd, nl) + def interactive(self, message, fd=sys.stderr, nl=os.linesep): + '''Print only in interactive mode''' + if self.readline is not None: + self.out(message, fd, nl) + def getline(self, prompt, history=True): '''Read a line from stdin''' try: @@ -157,15 +152,6 @@ class Printer(object): rows, cols, px_x, px_y = struct.unpack("HHHH", resp) return rows, cols - def completer(self, texte, state): - r = self.readline - if state == 0 and texte == "" and r.get_line_buffer()[:r.get_begidx() + 1].lstrip() != "": - return None - cl = [ c for c in Command.list() if c.startswith(texte) ] - if state < len(cl): - return cl[state] - return None - class History(object): '''History class''' @@ -176,12 +162,12 @@ class History(object): return self.readline is not None def __getattribute__(self, name): - r = object.__getattribute__(self, "readline") - if name == "readline": - return r - if r is None: - return lambda *a,**k: None - return object.__getattribute__(self, name) + r = object.__getattribute__(self, "readline") + if name == "readline": + return r + if r is None: + return lambda *a,**k: None + return object.__getattribute__(self, name) def __iter__(self): if self.readline is None: @@ -229,6 +215,33 @@ class History(object): '''Clear history''' self.readline.clear_history() -# Need to be at end of file to fix cross loading of printer by command and command by printer -# So i maybe should change the design of import -from cccli.command import Command + +class Completion(object): + '''Handle completion functions''' + def __init__(self): + self.readline = None + + def __getattribute__(self, name): + r = object.__getattribute__(self, "readline") + if name == "readline": + return r + if r is None: + return lambda *a,**k: None + return object.__getattribute__(self, name) + + def get_buf(self): + '''Return current readline buffer''' + return self.readline.get_line_buffer() + + def get_begin(self): + '''Get the beginning index of the readline tab-completion scope''' + return self.readline.get_begidx() + + def get_end(self): + '''Get the ending index of the readline tab-completion scope''' + return self.readline.get_begidx() + + def set_completer(self, func): + '''Set completer function''' + self.readline.set_completer(func) + self.readline.parse_and_bind("tab: complete")