commit c8d21d593887bb537c144b71f15c7ed38f1880ed
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon Aug 29 09:50:15 2011 -0700
Class and formatting interpretor refactoring
This moves around the interpretor code a bit to allow for arbitrary attribute
drawing and introduce an interpretor class (needed for when implementing the
'/write' option).
---
src/cli/interpretorPanel.py | 41 +++++----
src/util/torInterpretor.py | 191 ++++++++++++++++++++++---------------------
2 files changed, 121 insertions(+), 111 deletions(-)
diff --git a/src/cli/interpretorPanel.py b/src/cli/interpretorPanel.py
index 95866db..02868ce 100644
--- a/src/cli/interpretorPanel.py
+++ b/src/cli/interpretorPanel.py
@@ -8,7 +8,7 @@ import curses
from util import panel, textInput, torInterpretor, uiTools
USAGE_INFO = "to use this panel press enter"
-PROMPT_LINE = [(torInterpretor.PROMPT, torInterpretor.Formats.PROMPT), (USAGE_INFO, torInterpretor.Formats.USAGE)]
+PROMPT_LINE = [torInterpretor.PROMPT, (USAGE_INFO, torInterpretor.USAGE_FORMAT)]
# limits used for cropping
BACKLOG_LIMIT = 100
@@ -17,34 +17,37 @@ LINES_LIMIT = 2000
# lazy loaded curses formatting constants
FORMATS = {}
-def getFormat(format):
+def getFormat(formatAttr):
"""
- Provides the curses drawing attributes for a torInterpretor.Formats enum.
- This returns plain formatting if the entry doesn't exist.
+ Provides the curses drawing attributes for torInterpretor formatting
+ attributes.
Arguments:
- format - format enum to fetch
+ formatAttr - list of formatting attributes
"""
# initializes formats if they haven't yet been loaded
if not FORMATS:
- FORMATS[torInterpretor.Formats.PROMPT] = curses.A_BOLD | uiTools.getColor("green")
- FORMATS[torInterpretor.Formats.INPUT] = uiTools.getColor("cyan")
- FORMATS[torInterpretor.Formats.INPUT_INTERPRETOR] = curses.A_BOLD | uiTools.getColor("magenta")
- FORMATS[torInterpretor.Formats.INPUT_CMD] = curses.A_BOLD | uiTools.getColor("green")
- FORMATS[torInterpretor.Formats.INPUT_ARG] = curses.A_BOLD | uiTools.getColor("cyan")
- FORMATS[torInterpretor.Formats.OUTPUT] = uiTools.getColor("blue")
- FORMATS[torInterpretor.Formats.USAGE] = uiTools.getColor("cyan")
- FORMATS[torInterpretor.Formats.HELP] = uiTools.getColor("magenta")
- FORMATS[torInterpretor.Formats.ERROR] = curses.A_BOLD | uiTools.getColor("red")
+ for colorEnum in torInterpretor.Color.values():
+ FORMATS[colorEnum] = uiTools.getColor(colorEnum.lower())
+
+ FORMATS[torInterpretor.Attr.BOLD] = curses.A_BOLD
+ FORMATS[torInterpretor.Attr.UNDERLINE] = curses.A_UNDERLINE
+ FORMATS[torInterpretor.Attr.HILIGHT] = curses.A_STANDOUT
+
+ cursesFormatting = curses.A_NORMAL
+
+ for attr in formatAttr:
+ cursesFormatting |= FORMATS.get(attr, curses.A_NORMAL)
- return FORMATS.get(format, curses.A_NORMAL)
+ return cursesFormatting
class InterpretorPanel(panel.Panel):
def __init__(self, stdscr):
panel.Panel.__init__(self, stdscr, "interpretor", 0)
self.isInputMode = False
self.scroll = 0
+ self.interpretor = torInterpretor.ControlInterpretor()
self.previousCommands = [] # user input, newest to oldest
self.contents = [PROMPT_LINE] # (msg, format enum) tuples being displayed (oldest to newest)
@@ -61,18 +64,18 @@ class InterpretorPanel(panel.Panel):
self.redraw(True)
# intercepts input so user can cycle through the history
- torCommands = torInterpretor.TorCommandOptions()
+ torCommands = torInterpretor.TorControlCompleter()
validator = textInput.BasicValidator()
validator = textInput.HistoryValidator(self.previousCommands, validator)
validator = textInput.TabCompleter(torCommands.getMatches, validator)
- xOffset = len(torInterpretor.PROMPT)
+ xOffset = len(torInterpretor.PROMPT[0])
if len(self.contents) > self.maxY - 1:
xOffset += 3 # offset for scrollbar
inputLine = min(self.maxY - 1, len(self.contents))
- inputFormat = getFormat(torInterpretor.Formats.INPUT)
+ inputFormat = getFormat(torInterpretor.INPUT_FORMAT)
input = self.getstr(inputLine, xOffset, "", inputFormat, validator = validator)
input, isDone = input.strip(), False
@@ -84,7 +87,7 @@ class InterpretorPanel(panel.Panel):
self.previousCommands = self.previousCommands[:BACKLOG_LIMIT]
try:
- inputEntry, outputEntry = torInterpretor.handleQuery(input)
+ inputEntry, outputEntry = self.interpretor.handleQuery(input)
except torInterpretor.InterpretorClosed:
isDone = True
diff --git a/src/util/torInterpretor.py b/src/util/torInterpretor.py
index cffe200..c7bcebb 100644
--- a/src/util/torInterpretor.py
+++ b/src/util/torInterpretor.py
@@ -8,9 +8,6 @@ import readline # simply importing this provides history to raw_input
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")
Color = enum.Enum(*TERM_COLORS)
@@ -21,6 +18,16 @@ FG_ENCODING = dict([(Color.values()[i], str(30 + i)) for i in range(8)])
BG_ENCODING = dict([(BgColor.values()[i], str(40 + i)) for i in range(8)])
ATTR_ENCODING = {Attr.BOLD: "1", Attr.UNDERLINE: "4", Attr.HILIGHT: "7"}
+PROMPT = (">>> ", (Attr.BOLD, Color.GREEN))
+INPUT_FORMAT = (Color.CYAN, )
+INPUT_INTERPRETOR_FORMAT = (Attr.BOLD, Color.MAGENTA)
+INPUT_CMD_FORMAT = (Attr.BOLD, Color.GREEN)
+INPUT_ARG_FORMAT = (Attr.BOLD, Color.CYAN)
+OUTPUT_FORMAT = (Color.BLUE, )
+USAGE_FORMAT = (Color.CYAN, )
+HELP_FORMAT = (Color.MAGENTA, )
+ERROR_FORMAT = (Attr.BOLD, Color.RED)
+
CSI = "\x1B[%sm"
RESET = CSI % "0"
@@ -64,7 +71,7 @@ def format(msg, *attr):
return (CSI % ";".join(encodings)) + msg + RESET
else: return msg
-class TorCommandOptions:
+class TorControlCompleter:
"""
Command autocompleter, fetching the valid options from the attached Tor
instance.
@@ -169,12 +176,96 @@ class TorCommandOptions:
if not state: return cmd
else: state -= 1
+class ControlInterpretor:
+ """
+ Interpretor that handles queries to the control port, providing usability
+ imporvements like irc style help optoins. This tracks input and responses.
+ """
+
+ def __init__(self):
+ self.queries = [] # requests made, newest to oldest
+ self.contents = [] # (msg, format list) tuples of both input and output (oldest to newest)
+
+ 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 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], []
+ 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, INPUT_INTERPRETOR_FORMAT))
+ outputEntry.append(("Not yet implemented...", ERROR_FORMAT)) # 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, ""
+
+ # makes commands uppercase to match the spec
+ cmd = cmd.upper()
+
+ inputEntry.append((cmd + " ", INPUT_CMD_FORMAT))
+ if arg: inputEntry.append((arg, INPUT_ARG_FORMAT))
+
+ if cmd == "GETINFO":
+ try:
+ response = conn.getInfo(arg, suppressExc = False)
+ outputEntry.append((response, OUTPUT_FORMAT))
+ except Exception, exc:
+ outputEntry.append((str(exc), ERROR_FORMAT))
+ elif cmd == "SETCONF":
+ if "=" in arg:
+ param, value = arg.split("=", 1)
+
+ try:
+ conn.setOption(param.strip(), value.strip())
+ except Exception, exc:
+ outputEntry.append((str(exc), ERROR_FORMAT))
+ else:
+ # TODO: resets the attribute
+ outputEntry.append(("Not yet implemented...", ERROR_FORMAT)) # 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], OUTPUT_FORMAT))
+ except Exception, exc:
+ outputEntry.append((str(exc), ERROR_FORMAT))
+
+ return (_splitOnNewlines(inputEntry), _splitOnNewlines(outputEntry))
+
def prompt():
prompt = format(">>> ", Color.GREEN, Attr.BOLD)
input = ""
# sets up tab autocompetion
- torCommands = TorCommandOptions()
+ torCommands = TorControlCompleter()
readline.parse_and_bind("tab: complete")
readline.set_completer(torCommands.complete)
@@ -184,17 +275,7 @@ def prompt():
# command by itself.
readline.set_completer_delims("\n")
-
- 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)
+ interpretor = ControlInterpretor()
while input != "/quit":
try:
@@ -205,90 +286,16 @@ def prompt():
print
break
- _, outputEntry = handleQuery(input)
+ _, outputEntry = interpretor.handleQuery(input)
for line in outputEntry:
outputLine = ""
for msg, msgFormat in line:
- outputLine += format(msg, *formatMap[msgFormat])
+ outputLine += format(msg, *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, ""
-
- # makes commands uppercase to match the spec
- cmd = cmd.upper()
-
- inputEntry.append((cmd + " ", Formats.INPUT_CMD))
- if arg: inputEntry.append((arg, Formats.INPUT_ARG))
-
- if cmd == "GETINFO":
- try:
- response = conn.getInfo(arg, suppressExc = False)
- outputEntry.append((response, Formats.OUTPUT))
- except Exception, exc:
- outputEntry.append((str(exc), Formats.ERROR))
- elif cmd == "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.