709
|
1 # this version is adapted from http://wiki.ipython.org/Old_Embedding/GTK
|
|
2
|
|
3 """
|
|
4 Backend to the console plugin.
|
|
5
|
|
6 @author: Eitan Isaacson
|
|
7 @organization: IBM Corporation
|
|
8 @copyright: Copyright (c) 2007 IBM Corporation
|
|
9 @license: BSD
|
|
10
|
|
11 All rights reserved. This program and the accompanying materials are made
|
|
12 available under the terms of the BSD which accompanies this distribution, and
|
|
13 is available at U{http://www.opensource.org/licenses/bsd-license.php}
|
|
14 """
|
|
15 # this file is a modified version of source code from the Accerciser project
|
|
16 # http://live.gnome.org/accerciser
|
|
17
|
|
18 import gtk
|
|
19 import re
|
|
20 import sys
|
|
21 import os
|
|
22 import pango
|
|
23 from StringIO import StringIO
|
|
24
|
|
25 try:
|
|
26 import IPython
|
|
27 except Exception,e:
|
|
28 raise "Error importing IPython (%s)" % str(e)
|
|
29
|
|
30 ansi_colors = {'0;30': 'Black',
|
|
31 '0;31': 'Red',
|
|
32 '0;32': 'Green',
|
|
33 '0;33': 'Brown',
|
|
34 '0;34': 'Blue',
|
|
35 '0;35': 'Purple',
|
|
36 '0;36': 'Cyan',
|
|
37 '0;37': 'LightGray',
|
|
38 '1;30': 'DarkGray',
|
|
39 '1;31': 'DarkRed',
|
|
40 '1;32': 'SeaGreen',
|
|
41 '1;33': 'Yellow',
|
|
42 '1;34': 'LightBlue',
|
|
43 '1;35': 'MediumPurple',
|
|
44 '1;36': 'LightCyan',
|
|
45 '1;37': 'White'}
|
|
46
|
|
47 class IterableIPShell:
|
|
48 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
|
|
49 cin=None, cout=None,cerr=None, input_func=None):
|
|
50 if input_func:
|
|
51 IPython.iplib.raw_input_original = input_func
|
|
52 if cin:
|
|
53 IPython.Shell.Term.cin = cin
|
|
54 if cout:
|
|
55 IPython.Shell.Term.cout = cout
|
|
56 if cerr:
|
|
57 IPython.Shell.Term.cerr = cerr
|
|
58
|
|
59 if argv is None:
|
|
60 argv=[]
|
|
61
|
|
62 # This is to get rid of the blockage that occurs during
|
|
63 # IPython.Shell.InteractiveShell.user_setup()
|
|
64 IPython.iplib.raw_input = lambda x: None
|
|
65
|
|
66 self.term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
|
|
67 os.environ['TERM'] = 'dumb'
|
|
68 excepthook = sys.excepthook
|
|
69 self.IP = IPython.Shell.make_IPython(argv,user_ns=user_ns,
|
|
70 user_global_ns=user_global_ns,
|
|
71 embedded=True,
|
|
72 shell_class=IPython.Shell.InteractiveShell)
|
|
73 self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd),
|
|
74 header='IPython system call: ',
|
|
75 verbose=self.IP.rc.system_verbose)
|
|
76 sys.excepthook = excepthook
|
|
77 self.iter_more = 0
|
|
78 self.history_level = 0
|
|
79 self.complete_sep = re.compile('[\s\{\}\[\]\(\)]')
|
|
80
|
|
81 def execute(self):
|
|
82 self.history_level = 0
|
|
83 orig_stdout = sys.stdout
|
|
84 sys.stdout = IPython.Shell.Term.cout
|
|
85 try:
|
|
86 line = self.IP.raw_input(None, self.iter_more)
|
|
87 if self.IP.autoindent:
|
|
88 self.IP.readline_startup_hook(None)
|
|
89 except KeyboardInterrupt:
|
|
90 self.IP.write('\nKeyboardInterrupt\n')
|
|
91 self.IP.resetbuffer()
|
|
92 # keep cache in sync with the prompt counter:
|
|
93 self.IP.outputcache.prompt_count -= 1
|
|
94
|
|
95 if self.IP.autoindent:
|
|
96 self.IP.indent_current_nsp = 0
|
|
97 self.iter_more = 0
|
|
98 except:
|
|
99 self.IP.showtraceback()
|
|
100 else:
|
|
101 self.iter_more = self.IP.push(line)
|
|
102 if (self.IP.SyntaxTB.last_syntax_error and
|
|
103 self.IP.rc.autoedit_syntax):
|
|
104 self.IP.edit_syntax_error()
|
|
105 if self.iter_more:
|
|
106 self.prompt = str(self.IP.outputcache.prompt2).strip()
|
|
107 if self.IP.autoindent:
|
|
108 self.IP.readline_startup_hook(self.IP.pre_readline)
|
|
109 else:
|
|
110 self.prompt = str(self.IP.outputcache.prompt1).strip()
|
|
111 sys.stdout = orig_stdout
|
|
112
|
|
113 def historyBack(self):
|
|
114 self.history_level -= 1
|
|
115 return self._getHistory()
|
|
116
|
|
117 def historyForward(self):
|
|
118 self.history_level += 1
|
|
119 return self._getHistory()
|
|
120
|
|
121 def _getHistory(self):
|
|
122 try:
|
|
123 rv = self.IP.user_ns['In'][self.history_level].strip('\n')
|
|
124 except IndexError:
|
|
125 self.history_level = 0
|
|
126 rv = ''
|
|
127 return rv
|
|
128
|
|
129 def updateNamespace(self, ns_dict):
|
|
130 self.IP.user_ns.update(ns_dict)
|
|
131
|
|
132 def complete(self, line):
|
|
133 split_line = self.complete_sep.split(line)
|
|
134 possibilities = self.IP.complete(split_line[-1])
|
|
135 if possibilities:
|
|
136 common_prefix = reduce(self._commonPrefix, possibilities)
|
|
137 completed = line[:-len(split_line[-1])]+common_prefix
|
|
138 else:
|
|
139 completed = line
|
|
140 return completed, possibilities
|
|
141
|
|
142 def _commonPrefix(self, str1, str2):
|
|
143 for i in range(len(str1)):
|
|
144 if not str2.startswith(str1[:i+1]):
|
|
145 return str1[:i]
|
|
146 return str1
|
|
147
|
|
148 def shell(self, cmd,verbose=0,debug=0,header=''):
|
|
149 stat = 0
|
|
150 if verbose or debug: print header+cmd
|
|
151 # flush stdout so we don't mangle python's buffering
|
|
152 if not debug:
|
|
153 input, output = os.popen4(cmd)
|
|
154 print output.read()
|
|
155 output.close()
|
|
156 input.close()
|
|
157
|
|
158 class ConsoleView(gtk.TextView):
|
|
159 def __init__(self):
|
|
160 gtk.TextView.__init__(self)
|
|
161 self.modify_font(pango.FontDescription('Mono'))
|
|
162 self.set_cursor_visible(True)
|
|
163 self.text_buffer = self.get_buffer()
|
|
164 self.mark = self.text_buffer.create_mark('scroll_mark',
|
|
165 self.text_buffer.get_end_iter(),
|
|
166 False)
|
|
167 for code in ansi_colors:
|
|
168 self.text_buffer.create_tag(code,
|
|
169 foreground=ansi_colors[code],
|
|
170 weight=700)
|
|
171 self.text_buffer.create_tag('0')
|
|
172 self.text_buffer.create_tag('notouch', editable=False)
|
|
173 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
|
|
174 self.line_start = \
|
|
175 self.text_buffer.create_mark('line_start',
|
|
176 self.text_buffer.get_end_iter(), True
|
|
177 )
|
|
178 self.connect('key-press-event', self._onKeypress)
|
|
179 self.last_cursor_pos = 0
|
|
180
|
|
181 def write(self, text, editable=False):
|
|
182 segments = self.color_pat.split(text)
|
|
183 segment = segments.pop(0)
|
|
184 start_mark = self.text_buffer.create_mark(None,
|
|
185 self.text_buffer.get_end_iter(),
|
|
186 True)
|
|
187 self.text_buffer.insert(self.text_buffer.get_end_iter(), segment)
|
|
188
|
|
189 if segments:
|
|
190 ansi_tags = self.color_pat.findall(text)
|
|
191 for tag in ansi_tags:
|
|
192 i = segments.index(tag)
|
|
193 self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(),
|
|
194 segments[i+1], tag)
|
|
195 segments.pop(i)
|
|
196 if not editable:
|
|
197 self.text_buffer.apply_tag_by_name('notouch',
|
|
198 self.text_buffer.get_iter_at_mark(start_mark),
|
|
199 self.text_buffer.get_end_iter())
|
|
200 self.text_buffer.delete_mark(start_mark)
|
|
201 self.scroll_mark_onscreen(self.mark)
|
|
202
|
|
203 def showPrompt(self, prompt):
|
|
204 self.write(prompt)
|
|
205 self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter())
|
|
206
|
|
207 def changeLine(self, text):
|
|
208 iter = self.text_buffer.get_iter_at_mark(self.line_start)
|
|
209 iter.forward_to_line_end()
|
|
210 self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter)
|
|
211 self.write(text, True)
|
|
212
|
|
213 def getCurrentLine(self):
|
|
214 rv = self.text_buffer.get_slice(self.text_buffer.get_iter_at_mark(self.line_start),
|
|
215 self.text_buffer.get_end_iter(), False)
|
|
216 return rv
|
|
217
|
|
218 def showReturned(self, text):
|
|
219 iter = self.text_buffer.get_iter_at_mark(self.line_start)
|
|
220 iter.forward_to_line_end()
|
|
221 self.text_buffer.apply_tag_by_name('notouch',
|
|
222 self.text_buffer.get_iter_at_mark(self.line_start),
|
|
223 iter)
|
|
224 self.write('\n'+text)
|
|
225 if text:
|
|
226 self.write('\n')
|
|
227 self.showPrompt(self.prompt)
|
|
228 self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter())
|
|
229 self.text_buffer.place_cursor(self.text_buffer.get_end_iter())
|
|
230
|
|
231 def _onKeypress(self, obj, event):
|
|
232 if not event.string:
|
|
233 return
|
|
234 insert_mark = self.text_buffer.get_insert()
|
|
235 insert_iter = self.text_buffer.get_iter_at_mark(insert_mark)
|
|
236 selection_mark = self.text_buffer.get_selection_bound()
|
|
237 selection_iter = self.text_buffer.get_iter_at_mark(selection_mark)
|
|
238 start_iter = self.text_buffer.get_iter_at_mark(self.line_start)
|
|
239 if start_iter.compare(insert_iter) <= 0 and \
|
|
240 start_iter.compare(selection_iter) <= 0:
|
|
241 return
|
|
242 elif start_iter.compare(insert_iter) > 0 and \
|
|
243 start_iter.compare(selection_iter) > 0:
|
|
244 self.text_buffer.place_cursor(start_iter)
|
|
245 elif insert_iter.compare(selection_iter) < 0:
|
|
246 self.text_buffer.move_mark(insert_mark, start_iter)
|
|
247 elif insert_iter.compare(selection_iter) > 0:
|
|
248 self.text_buffer.move_mark(selection_mark, start_iter)
|
|
249
|
|
250
|
|
251 class IPythonView(ConsoleView, IterableIPShell):
|
|
252 def __init__(self, **kw):
|
|
253 ConsoleView.__init__(self)
|
|
254 self.cout = StringIO()
|
|
255 IterableIPShell.__init__(self, cout=self.cout,cerr=self.cout,
|
|
256 input_func=self.raw_input, **kw)
|
|
257 self.connect('key_press_event', self.keyPress)
|
|
258 self.execute()
|
|
259 self.cout.truncate(0)
|
|
260 self.showPrompt(self.prompt)
|
|
261 self.interrupt = False
|
|
262
|
|
263 def raw_input(self, prompt=''):
|
|
264 if self.interrupt:
|
|
265 self.interrupt = False
|
|
266 raise KeyboardInterrupt
|
|
267 return self.getCurrentLine()
|
|
268
|
|
269 def keyPress(self, widget, event):
|
|
270 if event.state & gtk.gdk.CONTROL_MASK and event.keyval == 99:
|
|
271 self.interrupt = True
|
|
272 self._processLine()
|
|
273 return True
|
|
274 elif event.keyval == gtk.keysyms.Return:
|
|
275 self._processLine()
|
|
276 return True
|
|
277 elif event.keyval == gtk.keysyms.Up:
|
|
278 self.changeLine(self.historyBack())
|
|
279 return True
|
|
280 elif event.keyval == gtk.keysyms.Down:
|
|
281 self.changeLine(self.historyForward())
|
|
282 return True
|
|
283 # todo: Home needs to advance past the ipython prompt
|
|
284 elif event.keyval == gtk.keysyms.Tab:
|
|
285 if not self.getCurrentLine().strip():
|
|
286 return False
|
|
287 completed, possibilities = self.complete(self.getCurrentLine())
|
|
288 if len(possibilities) > 1:
|
|
289 slice = self.getCurrentLine()
|
|
290 self.write('\n')
|
|
291 for symbol in possibilities:
|
|
292 self.write(symbol+'\n')
|
|
293 self.showPrompt(self.prompt)
|
|
294 self.changeLine(completed or slice)
|
|
295 return True
|
|
296
|
|
297 def _processLine(self):
|
|
298 self.history_pos = 0
|
|
299 self.execute()
|
|
300 rv = self.cout.getvalue()
|
|
301 if rv: rv = rv.strip('\n')
|
|
302 self.showReturned(rv)
|
|
303 self.cout.truncate(0)
|