1 |
#!/usr/bin/python |
#!/usr/bin/python |
2 |
""" |
''' |
3 |
Provides IPython console widget. |
Provides IPython console widget. |
4 |
|
|
5 |
@author: Eitan Isaacson |
@author: Eitan Isaacson |
10 |
All rights reserved. This program and the accompanying materials are made |
All rights reserved. This program and the accompanying materials are made |
11 |
available under the terms of the BSD which accompanies this distribution, and |
available under the terms of the BSD which accompanies this distribution, and |
12 |
is available at U{http://www.opensource.org/licenses/bsd-license.php} |
is available at U{http://www.opensource.org/licenses/bsd-license.php} |
13 |
""" |
''' |
14 |
|
|
15 |
import gtk, gobject |
import gtk, gobject |
16 |
import re |
import re |
26 |
raise Exception("Error importing IPython (%s)" % str(e)) |
raise Exception("Error importing IPython (%s)" % str(e)) |
27 |
|
|
28 |
class IterableIPShell: |
class IterableIPShell: |
29 |
""" |
''' |
30 |
Create an IPython instance. Does not start a blocking event loop, |
Create an IPython instance. Does not start a blocking event loop, |
31 |
instead allow single iterations. This allows embedding in GTK+ |
instead allow single iterations. This allows embedding in GTK+ |
32 |
without blockage. |
without blockage. |
41 |
@type history_level: integer |
@type history_level: integer |
42 |
@ivar complete_sep: Seperation delimeters for completion function. |
@ivar complete_sep: Seperation delimeters for completion function. |
43 |
@type complete_sep: _sre.SRE_Pattern |
@type complete_sep: _sre.SRE_Pattern |
44 |
""" |
''' |
45 |
def __init__(self,argv=[],user_ns=None,user_global_ns=None, |
def __init__(self,argv=[],user_ns=None,user_global_ns=None, |
46 |
cin=None, cout=None,cerr=None, input_func=None): |
cin=None, cout=None,cerr=None, input_func=None): |
47 |
""" |
''' |
48 |
|
|
49 |
|
|
50 |
@param argv: Command line options for IPython |
@param argv: Command line options for IPython |
51 |
@type argv: list |
@type argv: list |
52 |
@param user_ns: User namespace. |
@param user_ns: User namespace. |
61 |
@type cerr: IO stream |
@type cerr: IO stream |
62 |
@param input_func: Replacement for builtin raw_input() |
@param input_func: Replacement for builtin raw_input() |
63 |
@type input_func: function |
@type input_func: function |
64 |
""" |
''' |
|
|
|
65 |
if input_func: |
if input_func: |
66 |
IPython.iplib.raw_input_original = input_func |
IPython.iplib.raw_input_original = input_func |
67 |
if cin: |
if cin: |
92 |
self.complete_sep = re.compile('[\s\{\}\[\]\(\)]') |
self.complete_sep = re.compile('[\s\{\}\[\]\(\)]') |
93 |
|
|
94 |
def execute(self): |
def execute(self): |
95 |
""" |
''' |
96 |
Executes the current line provided by the shell object. |
Executes the current line provided by the shell object. |
97 |
""" |
''' |
98 |
self.history_level = 0 |
self.history_level = 0 |
99 |
orig_stdout = sys.stdout |
orig_stdout = sys.stdout |
100 |
sys.stdout = IPython.Shell.Term.cout |
sys.stdout = IPython.Shell.Term.cout |
127 |
sys.stdout = orig_stdout |
sys.stdout = orig_stdout |
128 |
|
|
129 |
def historyBack(self): |
def historyBack(self): |
130 |
""" |
''' |
131 |
Provides one history command back. |
Provides one history command back. |
132 |
|
|
133 |
@return: The command string. |
@return: The command string. |
134 |
@rtype: string |
@rtype: string |
135 |
""" |
''' |
136 |
self.history_level -= 1 |
self.history_level -= 1 |
137 |
return self._getHistory() |
return self._getHistory() |
138 |
|
|
139 |
def historyForward(self): |
def historyForward(self): |
140 |
""" |
''' |
141 |
Provides one history command forward. |
Provides one history command forward. |
142 |
|
|
143 |
@return: The command string. |
@return: The command string. |
144 |
@rtype: string |
@rtype: string |
145 |
""" |
''' |
146 |
self.history_level += 1 |
self.history_level += 1 |
147 |
return self._getHistory() |
return self._getHistory() |
148 |
|
|
149 |
def _getHistory(self): |
def _getHistory(self): |
150 |
""" |
''' |
151 |
Get's the command string of the current history level. |
Get's the command string of the current history level. |
152 |
|
|
153 |
@return: Historic command string. |
@return: Historic command string. |
154 |
@rtype: string |
@rtype: string |
155 |
""" |
''' |
156 |
try: |
try: |
157 |
rv = self.IP.user_ns['In'][self.history_level].strip('\n') |
rv = self.IP.user_ns['In'][self.history_level].strip('\n') |
158 |
except IndexError: |
except IndexError: |
161 |
return rv |
return rv |
162 |
|
|
163 |
def updateNamespace(self, ns_dict): |
def updateNamespace(self, ns_dict): |
164 |
""" |
''' |
165 |
Add the current dictionary to the shell namespace. |
Add the current dictionary to the shell namespace. |
166 |
|
|
167 |
@param ns_dict: A dictionary of symbol-values. |
@param ns_dict: A dictionary of symbol-values. |
168 |
@type ns_dict: dictionary |
@type ns_dict: dictionary |
169 |
""" |
''' |
170 |
self.IP.user_ns.update(ns_dict) |
self.IP.user_ns.update(ns_dict) |
171 |
|
|
172 |
def complete(self, line): |
def complete(self, line): |
173 |
""" |
''' |
174 |
Returns an auto completed line and/or posibilities for completion. |
Returns an auto completed line and/or posibilities for completion. |
175 |
|
|
176 |
@param line: Given line so far. |
@param line: Given line so far. |
179 |
@return: Line completed as for as possible, |
@return: Line completed as for as possible, |
180 |
and possible further completions. |
and possible further completions. |
181 |
@rtype: tuple |
@rtype: tuple |
182 |
""" |
''' |
183 |
split_line = self.complete_sep.split(line) |
split_line = self.complete_sep.split(line) |
184 |
possibilities = self.IP.complete(split_line[-1]) |
possibilities = self.IP.complete(split_line[-1]) |
185 |
if possibilities: |
if possibilities: |
186 |
def _commonPrefix(str1, str2): |
def _commonPrefix(str1, str2): |
187 |
""" |
''' |
188 |
Reduction function. returns common prefix of two given strings. |
Reduction function. returns common prefix of two given strings. |
189 |
|
|
190 |
@param str1: First string. |
@param str1: First string. |
194 |
|
|
195 |
@return: Common prefix to both strings. |
@return: Common prefix to both strings. |
196 |
@rtype: string |
@rtype: string |
197 |
""" |
''' |
198 |
for i in range(len(str1)): |
for i in range(len(str1)): |
199 |
if not str2.startswith(str1[:i+1]): |
if not str2.startswith(str1[:i+1]): |
200 |
return str1[:i] |
return str1[:i] |
207 |
|
|
208 |
|
|
209 |
def shell(self, cmd,verbose=0,debug=0,header=''): |
def shell(self, cmd,verbose=0,debug=0,header=''): |
210 |
""" |
''' |
211 |
Replacement method to allow shell commands without them blocking. |
Replacement method to allow shell commands without them blocking. |
212 |
|
|
213 |
@param cmd: Shell command to execute. |
@param cmd: Shell command to execute. |
218 |
@type debug: integer |
@type debug: integer |
219 |
@param header: Header to be printed before output |
@param header: Header to be printed before output |
220 |
@type header: string |
@type header: string |
221 |
""" |
''' |
222 |
stat = 0 |
stat = 0 |
223 |
if verbose or debug: print header+cmd |
if verbose or debug: print header+cmd |
224 |
# flush stdout so we don't mangle python's buffering |
# flush stdout so we don't mangle python's buffering |
229 |
input.close() |
input.close() |
230 |
|
|
231 |
class ConsoleView(gtk.TextView): |
class ConsoleView(gtk.TextView): |
232 |
""" |
''' |
233 |
Specialized text view for console-like workflow. |
Specialized text view for console-like workflow. |
234 |
|
|
235 |
@cvar ANSI_COLORS: Mapping of terminal colors to X11 names. |
@cvar ANSI_COLORS: Mapping of terminal colors to X11 names. |
243 |
@type mark: gtk.TextMark |
@type mark: gtk.TextMark |
244 |
@ivar line_start: Start of command line mark. |
@ivar line_start: Start of command line mark. |
245 |
@type line_start: gtk.TextMark |
@type line_start: gtk.TextMark |
246 |
""" |
''' |
247 |
ANSI_COLORS = {'0;30': 'Black', '0;31': 'Red', |
ANSI_COLORS = {'0;30': 'Black', '0;31': 'Red', |
248 |
'0;32': 'Green', '0;33': 'Brown', |
'0;32': 'Green', '0;33': 'Brown', |
249 |
'0;34': 'Blue', '0;35': 'Purple', |
'0;34': 'Blue', '0;35': 'Purple', |
254 |
'1;36': 'LightCyan', '1;37': 'White'} |
'1;36': 'LightCyan', '1;37': 'White'} |
255 |
|
|
256 |
def __init__(self): |
def __init__(self): |
257 |
""" |
''' |
258 |
Initialize console view. |
Initialize console view. |
259 |
""" |
''' |
260 |
gtk.TextView.__init__(self) |
gtk.TextView.__init__(self) |
261 |
self.modify_font(pango.FontDescription('Mono')) |
self.modify_font(pango.FontDescription('Mono')) |
262 |
self.set_cursor_visible(True) |
self.set_cursor_visible(True) |
280 |
gobject.idle_add(self._write, text, editable) |
gobject.idle_add(self._write, text, editable) |
281 |
|
|
282 |
def _write(self, text, editable=False): |
def _write(self, text, editable=False): |
283 |
""" |
''' |
284 |
Write given text to buffer. |
Write given text to buffer. |
285 |
|
|
286 |
@param text: Text to append. |
@param text: Text to append. |
287 |
@type text: string |
@type text: string |
288 |
@param editable: If true, added text is editable. |
@param editable: If true, added text is editable. |
289 |
@type editable: boolean |
@type editable: boolean |
290 |
""" |
''' |
291 |
segments = self.color_pat.split(text) |
segments = self.color_pat.split(text) |
292 |
segment = segments.pop(0) |
segment = segments.pop(0) |
293 |
start_mark = self.text_buffer.create_mark(None, |
start_mark = self.text_buffer.create_mark(None, |
314 |
gobject.idle_add(self._showPrompt, prompt) |
gobject.idle_add(self._showPrompt, prompt) |
315 |
|
|
316 |
def _showPrompt(self, prompt): |
def _showPrompt(self, prompt): |
317 |
""" |
''' |
318 |
Prints prompt at start of line. |
Prints prompt at start of line. |
319 |
|
|
320 |
@param prompt: Prompt to print. |
@param prompt: Prompt to print. |
321 |
@type prompt: string |
@type prompt: string |
322 |
""" |
''' |
323 |
self._write(prompt) |
self._write(prompt) |
324 |
self.text_buffer.move_mark(self.line_start, |
self.text_buffer.move_mark(self.line_start, |
325 |
self.text_buffer.get_end_iter()) |
self.text_buffer.get_end_iter()) |
328 |
gobject.idle_add(self._changeLine, text) |
gobject.idle_add(self._changeLine, text) |
329 |
|
|
330 |
def _changeLine(self, text): |
def _changeLine(self, text): |
331 |
""" |
''' |
332 |
Replace currently entered command line with given text. |
Replace currently entered command line with given text. |
333 |
|
|
334 |
@param text: Text to use as replacement. |
@param text: Text to use as replacement. |
335 |
@type text: string |
@type text: string |
336 |
""" |
''' |
337 |
iter = self.text_buffer.get_iter_at_mark(self.line_start) |
iter = self.text_buffer.get_iter_at_mark(self.line_start) |
338 |
iter.forward_to_line_end() |
iter.forward_to_line_end() |
339 |
self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter) |
self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter) |
340 |
self._write(text, True) |
self._write(text, True) |
341 |
|
|
342 |
def getCurrentLine(self): |
def getCurrentLine(self): |
343 |
""" |
''' |
344 |
Get text in current command line. |
Get text in current command line. |
345 |
|
|
346 |
@return: Text of current command line. |
@return: Text of current command line. |
347 |
@rtype: string |
@rtype: string |
348 |
""" |
''' |
349 |
rv = self.text_buffer.get_slice( |
rv = self.text_buffer.get_slice( |
350 |
self.text_buffer.get_iter_at_mark(self.line_start), |
self.text_buffer.get_iter_at_mark(self.line_start), |
351 |
self.text_buffer.get_end_iter(), False) |
self.text_buffer.get_end_iter(), False) |
355 |
gobject.idle_add(self._showReturned, text) |
gobject.idle_add(self._showReturned, text) |
356 |
|
|
357 |
def _showReturned(self, text): |
def _showReturned(self, text): |
358 |
""" |
''' |
359 |
Show returned text from last command and print new prompt. |
Show returned text from last command and print new prompt. |
360 |
|
|
361 |
@param text: Text to show. |
@param text: Text to show. |
362 |
@type text: string |
@type text: string |
363 |
""" |
''' |
364 |
iter = self.text_buffer.get_iter_at_mark(self.line_start) |
iter = self.text_buffer.get_iter_at_mark(self.line_start) |
365 |
iter.forward_to_line_end() |
iter.forward_to_line_end() |
366 |
self.text_buffer.apply_tag_by_name( |
self.text_buffer.apply_tag_by_name( |
375 |
self.text_buffer.place_cursor(self.text_buffer.get_end_iter()) |
self.text_buffer.place_cursor(self.text_buffer.get_end_iter()) |
376 |
|
|
377 |
def onKeyPress(self, widget, event): |
def onKeyPress(self, widget, event): |
378 |
""" |
''' |
379 |
Key press callback used for correcting behavior for console-like |
Key press callback used for correcting behavior for console-like |
380 |
interfaces. For example 'home' should go to prompt, not to begining of |
interfaces. For example 'home' should go to prompt, not to begining of |
381 |
line. |
line. |
387 |
|
|
388 |
@return: Return True if event should not trickle. |
@return: Return True if event should not trickle. |
389 |
@rtype: boolean |
@rtype: boolean |
390 |
""" |
''' |
391 |
insert_mark = self.text_buffer.get_insert() |
insert_mark = self.text_buffer.get_insert() |
392 |
insert_iter = self.text_buffer.get_iter_at_mark(insert_mark) |
insert_iter = self.text_buffer.get_iter_at_mark(insert_mark) |
393 |
selection_mark = self.text_buffer.get_selection_bound() |
selection_mark = self.text_buffer.get_selection_bound() |
394 |
selection_iter = self.text_buffer.get_iter_at_mark(selection_mark) |
selection_iter = self.text_buffer.get_iter_at_mark(selection_mark) |
395 |
start_iter = self.text_buffer.get_iter_at_mark(self.line_start) |
start_iter = self.text_buffer.get_iter_at_mark(self.line_start) |
396 |
if event.keyval == gtk.keysyms.Home: |
if event.keyval == gtk.keysyms.Home: |
397 |
if event.state == 0: |
if event.state & gtk.gdk.CONTROL_MASK or event.state & gtk.gdk.MOD1_MASK: |
398 |
self.text_buffer.place_cursor(start_iter) |
pass |
399 |
return True |
elif event.state & gtk.gdk.SHIFT_MASK: |
|
elif event.state == gtk.gdk.SHIFT_MASK: |
|
400 |
self.text_buffer.move_mark(insert_mark, start_iter) |
self.text_buffer.move_mark(insert_mark, start_iter) |
401 |
return True |
return True |
402 |
|
else: |
403 |
|
self.text_buffer.place_cursor(start_iter) |
404 |
|
return True |
405 |
elif event.keyval == gtk.keysyms.Left: |
elif event.keyval == gtk.keysyms.Left: |
406 |
insert_iter.backward_cursor_position() |
insert_iter.backward_cursor_position() |
407 |
if not insert_iter.editable(True): |
if not insert_iter.editable(True): |
422 |
return self.onKeyPressExtend(event) |
return self.onKeyPressExtend(event) |
423 |
|
|
424 |
def onKeyPressExtend(self, event): |
def onKeyPressExtend(self, event): |
425 |
""" |
''' |
426 |
For some reason we can't extend onKeyPress directly (bug #500900). |
For some reason we can't extend onKeyPress directly (bug #500900). |
427 |
""" |
''' |
428 |
pass |
pass |
429 |
|
|
430 |
class IPythonView(ConsoleView, IterableIPShell): |
class IPythonView(ConsoleView, IterableIPShell): |
431 |
""" |
''' |
432 |
Sub-class of both modified IPython shell and L{ConsoleView} this makes |
Sub-class of both modified IPython shell and L{ConsoleView} this makes |
433 |
a GTK+ IPython console. |
a GTK+ IPython console. |
434 |
""" |
''' |
435 |
def __init__(self): |
def __init__(self): |
436 |
""" |
''' |
437 |
Initialize. Redirect I/O to console. |
Initialize. Redirect I/O to console. |
438 |
""" |
''' |
439 |
ConsoleView.__init__(self) |
ConsoleView.__init__(self) |
440 |
self.cout = StringIO() |
self.cout = StringIO() |
441 |
IterableIPShell.__init__(self, cout=self.cout,cerr=self.cout, |
IterableIPShell.__init__(self, cout=self.cout,cerr=self.cout, |
447 |
self.interrupt = False |
self.interrupt = False |
448 |
|
|
449 |
def raw_input(self, prompt=''): |
def raw_input(self, prompt=''): |
450 |
""" |
''' |
451 |
Custom raw_input() replacement. Get's current line from console buffer. |
Custom raw_input() replacement. Get's current line from console buffer. |
452 |
|
|
453 |
@param prompt: Prompt to print. Here for compatability as replacement. |
@param prompt: Prompt to print. Here for compatability as replacement. |
455 |
|
|
456 |
@return: The current command line text. |
@return: The current command line text. |
457 |
@rtype: string |
@rtype: string |
458 |
""" |
''' |
459 |
if self.interrupt: |
if self.interrupt: |
460 |
self.interrupt = False |
self.interrupt = False |
461 |
raise KeyboardInterrupt |
raise KeyboardInterrupt |
462 |
return self.getCurrentLine() |
return self.getCurrentLine() |
463 |
|
|
464 |
def onKeyPressExtend(self, event): |
def onKeyPressExtend(self, event): |
465 |
""" |
''' |
466 |
Key press callback with plenty of shell goodness, like history, |
Key press callback with plenty of shell goodness, like history, |
467 |
autocompletions, etc. |
autocompletions, etc. |
468 |
|
|
473 |
|
|
474 |
@return: True if event should not trickle. |
@return: True if event should not trickle. |
475 |
@rtype: boolean |
@rtype: boolean |
476 |
""" |
''' |
477 |
if event.state & gtk.gdk.CONTROL_MASK and event.keyval == 99: |
if event.state & gtk.gdk.CONTROL_MASK and event.keyval == 99: |
478 |
self.interrupt = True |
self.interrupt = True |
479 |
self._processLine() |
self._processLine() |
501 |
return True |
return True |
502 |
|
|
503 |
def _processLine(self): |
def _processLine(self): |
504 |
""" |
''' |
505 |
Process current command line. |
Process current command line. |
506 |
""" |
''' |
507 |
self.history_pos = 0 |
self.history_pos = 0 |
508 |
self.execute() |
self.execute() |
509 |
rv = self.cout.getvalue() |
rv = self.cout.getvalue() |
511 |
self.showReturned(rv) |
self.showReturned(rv) |
512 |
self.cout.truncate(0) |
self.cout.truncate(0) |
513 |
|
|
|
|
|