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