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