[tor-commits] [arm/release] Porting basic interpretor functionality to prompt

atagar at torproject.org atagar at torproject.org
Sun Sep 25 21:38:26 UTC 2011


commit 88bddc5de5d1b7bec947a6780a5874dadd723128
Author: Damian Johnson <atagar at 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
 





More information about the tor-commits mailing list