A Discrete-Event Network Simulator
API
ipython_view.py
Go to the documentation of this file.
1 """
2 Backend to the console plugin.
3 
4 @author: Eitan Isaacson
5 @organization: IBM Corporation
6 @copyright: Copyright (c) 2007 IBM Corporation
7 @license: BSD
8 
9 All rights reserved. This program and the accompanying materials are made
10 available under the terms of the BSD which accompanies this distribution, and
11 is available at U{http://www.opensource.org/licenses/bsd-license.php}
12 """
13 # this file is a modified version of source code from the Accerciser project
14 # https://wiki.gnome.org/Apps/Accerciser
15 
16 import gtk, gobject
17 import re
18 import sys
19 import os
20 from gi.repository import Pango
21 from StringIO import StringIO
22 import IPython
23 
24 from pkg_resources import parse_version
25 
26 
27 try:
28  import IPython
29 except ImportError:
30 
32  IPython = None
33 
34 
36 
55  def __init__(self,argv=None,user_ns=None,user_global_ns=None,
56  cin=None, cout=None,cerr=None, input_func=None):
57  """! Initializer
58 
59  @param self: this object
60  @param argv: Command line options for IPython
61  @param user_ns: User namespace.
62  @param user_global_ns: User global namespace.
63  @param cin: Console standard input.
64  @param cout: Console standard output.
65  @param cerr: Console standard error.
66  @param input_func: Replacement for builtin raw_input()
67  """
68  io = IPython.utils.io
69  if input_func:
70  if parse_version(IPython.release.version) >= parse_version("1.2.1"):
71  IPython.terminal.interactiveshell.raw_input_original = input_func
72  else:
73  IPython.frontend.terminal.interactiveshell.raw_input_original = input_func
74  if cin:
75  io.stdin = io.IOStream(cin)
76  if cout:
77  io.stdout = io.IOStream(cout)
78  if cerr:
79  io.stderr = io.IOStream(cerr)
80 
81  # This is to get rid of the blockage that occurs during
82  # IPython.Shell.InteractiveShell.user_setup()
83 
84  io.raw_input = lambda x: None
85 
86  os.environ['TERM'] = 'dumb'
87  excepthook = sys.excepthook
88 
89  from IPython.config.loader import Config
90  cfg = Config()
91  cfg.InteractiveShell.colors = "Linux"
92 
93  # InteractiveShell's __init__ overwrites io.stdout,io.stderr with
94  # sys.stdout, sys.stderr, this makes sure they are right
95  #
96  old_stdout, old_stderr = sys.stdout, sys.stderr
97  sys.stdout, sys.stderr = io.stdout.stream, io.stderr.stream
98 
99  # InteractiveShell inherits from SingletonConfigurable, so use instance()
100  #
101  if parse_version(IPython.release.version) >= parse_version("1.2.1"):
102  self.IPIP = IPython.terminal.embed.InteractiveShellEmbed.instance(\
103  config=cfg, user_ns=user_ns)
104  else:
105  self.IPIP = IPython.frontend.terminal.embed.InteractiveShellEmbed.instance(\
106  config=cfg, user_ns=user_ns)
107 
108  sys.stdout, sys.stderr = old_stdout, old_stderr
109 
110  self.IPIP.system = lambda cmd: self.shellshell(self.IPIP.var_expand(cmd),
111  header='IPython system call: ')
112 # local_ns=user_ns)
113  #global_ns=user_global_ns)
114  #verbose=self.IP.rc.system_verbose)
115 
116  self.IPIP.raw_input = input_func
117  sys.excepthook = excepthook
118  self.iter_moreiter_more = 0
119  self.history_levelhistory_level = 0
120  self.complete_sepcomplete_sep = re.compile('[\s\{\}\[\]\‍(\‍)]')
121  self.updateNamespaceupdateNamespace({'exit':lambda:None})
122  self.updateNamespaceupdateNamespace({'quit':lambda:None})
123  self.IPIP.readline_startup_hook(self.IPIP.pre_readline)
124  # Workaround for updating namespace with sys.modules
125  #
126  self.__update_namespace__update_namespace()
127 
129  """!
130  Update self.IP namespace for autocompletion with sys.modules
131  @return none
132  """
133  for k, v in list(sys.modules.items()):
134  if not '.' in k:
135  self.IPIP.user_ns.update({k:v})
136 
137  def execute(self):
138  """!
139  Executes the current line provided by the shell object.
140  @return none
141  """
142  self.history_levelhistory_level = 0
143  orig_stdout = sys.stdout
144  sys.stdout = IPython.utils.io.stdout
145 
146  orig_stdin = sys.stdin
147  sys.stdin = IPython.utils.io.stdin;
148  self.promptprompt = self.generatePromptgeneratePrompt(self.iter_moreiter_more)
149 
150  self.IPIP.hooks.pre_prompt_hook()
151  if self.iter_moreiter_more:
152  try:
153  self.promptprompt = self.generatePromptgeneratePrompt(True)
154  except:
155  self.IPIP.showtraceback()
156  if self.IPIP.autoindent:
157  self.IPIP.rl_do_indent = True
158 
159  try:
160  line = self.IPIP.raw_input(self.promptprompt)
161  except KeyboardInterrupt:
162  self.IPIP.write('\nKeyboardInterrupt\n')
163  self.IPIP.input_splitter.reset()
164  except:
165  self.IPIP.showtraceback()
166  else:
167  self.IPIP.input_splitter.push(line)
168  self.iter_moreiter_more = self.IPIP.input_splitter.push_accepts_more()
169  self.promptprompt = self.generatePromptgeneratePrompt(self.iter_moreiter_more)
170  if (self.IPIP.SyntaxTB.last_syntax_error and
171  self.IPIP.autoedit_syntax):
172  self.IPIP.edit_syntax_error()
173  if not self.iter_moreiter_more:
174  if parse_version(IPython.release.version) >= parse_version("2.0.0-dev"):
175  source_raw = self.IPIP.input_splitter.raw_reset()
176  else:
177  source_raw = self.IPIP.input_splitter.source_raw_reset()[1]
178  self.IPIP.run_cell(source_raw, store_history=True)
179  self.IPIP.rl_do_indent = False
180  else:
181  # TODO: Auto-indent
182  #
183  self.IPIP.rl_do_indent = True
184  pass
185 
186  sys.stdout = orig_stdout
187  sys.stdin = orig_stdin
188 
189  def generatePrompt(self, is_continuation):
190  """!
191  Generate prompt depending on is_continuation value
192 
193  @param is_continuation
194  @return: The prompt string representation
195 
196  """
197 
198  # Backwards compatibility with ipyton-0.11
199  #
200  ver = IPython.__version__
201  if '0.11' in ver:
202  prompt = self.IPIP.hooks.generate_prompt(is_continuation)
203  else:
204  if is_continuation:
205  prompt = self.IPIP.prompt_manager.render('in2')
206  else:
207  prompt = self.IPIP.prompt_manager.render('in')
208 
209  return prompt
210 
211 
212  def historyBack(self):
213  """!
214  Provides one history command back.
215 
216  @param self this object
217  @return: The command string.
218  """
219  self.history_levelhistory_level -= 1
220  if not self._getHistory_getHistory():
221  self.history_levelhistory_level +=1
222  return self._getHistory_getHistory()
223 
224  def historyForward(self):
225  """!
226  Provides one history command forward.
227 
228  @param self this object
229  @return: The command string.
230  """
231  if self.history_levelhistory_level < 0:
232  self.history_levelhistory_level += 1
233  return self._getHistory_getHistory()
234 
235  def _getHistory(self):
236  """!
237  Gets the command string of the current history level.
238 
239  @param self this object
240  @return: Historic command string.
241  """
242  try:
243  rv = self.IPIP.user_ns['In'][self.history_levelhistory_level].strip('\n')
244  except IndexError:
245  rv = ''
246  return rv
247 
248  def updateNamespace(self, ns_dict):
249  """!
250  Add the current dictionary to the shell namespace.
251 
252  @param ns_dict: A dictionary of symbol-values.
253  @return none
254  """
255  self.IPIP.user_ns.update(ns_dict)
256 
257  def complete(self, line):
258  """!
259  Returns an auto completed line and/or possibilities for completion.
260 
261  @param line: Given line so far.
262  @return: Line completed as for as possible, and possible further completions.
263  """
264  split_line = self.complete_sepcomplete_sep.split(line)
265  if split_line[-1]:
266  possibilities = self.IPIP.complete(split_line[-1])
267  else:
268  completed = line
269  possibilities = ['', []]
270  if possibilities:
271  def _commonPrefix(str1, str2):
272  """!
273  Reduction function. returns common prefix of two given strings.
274 
275  @param str1: First string.
276  @param str2: Second string
277  @return: Common prefix to both strings.
278  """
279  for i in range(len(str1)):
280  if not str2.startswith(str1[:i+1]):
281  return str1[:i]
282  return str1
283  if possibilities[1]:
284  common_prefix = reduce(_commonPrefix, possibilities[1]) or line[-1]
285  completed = line[:-len(split_line[-1])]+common_prefix
286  else:
287  completed = line
288  else:
289  completed = line
290  return completed, possibilities[1]
291 
292 
293  def shell(self, cmd,verbose=0,debug=0,header=''):
294  """!
295  Replacement method to allow shell commands without them blocking.
296 
297  @param cmd: Shell command to execute.
298  @param verbose: Verbosity
299  @param debug: Debug level
300  @param header: Header to be printed before output
301  @return none
302  """
303  stat = 0
304  if verbose or debug: print(header+cmd)
305  # flush stdout so we don't mangle python's buffering
306  if not debug:
307  input, output = os.popen4(cmd)
308  print(output.read())
309  output.close()
310  input.close()
311 
312 
313 class ConsoleView(Gtk.TextView):
314 
324  """
325  Specialized text view for console-like workflow.
326 
327  @cvar ANSI_COLORS: Mapping of terminal colors to X11 names.
328  @type ANSI_COLORS: dictionary
329 
330  @ivar text_buffer: Widget's text buffer.
331  @type text_buffer: Gtk.TextBuffer
332  @ivar color_pat: Regex of terminal color pattern
333  @type color_pat: _sre.SRE_Pattern
334  @ivar mark: Scroll mark for automatic scrolling on input.
335  @type mark: Gtk.TextMark
336  @ivar line_start: Start of command line mark.
337  @type line_start: Gtk.TextMark
338  """
339  ANSI_COLORS = {'0;30': 'Black', '0;31': 'Red',
340  '0;32': 'Green', '0;33': 'Brown',
341  '0;34': 'Blue', '0;35': 'Purple',
342  '0;36': 'Cyan', '0;37': 'LightGray',
343  '1;30': 'DarkGray', '1;31': 'DarkRed',
344  '1;32': 'SeaGreen', '1;33': 'Yellow',
345  '1;34': 'LightBlue', '1;35': 'MediumPurple',
346  '1;36': 'LightCyan', '1;37': 'White'}
347 
348  def __init__(self):
349  """
350  Initialize console view.
351  """
352  GObject.GObject.__init__(self)
353  self.modify_font(Pango.FontDescription('Mono'))
354  self.set_cursor_visible(True)
355  self.text_buffertext_buffer = self.get_buffer()
356  self.markmark = self.text_buffertext_buffer.create_mark('scroll_mark',
357  self.text_buffertext_buffer.get_end_iter(),
358  False)
359  for code in self.ANSI_COLORSANSI_COLORS:
360  self.text_buffertext_buffer.create_tag(code,
361  foreground=self.ANSI_COLORSANSI_COLORS[code],
362  weight=700)
363  self.text_buffertext_buffer.create_tag('0')
364  self.text_buffertext_buffer.create_tag('notouch', editable=False)
365  self.color_patcolor_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
366  self.line_startline_start = \
367  self.text_buffertext_buffer.create_mark('line_start',
368  self.text_buffertext_buffer.get_end_iter(), True)
369  self.connect('key-press-event', self.onKeyPressonKeyPress)
370 
371  def write(self, text, editable=False):
372  """!
373  Write given text to buffer.
374 
375  @param text: Text to append.
376  @param editable: If true, added text is editable.
377  @return none
378  """
379  GObject.idle_add(self._write_write, text, editable)
380 
381  def _write(self, text, editable=False):
382  """!
383  Write given text to buffer.
384 
385  @param text: Text to append.
386  @param editable: If true, added text is editable.
387  @return none
388  """
389  segments = self.color_patcolor_pat.split(text)
390  segment = segments.pop(0)
391  start_mark = self.text_buffertext_buffer.create_mark(None,
392  self.text_buffertext_buffer.get_end_iter(),
393  True)
394  self.text_buffertext_buffer.insert(self.text_buffertext_buffer.get_end_iter(), segment)
395 
396  if segments:
397  ansi_tags = self.color_patcolor_pat.findall(text)
398  for tag in ansi_tags:
399  i = segments.index(tag)
400  self.text_buffertext_buffer.insert_with_tags_by_name(self.text_buffertext_buffer.get_end_iter(),
401  segments[i+1], str(tag))
402  segments.pop(i)
403  if not editable:
404  self.text_buffertext_buffer.apply_tag_by_name('notouch',
405  self.text_buffertext_buffer.get_iter_at_mark(start_mark),
406  self.text_buffertext_buffer.get_end_iter())
407  self.text_buffertext_buffer.delete_mark(start_mark)
408  self.scroll_mark_onscreen(self.markmark)
409 
410  def showPrompt(self, prompt):
411  """!
412  Prints prompt at start of line.
413 
414  @param prompt: Prompt to print.
415  @return none
416  """
417  GObject.idle_add(self._showPrompt_showPrompt, prompt)
418 
419  def _showPrompt(self, prompt):
420  """!
421  Prints prompt at start of line.
422 
423  @param prompt: Prompt to print.
424  @return none
425  """
426  self._write_write(prompt)
427  self.text_buffertext_buffer.move_mark(self.line_startline_start,
428  self.text_buffertext_buffer.get_end_iter())
429 
430  def changeLine(self, text):
431  """!
432  Replace currently entered command line with given text.
433 
434  @param text: Text to use as replacement.
435  @return none
436  """
437  GObject.idle_add(self._changeLine_changeLine, text)
438 
439  def _changeLine(self, text):
440  """!
441  Replace currently entered command line with given text.
442 
443  @param text: Text to use as replacement.
444  @return none
445  """
446  iter = self.text_buffertext_buffer.get_iter_at_mark(self.line_startline_start)
447  iter.forward_to_line_end()
448  self.text_buffertext_buffer.delete(self.text_buffertext_buffer.get_iter_at_mark(self.line_startline_start), iter)
449  self._write_write(text, True)
450 
451  def getCurrentLine(self):
452  """!
453  Get text in current command line.
454 
455  @return Text of current command line.
456  """
457  rv = self.text_buffertext_buffer.get_slice(
458  self.text_buffertext_buffer.get_iter_at_mark(self.line_startline_start),
459  self.text_buffertext_buffer.get_end_iter(), False)
460  return rv
461 
462  def showReturned(self, text):
463  """!
464  Show returned text from last command and print new prompt.
465 
466  @param text: Text to show.
467  @return none
468  """
469  GObject.idle_add(self._showReturned_showReturned, text)
470 
471  def _showReturned(self, text):
472  """!
473  Show returned text from last command and print new prompt.
474 
475  @param text: Text to show.
476  @return none
477  """
478  iter = self.text_buffertext_buffer.get_iter_at_mark(self.line_startline_start)
479  iter.forward_to_line_end()
480  self.text_buffertext_buffer.apply_tag_by_name(
481  'notouch',
482  self.text_buffertext_buffer.get_iter_at_mark(self.line_startline_start),
483  iter)
484  self._write_write('\n'+text)
485  if text:
486  self._write_write('\n')
487  self._showPrompt_showPrompt(self.prompt)
488  self.text_buffertext_buffer.move_mark(self.line_startline_start,self.text_buffertext_buffer.get_end_iter())
489  self.text_buffertext_buffer.place_cursor(self.text_buffertext_buffer.get_end_iter())
490 
491  if self.IP.rl_do_indent:
492  indentation = self.IP.input_splitter.indent_spaces * ' '
493  self.text_buffertext_buffer.insert_at_cursor(indentation)
494 
495  def onKeyPress(self, widget, event):
496  """!
497  Key press callback used for correcting behavior for console-like
498  interfaces. For example 'home' should go to prompt, not to beginning of
499  line.
500 
501  @param widget: Widget that key press accored in.
502  @param event: Event object
503  @return Return True if event should not trickle.
504  """
505  insert_mark = self.text_buffertext_buffer.get_insert()
506  insert_iter = self.text_buffertext_buffer.get_iter_at_mark(insert_mark)
507  selection_mark = self.text_buffertext_buffer.get_selection_bound()
508  selection_iter = self.text_buffertext_buffer.get_iter_at_mark(selection_mark)
509  start_iter = self.text_buffertext_buffer.get_iter_at_mark(self.line_startline_start)
510  if event.keyval == Gdk.KEY_Home:
511  if event.get_state() & Gdk.ModifierType.CONTROL_MASK or event.get_state() & Gdk.ModifierType.MOD1_MASK:
512  pass
513  elif event.get_state() & Gdk.ModifierType.SHIFT_MASK:
514  self.text_buffertext_buffer.move_mark(insert_mark, start_iter)
515  return True
516  else:
517  self.text_buffertext_buffer.place_cursor(start_iter)
518  return True
519  elif event.keyval == Gdk.KEY_Left:
520  insert_iter.backward_cursor_position()
521  if not insert_iter.editable(True):
522  return True
523  elif not event.string:
524  pass
525  elif start_iter.compare(insert_iter) <= 0 and \
526  start_iter.compare(selection_iter) <= 0:
527  pass
528  elif start_iter.compare(insert_iter) > 0 and \
529  start_iter.compare(selection_iter) > 0:
530  self.text_buffertext_buffer.place_cursor(start_iter)
531  elif insert_iter.compare(selection_iter) < 0:
532  self.text_buffertext_buffer.move_mark(insert_mark, start_iter)
533  elif insert_iter.compare(selection_iter) > 0:
534  self.text_buffertext_buffer.move_mark(selection_mark, start_iter)
535 
536  return self.onKeyPressExtendonKeyPressExtend(event)
537 
538  def onKeyPressExtend(self, event):
539  """!
540  For some reason we can't extend onKeyPress directly (bug #500900).
541  @param event key press
542  @return none
543  """
544  pass
545 
546 
547 class IPythonView(ConsoleView, IterableIPShell):
548 
562  """
563  Sub-class of both modified IPython shell and L{ConsoleView} this makes
564  a GTK+ IPython console.
565  """
566  def __init__(self):
567  """
568  Initialize. Redirect I/O to console.
569  """
570  ConsoleView.__init__(self)
571  self.coutcout = StringIO()
572  IterableIPShell.__init__(self, cout=self.coutcout,cerr=self.coutcout,
573  input_func=self.raw_inputraw_input)
574  self.interruptinterrupt = False
575  self.executeexecute()
576  self.promptpromptprompt = self.generatePromptgeneratePrompt(False)
577  self.coutcout.truncate(0)
578  self.showPromptshowPrompt(self.promptpromptprompt)
579 
580  def raw_input(self, prompt=''):
581  """!
582  Custom raw_input() replacement. Gets current line from console buffer.
583 
584  @param prompt: Prompt to print. Here for compatibility as replacement.
585  @return The current command line text.
586  """
587  if self.interruptinterrupt:
588  self.interruptinterrupt = False
589  raise KeyboardInterrupt
590  return self.getCurrentLinegetCurrentLine()
591 
592  def onKeyPressExtend(self, event):
593  """!
594  Key press callback with plenty of shell goodness, like history,
595  autocompletions, etc.
596 
597  @param event: Event object.
598  @return True if event should not trickle.
599  """
600 
601  if event.get_state() & Gdk.ModifierType.CONTROL_MASK and event.keyval == 99:
602  self.interruptinterrupt = True
603  self._processLine_processLine()
604  return True
605  elif event.keyval == Gdk.KEY_Return:
606  self._processLine_processLine()
607  return True
608  elif event.keyval == Gdk.KEY_Up:
609  self.changeLinechangeLine(self.historyBackhistoryBack())
610  return True
611  elif event.keyval == Gdk.KEY_Down:
612  self.changeLinechangeLine(self.historyForwardhistoryForward())
613  return True
614  elif event.keyval == Gdk.KEY_Tab:
615  if not self.getCurrentLinegetCurrentLine().strip():
616  return False
617  completed, possibilities = self.completecomplete(self.getCurrentLinegetCurrentLine())
618  if len(possibilities) > 1:
619  slice = self.getCurrentLinegetCurrentLine()
620  self.writewrite('\n')
621  for symbol in possibilities:
622  self.writewrite(symbol+'\n')
623  self.showPromptshowPrompt(self.promptpromptprompt)
624  self.changeLinechangeLine(completed or slice)
625  return True
626 
627  def _processLine(self):
628  """!
629  Process current command line.
630  @return none
631  """
632  self.history_poshistory_pos = 0
633  self.executeexecute()
634  rv = self.coutcout.getvalue()
635  if rv: rv = rv.strip('\n')
636  self.showReturnedshowReturned(rv)
637  self.coutcout.truncate(0)
638  self.coutcout.seek(0)
639 
640 if __name__ == "__main__":
641  window = Gtk.Window()
642  window.set_default_size(640, 320)
643  window.connect('delete-event', lambda x, y: Gtk.main_quit())
644  window.add(IPythonView())
645  window.show_all()
646  Gtk.main()
647 
def write(self, text, editable=False)
Write given text to buffer.
def changeLine(self, text)
Replace currently entered command line with given text.
dictionary ANSI_COLORS
color list
def _showPrompt(self, prompt)
Prints prompt at start of line.
def _showReturned(self, text)
Show returned text from last command and print new prompt.
def getCurrentLine(self)
Get text in current command line.
def onKeyPressExtend(self, event)
For some reason we can't extend onKeyPress directly (bug #500900).
def _write(self, text, editable=False)
Write given text to buffer.
def _changeLine(self, text)
Replace currently entered command line with given text.
def showPrompt(self, prompt)
Prints prompt at start of line.
def showReturned(self, text)
Show returned text from last command and print new prompt.
def onKeyPress(self, widget, event)
Key press callback used for correcting behavior for console-like interfaces.
def onKeyPressExtend(self, event)
Key press callback with plenty of shell goodness, like history, autocompletions, etc.
def raw_input(self, prompt='')
Custom raw_input() replacement.
def _processLine(self)
Process current command line.
def updateNamespace(self, ns_dict)
Add the current dictionary to the shell namespace.
def __init__(self, argv=None, user_ns=None, user_global_ns=None, cin=None, cout=None, cerr=None, input_func=None)
Initializer.
Definition: ipython_view.py:56
def _getHistory(self)
Gets the command string of the current history level.
def __update_namespace(self)
Update self.IP namespace for autocompletion with sys.modules.
def historyBack(self)
Provides one history command back.
def generatePrompt(self, is_continuation)
Generate prompt depending on is_continuation value.
def historyForward(self)
Provides one history command forward.
def shell(self, cmd, verbose=0, debug=0, header='')
Replacement method to allow shell commands without them blocking.
def execute(self)
Executes the current line provided by the shell object.
def complete(self, line)
Returns an auto completed line and/or possibilities for completion.
#define delete
#define list