Rosetta
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
interactive_terminal.py
Go to the documentation of this file.
1 """
2 This is a modified version of source code from the Accerciser project
3 (http://live.gnome.org/accerciser).
4 
5 Backend to the console plugin.
6 
7 @author: Eitan Isaacson
8 @organization: IBM Corporation
9 @copyright: Copyright (c) 2007 IBM Corporation
10 @license: BSD
11 
12 All rights reserved. This program and the accompanying materials are made
13 available under the terms of the BSD which accompanies this distribution, and
14 is available at U{http://www.opensource.org/licenses/bsd-license.php}
15 """
16 
17 import re
18 import sys
19 import os
20 from StringIO import StringIO
21 import Tkinter
22 import IPython
23 
24 #Works by itself, but not able to import it into the GUI at this time.
26  def __init__(self,argv=None,user_ns=None,user_global_ns=None,
27  cin=None, cout=None,cerr=None, input_func=None):
28  if input_func:
29  IPython.iplib.raw_input_original = input_func
30  if cin:
31  IPython.Shell.Term.cin = cin
32  if cout:
33  IPython.Shell.Term.cout = cout
34  if cerr:
35  IPython.Shell.Term.cerr = cerr
36 
37  if argv is None:
38  argv=[]
39 
40  # This is to get rid of the blockage that occurs during
41  # IPython.Shell.InteractiveShell.user_setup()
42  IPython.iplib.raw_input = lambda x: None
43 
44  self.term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
45  os.environ['TERM'] = 'dumb'
46  excepthook = sys.excepthook
47  self.IP = IPython.Shell.make_IPython(argv,user_ns=user_ns,
48  user_global_ns=user_global_ns,
49  embedded=True,
50  shell_class=IPython.Shell.InteractiveShell)
51  self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd),
52  header='IPython system call: ',
53  verbose=self.IP.rc.system_verbose)
54  sys.excepthook = excepthook
55  self.iter_more = 0
56  self.history_level = 0
57  self.complete_sep = re.compile('[\s\{\}\[\]\(\)]')
58 
59  def execute(self):
60  self.history_level = 0
61  orig_stdout = sys.stdout
62  sys.stdout = IPython.Shell.Term.cout
63  try:
64  line = self.IP.raw_input(None, self.iter_more)
65  if self.IP.autoindent:
66  self.IP.readline_startup_hook(None)
67  except KeyboardInterrupt:
68  self.IP.write('\nKeyboardInterrupt\n')
69  self.IP.resetbuffer()
70  # keep cache in sync with the prompt counter:
71  self.IP.outputcache.prompt_count -= 1
72 
73  if self.IP.autoindent:
74  self.IP.indent_current_nsp = 0
75  self.iter_more = 0
76  except:
77  self.IP.showtraceback()
78  else:
79  self.iter_more = self.IP.push(line)
80  if (self.IP.SyntaxTB.last_syntax_error and
81  self.IP.rc.autoedit_syntax):
82  self.IP.edit_syntax_error()
83  if self.iter_more:
84  self.prompt = str(self.IP.outputcache.prompt2).strip()
85  if self.IP.autoindent:
86  self.IP.readline_startup_hook(self.IP.pre_readline)
87  else:
88  self.prompt = str(self.IP.outputcache.prompt1).strip()
89  sys.stdout = orig_stdout
90 
91  def historyBack(self):
92  self.history_level -= 1
93  return self._getHistory()
94 
95  def historyForward(self):
96  self.history_level += 1
97  return self._getHistory()
98 
99  def _getHistory(self):
100  try:
101  rv = self.IP.user_ns['In'][self.history_level].strip('\n')
102  except IndexError:
103  self.history_level = 0
104  rv = ''
105  return rv
106 
107  def updateNamespace(self, ns_dict):
108  self.IP.user_ns.update(ns_dict)
109 
110  def complete(self, line):
111  split_line = self.complete_sep.split(line)
112  possibilities = self.IP.complete(split_line[-1])
113  if possibilities:
114  common_prefix = reduce(self._commonPrefix, possibilities)
115  completed = line[:-len(split_line[-1])]+common_prefix
116  else:
117  completed = line
118  return completed, possibilities
119 
120  def _commonPrefix(self, str1, str2):
121  for i in range(len(str1)):
122  if not str2.startswith(str1[:i+1]):
123  return str1[:i]
124  return str1
125 
126  def shell(self, cmd,verbose=0,debug=0,header=''):
127  stat = 0
128  if verbose or debug: print header+cmd
129  # flush stdout so we don't mangle python's buffering
130  if not debug:
131  input, output = os.popen4(cmd)
132  print output.read()
133  output.close()
134  input.close()
135 
136 
137 ansi_colors = {'0;30': 'Black',
138  '0;31': 'Red',
139  '0;32': 'Green',
140  '0;33': 'Brown',
141  '0;34': 'Blue',
142  '0;35': 'Purple',
143  '0;36': 'Cyan',
144  '0;37': 'LightGray',
145  '1;30': 'DarkGray',
146  '1;31': 'DarkRed',
147  '1;32': 'SeaGreen',
148  '1;33': 'Yellow',
149  '1;34': 'LightBlue',
150  '1;35': 'MediumPurple',
151  '1;36': 'LightCyan',
152  '1;37': 'White'}
153 
154 
155 class TkConsoleView(Tkinter.Text):
156  def __init__(self,root):
157  Tkinter.Text.__init__(self,root)
158 
159  # As the stdout,stderr etc. get fiddled about with we need to put any
160  # debug output into a file
161  self.debug=0
162  if self.debug:
163  self.o = open('debug.out','w')
164 
165  # Keeps track of where the insert cursor should be on the entry line
166  self.mark = 'scroll_mark'
167  self.mark_set(self.mark,Tkinter.END)
168  self.mark_gravity(self.mark,Tkinter.RIGHT)
169 
170  # Set the tags for colouring the text
171  for code in ansi_colors:
172  self.tag_config(code,
173  foreground=ansi_colors[code])
174 
175  self.tag_config('notouch') # Tag for indicating what areas of the widget aren't editable
176 
177 
178  # colour_pat matches the colour tags and places these in a group
179  # match character with hex value 01 (start of heading?) zero or more times, followed by
180  # the hex character 1b (escape) then "[" and group ...things.. followed by m (?) and then
181  # hex character 02 (start of text) zero or more times
182  self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
183 
184  self.line_start = 'line_start' # Tracks start of user input on the line (excluding prompt)
185  self.mark_set(self.line_start,Tkinter.INSERT)
186  self.mark_gravity(self.line_start,Tkinter.LEFT)
187 
188  self._setBindings()
189 
190  def write(self, text, editable=False):
191 
192  segments = self.color_pat.split(text)
193  # First is blank line
194  segment = segments.pop(0)
195 
196  # Keep track of where we started entering text so we can set as non-editable
197  self.start_mark = 'start_mark'
198  self.mark_set(self.start_mark,Tkinter.INSERT)
199  self.mark_gravity(self.start_mark,Tkinter.LEFT)
200 
201  self.insert(Tkinter.END, segment)
202 
203  if segments:
204  # Just return the colour tags
205  ansi_tags = self.color_pat.findall(text)
206 
207  for tag in ansi_tags:
208  i = segments.index(tag)
209  self.insert(Tkinter.END,segments[i+1],tag)
210  segments.pop(i)
211 
212  if not editable:
213  if self.debug:
214  print "adding notouch between %s : %s" % ( self.index(self.start_mark),\
215  self.index(Tkinter.INSERT) )
216 
217  self.tag_add('notouch',self.start_mark,"%s-1c" % Tkinter.INSERT)
218 
219  self.mark_unset(self.start_mark)
220  #jmht self.scroll_mark_onscreen(self.mark)
221 
222 
223  def showBanner(self,banner):
224  """Print the supplied banner on starting the shell"""
225  self.write(banner)
226 
227  def showPrompt(self, prompt):
228  self.write(prompt)
229  self.mark_set(self.line_start,Tkinter.INSERT)
230  self.see(Tkinter.INSERT) #Make sure we can always see the prompt
231 
232 
233  def changeLine(self, text):
234  self.delete(self.line_start,"%s lineend" % self.line_start)
235  self.write(text, True)
236 
237  def getCurrentLine(self):
238 
239  rv = self.get(self.line_start,Tkinter.END)
240 
241  if self.debug:
242  print >> self.o,"getCurrentline: %s" % rv
243  print >> self.o,"INSERT: %s" % Tkinter.END
244  print >> self.o,"END: %s" % Tkinter.INSERT
245  print >> self.o,"line_start: %s" % self.index(self.line_start)
246 
247  return rv
248 
249  def showReturned(self, text):
250  self.tag_add('notouch',self.line_start,"%s lineend" % self.line_start )
251  self.write('\n'+text)
252  if text:
253  self.write('\n')
254  self.showPrompt(self.prompt)
255  #self.mark_set(self.line_start,Tkinter.END) #jmht don't need this as showprompt sets mark
256 
257  def _setBindings(self):
258  """ Bind the keys we require.
259  REM: if a bound function returns "break" then no other bindings are called
260  If it returns None, then the other default bindings are called.
261  """
262  self.bind("<Key>",self.processKeyPress)
263  self.bind("<Return>",self.processEnterPress)
264  self.bind("<Up>",self.processUpPress)
265  self.bind("<Down>",self.processDownPress)
266  self.bind("<Tab>",self.processTabPress)
267  self.bind("<BackSpace>",self.processBackSpacePress)
268 
269  def isEditable(self):
270  """ Scan the notouch tag range in pairs and see if the INSERT index falls
271  between any of them.
272  """
273  ranges = self.tag_ranges('notouch')
274  first=None
275  for idx in ranges:
276  if not first:
277  first=idx
278  continue
279  else:
280 
281  if self.debug:
282  print "Comparing %s between %s : %s " % (self.index(Tkinter.INSERT),first,idx)
283 
284  if self.compare( Tkinter.INSERT,'>=',first ) and \
285  self.compare( Tkinter.INSERT,'<=',idx ):
286  return False
287  first=None
288  return True
289 
290  def processKeyPress(self,event):
291 
292  if self.debug:
293  print >>self.o,"processKeyPress got key: %s" % event.char
294  print >>self.o,"processKeyPress INSERT: %s" % self.index(Tkinter.INSERT)
295  print >>self.o,"processKeyPress END: %s" % self.index(Tkinter.END)
296 
297  if not self.isEditable():
298  # Move cursor mark to start of line
299  self.mark_set(Tkinter.INSERT,self.mark)
300 
301  # Make sure line_start follows inserted text
302  self.mark_set(self.mark,"%s+1c" % Tkinter.INSERT)
303 
304 
305  def processBackSpacePress(self,event):
306  if not self.isEditable():
307  return "break"
308 
309  def processEnterPress(self,event):
310  self._processLine()
311  return "break" # Need break to stop the other bindings being called
312 
313  def processUpPress(self,event):
314  self.changeLine(self.historyBack())
315  return "break"
316 
317  def processDownPress(self,event):
318  self.changeLine(self.historyForward())
319  return "break"
320 
321  def processTabPress(self,event):
322  if not self.getCurrentLine().strip():
323  return
324  completed, possibilities = self.complete(self.getCurrentLine())
325  if len(possibilities) > 1:
326  slice = self.getCurrentLine()
327  self.write('\n')
328  for symbol in possibilities:
329  self.write(symbol+'\n')
330  self.showPrompt(self.prompt)
331  self.changeLine(completed or slice)
332  return "break"
333 
335  def __init__(self,root,banner=None):
336  TkConsoleView.__init__(self,root)
337  self.cout = StringIO()
338  IterableIPShell.__init__(self, cout=self.cout,cerr=self.cout,
339  input_func=self.raw_input)
340 
341  if banner:
342  self.showBanner(banner)
343  self.execute()
344  self.cout.truncate(0)
345  self.showPrompt(self.prompt)
346  self.interrupt = False
347 
348  def raw_input(self, prompt=''):
349  if self.interrupt:
350  self.interrupt = False
351  raise KeyboardInterrupt
352  return self.getCurrentLine()
353 
354  def _processLine(self):
355  self.history_pos = 0
356  self.execute()
357  rv = self.cout.getvalue()
358  if self.debug:
359  print >>self.o,"_processLine got rv: %s" % rv
360  if rv: rv = rv.strip('\n')
361  self.showReturned(rv)
362  self.cout.truncate(0)
363 
364 if __name__ == "__main__":
365  root = Tkinter.Tk()
366  s=IPythonView(root)
367  s.pack()
368  root.mainloop()
Fstring::size_type len(Fstring const &s)
Length.
Definition: Fstring.hh:2207
bool open(utility::io::izstream &db_stream, std::string const &db_file, bool warn)
Open a database file on a provided stream.
Definition: open.cc:55
std::string & strip(std::string &s, std::string const &chars)
Strip Specified Characters from a string's Tails.