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# http://live.gnome.org/accerciser
15
16from __future__ import print_function
17import gtk, gobject
18import re
19import sys
20import os
21from gi.repository import Pango
22from StringIO import StringIO
23import IPython
24
25from pkg_resources import parse_version
26
27try:
28 import IPython
29except 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.IP = IPython.terminal.embed.InteractiveShellEmbed.instance(\
123 config=cfg, user_ns=user_ns)
124 else:
125 self.IP = 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.IP.system = lambda cmd: self.shell(self.IP.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.IP.raw_input = input_func
137 sys.excepthook = excepthook
138 self.iter_more = 0
140 self.complete_sep = re.compile('[\s\{\}\[\]\‍(\‍)]')
141 self.updateNamespace({'exit':lambda:None})
142 self.updateNamespace({'quit':lambda:None})
143 self.IP.readline_startup_hook(self.IP.pre_readline)
144 # Workaround for updating namespace with sys.modules
145 #
146 self.__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.IP.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_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;
169
170 self.IP.hooks.pre_prompt_hook()
171 if self.iter_more:
172 try:
173 self.prompt = self.generatePrompt(True)
174 except:
175 self.IP.showtraceback()
176 if self.IP.autoindent:
177 self.IP.rl_do_indent = True
178
179 try:
180 line = self.IP.raw_input(self.prompt)
181 except KeyboardInterrupt:
182 self.IP.write('\nKeyboardInterrupt\n')
183 self.IP.input_splitter.reset()
184 except:
185 self.IP.showtraceback()
186 else:
187 self.IP.input_splitter.push(line)
188 self.iter_more = self.IP.input_splitter.push_accepts_more()
189 self.prompt = self.generatePrompt(self.iter_more)
190 if (self.IP.SyntaxTB.last_syntax_error and
191 self.IP.autoedit_syntax):
192 self.IP.edit_syntax_error()
193 if not self.iter_more:
194 if parse_version(IPython.release.version) >= parse_version("2.0.0-dev"):
195 source_raw = self.IP.input_splitter.raw_reset()
196 else:
197 source_raw = self.IP.input_splitter.source_raw_reset()[1]
198 self.IP.run_cell(source_raw, store_history=True)
199 self.IP.rl_do_indent = False
200 else:
201 # TODO: Auto-indent
202 #
203 self.IP.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.IP.hooks.generate_prompt(is_continuation)
223 else:
224 if is_continuation:
225 prompt = self.IP.prompt_manager.render('in2')
226 else:
227 prompt = self.IP.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_level -= 1
240 if not self._getHistory():
241 self.history_level +=1
242 return self._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_level < 0:
252 self.history_level += 1
253 return self._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.IP.user_ns['In'][self.history_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.IP.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_sep.split(line)
285 if split_line[-1]:
286 possibilities = self.IP.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
333class 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. @type text_buffer: Gtk.TextBuffer
351 @ivar color_pat: Regex of terminal color pattern
352 @type color_pat: _sre.SRE_Pattern
353 @ivar mark: Scroll mark for automatic scrolling on input.
354 @type mark: Gtk.TextMark
355 @ivar line_start: Start of command line mark.
356 @type line_start: Gtk.TextMark
357 """
358 ANSI_COLORS = {'0;30': 'Black', '0;31': 'Red',
359 '0;32': 'Green', '0;33': 'Brown',
360 '0;34': 'Blue', '0;35': 'Purple',
361 '0;36': 'Cyan', '0;37': 'LightGray',
362 '1;30': 'DarkGray', '1;31': 'DarkRed',
363 '1;32': 'SeaGreen', '1;33': 'Yellow',
364 '1;34': 'LightBlue', '1;35': 'MediumPurple',
365 '1;36': 'LightCyan', '1;37': 'White'}
366
367 def __init__(self):
368 """
369 Initialize console view.
370 """
371 GObject.GObject.__init__(self)
372 self.modify_font(Pango.FontDescription('Mono'))
373 self.set_cursor_visible(True)
374 self.text_buffer = self.get_buffer()
375 self.mark = self.text_buffer.create_mark('scroll_mark',
376 self.text_buffer.get_end_iter(),
377 False)
378 for code in self.ANSI_COLORS:
379 self.text_buffer.create_tag(code,
380 foreground=self.ANSI_COLORS[code],
381 weight=700)
382 self.text_buffer.create_tag('0')
383 self.text_buffer.create_tag('notouch', editable=False)
384 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
385 self.line_start = \
386 self.text_buffer.create_mark('line_start',
387 self.text_buffer.get_end_iter(), True)
388 self.connect('key-press-event', self.onKeyPress)
389
390 def write(self, text, editable=False):
391 """!
392 Write given text to buffer.
393
394 @param text: Text to append.
395 @param editable: If true, added text is editable.
396 @return none
397 """
398 GObject.idle_add(self._write, text, editable)
399
400 def _write(self, text, editable=False):
401 """!
402 Write given text to buffer.
403
404 @param text: Text to append.
405 @param editable: If true, added text is editable.
406 @return none
407 """
408 segments = self.color_pat.split(text)
409 segment = segments.pop(0)
410 start_mark = self.text_buffer.create_mark(None,
411 self.text_buffer.get_end_iter(),
412 True)
413 self.text_buffer.insert(self.text_buffer.get_end_iter(), segment)
414
415 if segments:
416 ansi_tags = self.color_pat.findall(text)
417 for tag in ansi_tags:
418 i = segments.index(tag)
419 self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(),
420 segments[i+1], str(tag))
421 segments.pop(i)
422 if not editable:
423 self.text_buffer.apply_tag_by_name('notouch',
424 self.text_buffer.get_iter_at_mark(start_mark),
425 self.text_buffer.get_end_iter())
426 self.text_buffer.delete_mark(start_mark)
427 self.scroll_mark_onscreen(self.mark)
428
429 def showPrompt(self, prompt):
430 """!
431 Prints prompt at start of line.
432
433 @param prompt: Prompt to print.
434 @return none
435 """
436 GObject.idle_add(self._showPrompt, prompt)
437
438 def _showPrompt(self, prompt):
439 """!
440 Prints prompt at start of line.
441
442 @param prompt: Prompt to print.
443 @return none
444 """
445 self._write(prompt)
446 self.text_buffer.move_mark(self.line_start,
447 self.text_buffer.get_end_iter())
448
449 def changeLine(self, text):
450 """!
451 Replace currently entered command line with given text.
452
453 @param text: Text to use as replacement.
454 @return none
455 """
456 GObject.idle_add(self._changeLine, text)
457
458 def _changeLine(self, text):
459 """!
460 Replace currently entered command line with given text.
461
462 @param text: Text to use as replacement.
463 @return none
464 """
465 iter = self.text_buffer.get_iter_at_mark(self.line_start)
466 iter.forward_to_line_end()
467 self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter)
468 self._write(text, True)
469
470 def getCurrentLine(self):
471 """!
472 Get text in current command line.
473
474 @return Text of current command line.
475 """
476 rv = self.text_buffer.get_slice(
477 self.text_buffer.get_iter_at_mark(self.line_start),
478 self.text_buffer.get_end_iter(), False)
479 return rv
480
481 def showReturned(self, text):
482 """!
483 Show returned text from last command and print new prompt.
484
485 @param text: Text to show.
486 @return none
487 """
488 GObject.idle_add(self._showReturned, text)
489
490 def _showReturned(self, text):
491 """!
492 Show returned text from last command and print new prompt.
493
494 @param text: Text to show.
495 @return none
496 """
497 iter = self.text_buffer.get_iter_at_mark(self.line_start)
498 iter.forward_to_line_end()
499 self.text_buffer.apply_tag_by_name(
500 'notouch',
501 self.text_buffer.get_iter_at_mark(self.line_start),
502 iter)
503 self._write('\n'+text)
504 if text:
505 self._write('\n')
506 self._showPrompt(self.prompt)
507 self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter())
508 self.text_buffer.place_cursor(self.text_buffer.get_end_iter())
509
510 if self.IP.rl_do_indent:
511 indentation = self.IP.input_splitter.indent_spaces * ' '
512 self.text_buffer.insert_at_cursor(indentation)
513
514 def onKeyPress(self, widget, event):
515 """!
516 Key press callback used for correcting behavior for console-like
517 interfaces. For example 'home' should go to prompt, not to beginning of
518 line.
519
520 @param widget: Widget that key press accored in.
521 @param event: Event object
522 @return Return True if event should not trickle.
523 """
524 insert_mark = self.text_buffer.get_insert()
525 insert_iter = self.text_buffer.get_iter_at_mark(insert_mark)
526 selection_mark = self.text_buffer.get_selection_bound()
527 selection_iter = self.text_buffer.get_iter_at_mark(selection_mark)
528 start_iter = self.text_buffer.get_iter_at_mark(self.line_start)
529 if event.keyval == Gdk.KEY_Home:
530 if event.get_state() & Gdk.ModifierType.CONTROL_MASK or event.get_state() & Gdk.ModifierType.MOD1_MASK:
531 pass
532 elif event.get_state() & Gdk.ModifierType.SHIFT_MASK:
533 self.text_buffer.move_mark(insert_mark, start_iter)
534 return True
535 else:
536 self.text_buffer.place_cursor(start_iter)
537 return True
538 elif event.keyval == Gdk.KEY_Left:
539 insert_iter.backward_cursor_position()
540 if not insert_iter.editable(True):
541 return True
542 elif not event.string:
543 pass
544 elif start_iter.compare(insert_iter) <= 0 and \
545 start_iter.compare(selection_iter) <= 0:
546 pass
547 elif start_iter.compare(insert_iter) > 0 and \
548 start_iter.compare(selection_iter) > 0:
549 self.text_buffer.place_cursor(start_iter)
550 elif insert_iter.compare(selection_iter) < 0:
551 self.text_buffer.move_mark(insert_mark, start_iter)
552 elif insert_iter.compare(selection_iter) > 0:
553 self.text_buffer.move_mark(selection_mark, start_iter)
554
555 return self.onKeyPressExtend(event)
556
557 def onKeyPressExtend(self, event):
558 """!
559 For some reason we can't extend onKeyPress directly (bug #500900).
560 @param event key press
561 @return none
562 """
563 pass
564
565
581 """
582 Sub-class of both modified IPython shell and L{ConsoleView} this makes
583 a GTK+ IPython console.
584 """
585 def __init__(self):
586 """
587 Initialize. Redirect I/O to console.
588 """
589 ConsoleView.__init__(self)
590 self.cout = StringIO()
591 IterableIPShell.__init__(self, cout=self.cout,cerr=self.cout,
592 input_func=self.raw_input)
593 self.interrupt = False
594 self.execute()
595 self.promptprompt = self.generatePrompt(False)
596 self.cout.truncate(0)
597 self.showPrompt(self.promptprompt)
598
599 def raw_input(self, prompt=''):
600 """!
601 Custom raw_input() replacement. Gets current line from console buffer.
602
603 @param prompt: Prompt to print. Here for compatibility as replacement.
604 @return The current command line text.
605 """
606 if self.interrupt:
607 self.interrupt = False
608 raise KeyboardInterrupt
609 return self.getCurrentLine()
610
611 def onKeyPressExtend(self, event):
612 """!
613 Key press callback with plenty of shell goodness, like history,
614 autocompletions, etc.
615
616 @param event: Event object.
617 @return True if event should not trickle.
618 """
619
620 if event.get_state() & Gdk.ModifierType.CONTROL_MASK and event.keyval == 99:
621 self.interrupt = True
622 self._processLine()
623 return True
624 elif event.keyval == Gdk.KEY_Return:
625 self._processLine()
626 return True
627 elif event.keyval == Gdk.KEY_Up:
628 self.changeLine(self.historyBack())
629 return True
630 elif event.keyval == Gdk.KEY_Down:
631 self.changeLine(self.historyForward())
632 return True
633 elif event.keyval == Gdk.KEY_Tab:
634 if not self.getCurrentLine().strip():
635 return False
636 completed, possibilities = self.complete(self.getCurrentLine())
637 if len(possibilities) > 1:
638 slice = self.getCurrentLine()
639 self.write('\n')
640 for symbol in possibilities:
641 self.write(symbol+'\n')
642 self.showPrompt(self.promptprompt)
643 self.changeLine(completed or slice)
644 return True
645
646 def _processLine(self):
647 """!
648 Process current command line.
649 @return none
650 """
651 self.history_pos = 0
652 self.execute()
653 rv = self.cout.getvalue()
654 if rv: rv = rv.strip('\n')
655 self.showReturned(rv)
656 self.cout.truncate(0)
657 self.cout.seek(0)
658
659if __name__ == "__main__":
660 window = Gtk.Window()
661 window.set_default_size(640, 320)
662 window.connect('delete-event', lambda x, y: Gtk.main_quit())
663 window.add(IPythonView())
664 window.show_all()
665 Gtk.main()
666
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