commit 88bddc5de5d1b7bec947a6780a5874dadd723128 Author: Damian Johnson atagar@torproject.org Date: Fri Aug 26 10:28:15 2011 -0700
Porting basic interpretor functionality to prompt
Quick hack so the panel and prompt both share the same interpretor input/response logic. This makes the arm prompt now basically functional though gonna need to do some cleanup. --- src/cli/interpretorPanel.py | 160 +++++++++---------------------------------- src/util/torInterpretor.py | 132 +++++++++++++++++++++++++++++++++++- 2 files changed, 163 insertions(+), 129 deletions(-)
diff --git a/src/cli/interpretorPanel.py b/src/cli/interpretorPanel.py index b7c3fe0..b81fa85 100644 --- a/src/cli/interpretorPanel.py +++ b/src/cli/interpretorPanel.py @@ -5,13 +5,10 @@ information, tab completion, and other usability features.
import curses
-from util import enum, panel, textInput, torTools, uiTools +from util import enum, panel, textInput, torInterpretor, torTools, uiTools
from TorCtl import TorCtl
-Formats = enum.Enum("PROMPT", "INPUT", "INPUT_INTERPRETOR", "INPUT_CMD", "INPUT_ARG", "OUTPUT", "USAGE", "HELP", "ERROR") - -PROMPT = ">>> " USAGE_INFO = "to use this panel press enter"
# limits used for cropping @@ -29,7 +26,7 @@ class InterpretorPanel(panel.Panel): # contents of the panel (oldest to newest), each line is a list of (msg, # format enum) tuples
- self.contents = [[(PROMPT, Formats.PROMPT), (USAGE_INFO, Formats.USAGE)]] + self.contents = [[(torInterpretor.PROMPT, torInterpretor.Formats.PROMPT), (USAGE_INFO, torInterpretor.Formats.USAGE)]]
def prompt(self): """ @@ -49,13 +46,32 @@ class InterpretorPanel(panel.Panel): validator = textInput.BasicValidator() validator = textInput.HistoryValidator(self.previousCommands, validator)
- xOffset = len(PROMPT) + xOffset = len(torInterpretor.PROMPT) if len(self.contents) > self.maxY - 1: xOffset += 3 # offset for scrollbar
- input = self.getstr(min(self.maxY - 1, len(self.contents)), xOffset, "", self.formats[Formats.INPUT], validator = validator) + input = self.getstr(min(self.maxY - 1, len(self.contents)), xOffset, "", self.formats[torInterpretor.Formats.INPUT], validator = validator) + input, isDone = input.strip(), False
- isDone = self.handleQuery(input) + if not input: + isDone = True + else: + self.previousCommands.insert(0, input) + self.previousCommands = self.previousCommands[:COMMAND_BACKLOG] + + try: + inputEntry, outputEntry = torInterpretor.handleQuery(input) + except torInterpretor.InterpretorClosed: + isDone = True + + promptEntry = self.contents.pop() # removes old prompt entry + self.contents += inputEntry + self.contents += outputEntry + self.contents.append(promptEntry) + + # if too long then crop lines + cropLines = len(self.contents) - LINES_BACKLOG + if cropLines > 0: self.contents = self.contents[cropLines:]
if isDone: self.isInputMode = False @@ -63,89 +79,6 @@ class InterpretorPanel(panel.Panel):
panel.CURSES_LOCK.release()
- def handleQuery(self, input): - """ - Processes the given input. Requests starting with a '/' are special - commands to the interpretor, and anything else is sent to the control port. - This returns a boolean to indicate if the interpretor should terminate or - not. - - Arguments: - input - user input to be processed - """ - - if not input or not input.strip(): return True - input = input.strip() - inputEntry, outputEntry = [(PROMPT, Formats.PROMPT)], [] - conn = torTools.getConn() - - # input falls into three general categories: - # - interpretor command which starts with a '/' - # - controller commands handled by torTools (this allows for caching, - # proper handling by the rest of arm, etc) - # - unrecognized controller command, this has the possability of confusing - # arm... - - if input.startswith("/"): - # interpretor command - inputEntry.append((input, Formats.INPUT_INTERPRETOR)) - outputEntry.append(("Not yet implemented...", Formats.ERROR)) # TODO: implement - - # TODO: add /help option - # TODO: add /write option - else: - # controller command - if " " in input: cmd, arg = input.split(" ", 1) - else: cmd, arg = input, "" - - inputEntry.append((cmd + " ", Formats.INPUT_CMD)) - if arg: inputEntry.append((arg, Formats.INPUT_ARG)) - - if cmd.upper() == "GETINFO": - try: - response = conn.getInfo(arg, suppressExc = False) - outputEntry.append((response, Formats.OUTPUT)) - except Exception, exc: - outputEntry.append((str(exc), Formats.ERROR)) - elif cmd.upper() == "SETCONF": - if "=" in arg: - param, value = arg.split("=", 1) - - try: - conn.setOption(param.strip(), value.strip()) - except Exception, exc: - outputEntry.append((str(exc), Formats.ERROR)) - else: - # TODO: resets the attribute - outputEntry.append(("Not yet implemented...", Formats.ERROR)) # TODO: implement - else: - try: - response = conn.getTorCtl().sendAndRecv("%s\r\n" % input) - - for entry in response: - # Response entries are tuples with the response code, body, and - # extra info. For instance: - # ('250', 'version=0.2.2.23-alpha (git-b85eb949b528f4d7)', None) - - if len(entry) == 3: - outputEntry.append((entry[1], Formats.OUTPUT)) - except Exception, exc: - outputEntry.append((str(exc), Formats.ERROR)) - - self.previousCommands.insert(0, input) - self.previousCommands = self.previousCommands[:COMMAND_BACKLOG] - - promptEntry = self.contents.pop() # removes old prompt entry - self.contents += _splitOnNewlines(inputEntry) - self.contents += _splitOnNewlines(outputEntry) - self.contents.append(promptEntry) - - # if too long then crop lines - cropLines = len(self.contents) - LINES_BACKLOG - if cropLines > 0: self.contents = self.contents[cropLines:] - - return False - def handleKey(self, key): # TODO: allow contents to be searched (with hilighting?)
@@ -193,40 +126,13 @@ class InterpretorPanel(panel.Panel): if drawLine >= height: break
def _initFormats(self): - self.formats[Formats.PROMPT] = curses.A_BOLD | uiTools.getColor("green") - self.formats[Formats.INPUT] = uiTools.getColor("cyan") - self.formats[Formats.INPUT_INTERPRETOR] = curses.A_BOLD | uiTools.getColor("magenta") - self.formats[Formats.INPUT_CMD] = curses.A_BOLD | uiTools.getColor("green") - self.formats[Formats.INPUT_ARG] = curses.A_BOLD | uiTools.getColor("cyan") - self.formats[Formats.OUTPUT] = uiTools.getColor("blue") - self.formats[Formats.USAGE] = uiTools.getColor("cyan") - self.formats[Formats.HELP] = uiTools.getColor("magenta") - self.formats[Formats.ERROR] = curses.A_BOLD | uiTools.getColor("red") - -def _splitOnNewlines(entry): - """ - Splits a list of (msg, format) tuples on newlines into a list of lines. - - Arguments: - entry - list of display tuples - """ - - results, tmpLine = [], [] - entry = list(entry) # shallow copy - - while entry: - msg, format = entry.pop(0) - - if "\n" in msg: - msg, remainder = msg.split("\n", 1) - entry.insert(0, (remainder, format)) - - tmpLine.append((msg, format)) - results.append(tmpLine) - tmpLine = [] - else: - tmpLine.append((msg, format)) - - if tmpLine: results.append(tmpLine) - return results + self.formats[torInterpretor.Formats.PROMPT] = curses.A_BOLD | uiTools.getColor("green") + self.formats[torInterpretor.Formats.INPUT] = uiTools.getColor("cyan") + self.formats[torInterpretor.Formats.INPUT_INTERPRETOR] = curses.A_BOLD | uiTools.getColor("magenta") + self.formats[torInterpretor.Formats.INPUT_CMD] = curses.A_BOLD | uiTools.getColor("green") + self.formats[torInterpretor.Formats.INPUT_ARG] = curses.A_BOLD | uiTools.getColor("cyan") + self.formats[torInterpretor.Formats.OUTPUT] = uiTools.getColor("blue") + self.formats[torInterpretor.Formats.USAGE] = uiTools.getColor("cyan") + self.formats[torInterpretor.Formats.HELP] = uiTools.getColor("magenta") + self.formats[torInterpretor.Formats.ERROR] = curses.A_BOLD | uiTools.getColor("red")
diff --git a/src/util/torInterpretor.py b/src/util/torInterpretor.py index 9b6bff4..866495d 100644 --- a/src/util/torInterpretor.py +++ b/src/util/torInterpretor.py @@ -6,7 +6,10 @@ directly, history and tab completion.
import readline # simply importing this provides history to raw_input
-from util import enum +from util import enum, torTools + +PROMPT = ">>> " +Formats = enum.Enum("PROMPT", "INPUT", "INPUT_INTERPRETOR", "INPUT_CMD", "INPUT_ARG", "OUTPUT", "USAGE", "HELP", "ERROR")
TERM_COLORS = ("BLACK", "RED", "GREEN", "YELLOW", "BLUE", "MAGENTA", "CYAN", "WHITE")
@@ -21,6 +24,13 @@ ATTR_ENCODING = {Attr.BOLD: "1", Attr.UNDERLINE: "4", Attr.HILIGHT: "7"} CSI = "\x1B[%sm" RESET = CSI % "0"
+class InterpretorClosed(Exception): + """ + Exception raised when the interpretor should be shut down. + """ + + pass + def format(msg, *attr): """ Simple terminal text formatting, using ANSI escape sequences from: @@ -60,7 +70,125 @@ def prompt(): prompt = format(">>> ", Color.GREEN, Attr.BOLD) input = ""
+ formatMap = {} # mapping of Format to Color and Attr enums + formatMap[Formats.PROMPT] = (Attr.BOLD, Color.GREEN) + formatMap[Formats.INPUT] = (Color.CYAN, ) + formatMap[Formats.INPUT_INTERPRETOR] = (Attr.BOLD, Color.MAGENTA) + formatMap[Formats.INPUT_CMD] = (Attr.BOLD, Color.GREEN) + formatMap[Formats.INPUT_ARG] = (Attr.BOLD, Color.CYAN) + formatMap[Formats.OUTPUT] = (Color.BLUE, ) + formatMap[Formats.USAGE] = (Color.CYAN, ) + formatMap[Formats.HELP] = (Color.MAGENTA, ) + formatMap[Formats.ERROR] = (Attr.BOLD, Color.RED) + while input != "/quit": input = raw_input(prompt) - print format("echoing back '%s'" % input, Color.BLUE) + + _, outputEntry = handleQuery(input) + + for line in outputEntry: + outputLine = "" + + for msg, msgFormat in line: + outputLine += format(msg, *formatMap[msgFormat]) + + print outputLine + +def handleQuery(input): + """ + Processes the given input. Requests starting with a '/' are special + commands to the interpretor, and anything else is sent to the control port. + This returns an input/output tuple, each entry being a list of lines, each + line having a list of (msg, format) tuples for the content to be displayed. + This raises a InterpretorClosed if the interpretor should be shut down. + + Arguments: + input - user input to be processed + """ + + input = input.strip() + inputEntry, outputEntry = [(PROMPT, Formats.PROMPT)], [] + conn = torTools.getConn() + + # input falls into three general categories: + # - interpretor command which starts with a '/' + # - controller commands handled by torTools (this allows for caching, + # proper handling by the rest of arm, etc) + # - unrecognized controller command, this has the possability of confusing + # arm... + + if input.startswith("/"): + # interpretor command + inputEntry.append((input, Formats.INPUT_INTERPRETOR)) + outputEntry.append(("Not yet implemented...", Formats.ERROR)) # TODO: implement + + # TODO: add /help option + # TODO: add /write option + else: + # controller command + if " " in input: cmd, arg = input.split(" ", 1) + else: cmd, arg = input, "" + + inputEntry.append((cmd + " ", Formats.INPUT_CMD)) + if arg: inputEntry.append((arg, Formats.INPUT_ARG)) + + if cmd.upper() == "GETINFO": + try: + response = conn.getInfo(arg, suppressExc = False) + outputEntry.append((response, Formats.OUTPUT)) + except Exception, exc: + outputEntry.append((str(exc), Formats.ERROR)) + elif cmd.upper() == "SETCONF": + if "=" in arg: + param, value = arg.split("=", 1) + + try: + conn.setOption(param.strip(), value.strip()) + except Exception, exc: + outputEntry.append((str(exc), Formats.ERROR)) + else: + # TODO: resets the attribute + outputEntry.append(("Not yet implemented...", Formats.ERROR)) # TODO: implement + else: + try: + response = conn.getTorCtl().sendAndRecv("%s\r\n" % input) + + for entry in response: + # Response entries are tuples with the response code, body, and + # extra info. For instance: + # ('250', 'version=0.2.2.23-alpha (git-b85eb949b528f4d7)', None) + + if len(entry) == 3: + outputEntry.append((entry[1], Formats.OUTPUT)) + except Exception, exc: + outputEntry.append((str(exc), Formats.ERROR)) + + return (_splitOnNewlines(inputEntry), _splitOnNewlines(outputEntry)) + +def _splitOnNewlines(entry): + """ + Splits a list of (msg, format) tuples on newlines into a list of lines. + + Arguments: + entry - list of display tuples + """ + + results, tmpLine = [], [] + entry = list(entry) # shallow copy + + while entry: + msg, format = entry.pop(0) + + if "\n" in msg: + msg, remainder = msg.split("\n", 1) + entry.insert(0, (remainder, format)) + + tmpLine.append((msg, format)) + results.append(tmpLine) + tmpLine = [] + else: + tmpLine.append((msg, format)) + + if tmpLine: results.append(tmpLine) + return results
tor-commits@lists.torproject.org