##// END OF EJS Templates
Synchronous stdout/stderr output.
Gael Varoquaux -
Show More
@@ -0,0 +1,67 b''
1 # encoding: utf-8
2
3 """ Redirects stdout/stderr to given write methods."""
4
5 __docformat__ = "restructuredtext en"
6
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
13
14 #-------------------------------------------------------------------------------
15 # Imports
16 #-------------------------------------------------------------------------------
17
18 import sys
19 from IPython.kernel.core.output_trap import OutputTrap
20
21 class FileLike(object):
22 """ FileLike object that redirects all write to a callback.
23
24 Only the write-related methods are implemented, as well as those
25 required to read a StringIO.
26 """
27 closed = False
28
29 def __init__(self, write):
30 self.write = write
31
32 def flush(self):
33 pass
34
35 def close(self):
36 pass
37
38 def writelines(self, lines):
39 for line in lines:
40 self.write(line)
41
42 def isatty(self):
43 return False
44
45 def getvalue(self):
46 return ''
47
48
49 class SyncOutputTrap(OutputTrap):
50 """ Object which redirect text sent to stdout and stderr to write
51 callbacks.
52 """
53
54 def __init__(self, write_out, write_err):
55 # Store callbacks
56 self.out = FileLike(write_out)
57 self.err = FileLike(write_err)
58
59 # Boolean to check if the stdout/stderr hook is set.
60 self.out_set = False
61 self.err_set = False
62
63 def clear(self):
64 """ Clear out the buffers.
65 """
66 pass
67
@@ -1,418 +1,420 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A Wx widget to act as a console and input commands.
3 A Wx widget to act as a console and input commands.
4
4
5 This widget deals with prompts and provides an edit buffer
5 This widget deals with prompts and provides an edit buffer
6 restricted to after the last prompt.
6 restricted to after the last prompt.
7 """
7 """
8
8
9 __docformat__ = "restructuredtext en"
9 __docformat__ = "restructuredtext en"
10
10
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12 # Copyright (C) 2008 The IPython Development Team
12 # Copyright (C) 2008 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is
14 # Distributed under the terms of the BSD License. The full license is
15 # in the file COPYING, distributed as part of this software.
15 # in the file COPYING, distributed as part of this software.
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 import wx
22 import wx
23 import wx.stc as stc
23 import wx.stc as stc
24
24
25 from wx.py import editwindow
25 from wx.py import editwindow
26
26
27 import re
27 import re
28
28
29 # FIXME: Need to provide an API for non user-generated display on the
29 # FIXME: Need to provide an API for non user-generated display on the
30 # screen: this should not be editable by the user.
30 # screen: this should not be editable by the user.
31
31
32 if wx.Platform == '__WXMSW__':
32 if wx.Platform == '__WXMSW__':
33 _DEFAULT_SIZE = 80
33 _DEFAULT_SIZE = 80
34 else:
34 else:
35 _DEFAULT_SIZE = 10
35 _DEFAULT_SIZE = 10
36
36
37 _DEFAULT_STYLE = {
37 _DEFAULT_STYLE = {
38 'stdout' : 'fore:#0000FF',
38 'stdout' : 'fore:#0000FF',
39 'stderr' : 'fore:#007f00',
39 'stderr' : 'fore:#007f00',
40 'trace' : 'fore:#FF0000',
40 'trace' : 'fore:#FF0000',
41
41
42 'default' : 'size:%d' % _DEFAULT_SIZE,
42 'default' : 'size:%d' % _DEFAULT_SIZE,
43 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
43 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
44 'bracebad' : 'fore:#000000,back:#FF0000,bold',
44 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45
45
46 # properties for the various Python lexer styles
46 # properties for the various Python lexer styles
47 'comment' : 'fore:#007F00',
47 'comment' : 'fore:#007F00',
48 'number' : 'fore:#007F7F',
48 'number' : 'fore:#007F7F',
49 'string' : 'fore:#7F007F,italic',
49 'string' : 'fore:#7F007F,italic',
50 'char' : 'fore:#7F007F,italic',
50 'char' : 'fore:#7F007F,italic',
51 'keyword' : 'fore:#00007F,bold',
51 'keyword' : 'fore:#00007F,bold',
52 'triple' : 'fore:#7F0000',
52 'triple' : 'fore:#7F0000',
53 'tripledouble' : 'fore:#7F0000',
53 'tripledouble' : 'fore:#7F0000',
54 'class' : 'fore:#0000FF,bold,underline',
54 'class' : 'fore:#0000FF,bold,underline',
55 'def' : 'fore:#007F7F,bold',
55 'def' : 'fore:#007F7F,bold',
56 'operator' : 'bold'
56 'operator' : 'bold'
57 }
57 }
58
58
59 # new style numbers
59 # new style numbers
60 _STDOUT_STYLE = 15
60 _STDOUT_STYLE = 15
61 _STDERR_STYLE = 16
61 _STDERR_STYLE = 16
62 _TRACE_STYLE = 17
62 _TRACE_STYLE = 17
63
63
64
64
65 # system colors
65 # system colors
66 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
66 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
67
67
68 #-------------------------------------------------------------------------------
68 #-------------------------------------------------------------------------------
69 # The console widget class
69 # The console widget class
70 #-------------------------------------------------------------------------------
70 #-------------------------------------------------------------------------------
71 class ConsoleWidget(editwindow.EditWindow):
71 class ConsoleWidget(editwindow.EditWindow):
72 """ Specialized styled text control view for console-like workflow.
72 """ Specialized styled text control view for console-like workflow.
73
73
74 This widget is mainly interested in dealing with the prompt and
74 This widget is mainly interested in dealing with the prompt and
75 keeping the cursor inside the editing line.
75 keeping the cursor inside the editing line.
76 """
76 """
77
77
78 title = 'Console'
78 title = 'Console'
79
79
80 style = _DEFAULT_STYLE.copy()
80 style = _DEFAULT_STYLE.copy()
81
81
82 # Translation table from ANSI escape sequences to color. Override
82 # Translation table from ANSI escape sequences to color. Override
83 # this to specify your colors.
83 # this to specify your colors.
84 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
84 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
85 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
85 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
86 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
86 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
87 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
87 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
88 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
88 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
89 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
89 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
90 '1;34': [12, 'LIGHT BLUE'], '1;35':
90 '1;34': [12, 'LIGHT BLUE'], '1;35':
91 [13, 'MEDIUM VIOLET RED'],
91 [13, 'MEDIUM VIOLET RED'],
92 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
92 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
93
93
94 # The color of the carret (call _apply_style() after setting)
94 # The color of the carret (call _apply_style() after setting)
95 carret_color = 'BLACK'
95 carret_color = 'BLACK'
96
96
97
97
98 #--------------------------------------------------------------------------
98 #--------------------------------------------------------------------------
99 # Public API
99 # Public API
100 #--------------------------------------------------------------------------
100 #--------------------------------------------------------------------------
101
101
102 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
102 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
103 size=wx.DefaultSize, style=0, ):
103 size=wx.DefaultSize, style=0, ):
104 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
104 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
105 self.configure_scintilla()
105 self.configure_scintilla()
106
106
107 # FIXME: we need to retrieve this from the interpreter.
107 # FIXME: we need to retrieve this from the interpreter.
108 self.prompt = \
108 self.prompt = \
109 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
109 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
110 self.new_prompt(self.prompt % 1)
110 self.new_prompt(self.prompt % 1)
111
111
112 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
112 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
113 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
113 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
114
114
115
115
116 def configure_scintilla(self):
116 def configure_scintilla(self):
117 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
117 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
118 # the widget
118 # the widget
119 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
119 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
120 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
120 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
121 # Also allow Ctrl Shift "=" for poor non US keyboard users.
121 # Also allow Ctrl Shift "=" for poor non US keyboard users.
122 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
122 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
123 stc.STC_CMD_ZOOMIN)
123 stc.STC_CMD_ZOOMIN)
124
124
125 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
125 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
126 # stc.STC_CMD_PAGEUP)
126 # stc.STC_CMD_PAGEUP)
127
127
128 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
128 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
129 # stc.STC_CMD_PAGEDOWN)
129 # stc.STC_CMD_PAGEDOWN)
130
130
131 # Keys: we need to clear some of the keys the that don't play
131 # Keys: we need to clear some of the keys the that don't play
132 # well with a console.
132 # well with a console.
133 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
133 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
134 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
134 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
135 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
135 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
136
136
137
137
138 self.SetEOLMode(stc.STC_EOL_CRLF)
138 self.SetEOLMode(stc.STC_EOL_CRLF)
139 self.SetWrapMode(stc.STC_WRAP_CHAR)
139 self.SetWrapMode(stc.STC_WRAP_CHAR)
140 self.SetWrapMode(stc.STC_WRAP_WORD)
140 self.SetWrapMode(stc.STC_WRAP_WORD)
141 self.SetBufferedDraw(True)
141 self.SetBufferedDraw(True)
142 self.SetUseAntiAliasing(True)
142 self.SetUseAntiAliasing(True)
143 self.SetLayoutCache(stc.STC_CACHE_PAGE)
143 self.SetLayoutCache(stc.STC_CACHE_PAGE)
144 self.SetUndoCollection(False)
144 self.SetUndoCollection(False)
145 self.SetUseTabs(True)
145 self.SetUseTabs(True)
146 self.SetIndent(4)
146 self.SetIndent(4)
147 self.SetTabWidth(4)
147 self.SetTabWidth(4)
148
148
149 self.EnsureCaretVisible()
149 self.EnsureCaretVisible()
150 # we don't want scintilla's autocompletion to choose
150 # we don't want scintilla's autocompletion to choose
151 # automaticaly out of a single choice list, as we pop it up
151 # automaticaly out of a single choice list, as we pop it up
152 # automaticaly
152 # automaticaly
153 self.AutoCompSetChooseSingle(False)
153 self.AutoCompSetChooseSingle(False)
154 self.AutoCompSetMaxHeight(10)
154 self.AutoCompSetMaxHeight(10)
155
155
156 self.SetMargins(3, 3) #text is moved away from border with 3px
156 self.SetMargins(3, 3) #text is moved away from border with 3px
157 # Suppressing Scintilla margins
157 # Suppressing Scintilla margins
158 self.SetMarginWidth(0, 0)
158 self.SetMarginWidth(0, 0)
159 self.SetMarginWidth(1, 0)
159 self.SetMarginWidth(1, 0)
160 self.SetMarginWidth(2, 0)
160 self.SetMarginWidth(2, 0)
161
161
162 self._apply_style()
162 self._apply_style()
163
163
164 # Xterm escape sequences
164 # Xterm escape sequences
165 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
165 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
166 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
166 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
167
167
168 #self.SetEdgeMode(stc.STC_EDGE_LINE)
168 #self.SetEdgeMode(stc.STC_EDGE_LINE)
169 #self.SetEdgeColumn(80)
169 #self.SetEdgeColumn(80)
170
170
171 # styles
171 # styles
172 p = self.style
172 p = self.style
173 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
173 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
174 self.StyleClearAll()
174 self.StyleClearAll()
175 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
175 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
176 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
176 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
177 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
177 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
178
178
179 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
179 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
180 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
180 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
181 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
181 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
182 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
182 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
183 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
183 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
184 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
184 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
185 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
185 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
186 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
186 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
187 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
187 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
188 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
188 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
189 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
189 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
190 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
190 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
191 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
191 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
192 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
192 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
193
193
194
194
195 def write(self, text):
195 def write(self, text):
196 """ Write given text to buffer, while translating the ansi escape
196 """ Write given text to buffer, while translating the ansi escape
197 sequences.
197 sequences.
198 """
198 """
199 title = self.title_pat.split(text)
199 title = self.title_pat.split(text)
200 if len(title)>0:
200 if len(title)>0:
201 self.title = title[-1]
201 self.title = title[-1]
202
202
203 text = self.title_pat.sub('', text)
203 text = self.title_pat.sub('', text)
204 segments = self.color_pat.split(text)
204 segments = self.color_pat.split(text)
205 segment = segments.pop(0)
205 segment = segments.pop(0)
206 self.StartStyling(self.GetLength(), 0xFF)
206 self.StartStyling(self.GetLength(), 0xFF)
207 self.AppendText(segment)
207 self.AppendText(segment)
208
208
209 if segments:
209 if segments:
210 ansi_tags = self.color_pat.findall(text)
210 ansi_tags = self.color_pat.findall(text)
211
211
212 for tag in ansi_tags:
212 for tag in ansi_tags:
213 i = segments.index(tag)
213 i = segments.index(tag)
214 self.StartStyling(self.GetLength(), 0xFF)
214 self.StartStyling(self.GetLength(), 0xFF)
215 self.AppendText(segments[i+1])
215 self.AppendText(segments[i+1])
216
216
217 if tag != '0':
217 if tag != '0':
218 self.SetStyling(len(segments[i+1]),
218 self.SetStyling(len(segments[i+1]),
219 self.ANSI_STYLES[tag][0])
219 self.ANSI_STYLES[tag][0])
220
220
221 segments.pop(i)
221 segments.pop(i)
222
222
223 self.GotoPos(self.GetLength())
223 self.GotoPos(self.GetLength())
224 wx.Yield()
224
225
225
226
226 def new_prompt(self, prompt):
227 def new_prompt(self, prompt):
227 """ Prints a prompt at start of line, and move the start of the
228 """ Prints a prompt at start of line, and move the start of the
228 current block there.
229 current block there.
229
230
230 The prompt can be give with ascii escape sequences.
231 The prompt can be give with ascii escape sequences.
231 """
232 """
232 self.write(prompt)
233 self.write(prompt)
233 # now we update our cursor giving end of prompt
234 # now we update our cursor giving end of prompt
234 self.current_prompt_pos = self.GetLength()
235 self.current_prompt_pos = self.GetLength()
235 self.current_prompt_line = self.GetCurrentLine()
236 self.current_prompt_line = self.GetCurrentLine()
236 wx.Yield()
237 wx.Yield()
237 self.EnsureCaretVisible()
238 self.EnsureCaretVisible()
238
239
239
240
240 def replace_current_edit_buffer(self, text):
241 def replace_current_edit_buffer(self, text):
241 """ Replace currently entered command line with given text.
242 """ Replace currently entered command line with given text.
242 """
243 """
243 self.SetSelection(self.current_prompt_pos, self.GetLength())
244 self.SetSelection(self.current_prompt_pos, self.GetLength())
244 self.ReplaceSelection(text)
245 self.ReplaceSelection(text)
245 self.GotoPos(self.GetLength())
246 self.GotoPos(self.GetLength())
246
247
247
248
248 def get_current_edit_buffer(self):
249 def get_current_edit_buffer(self):
249 """ Returns the text in current edit buffer.
250 """ Returns the text in current edit buffer.
250 """
251 """
251 return self.GetTextRange(self.current_prompt_pos,
252 return self.GetTextRange(self.current_prompt_pos,
252 self.GetLength())
253 self.GetLength())
253
254
254
255
255 #--------------------------------------------------------------------------
256 #--------------------------------------------------------------------------
256 # Private API
257 # Private API
257 #--------------------------------------------------------------------------
258 #--------------------------------------------------------------------------
258
259
259 def _apply_style(self):
260 def _apply_style(self):
260 """ Applies the colors for the different text elements and the
261 """ Applies the colors for the different text elements and the
261 carret.
262 carret.
262 """
263 """
263 self.SetCaretForeground(self.carret_color)
264 self.SetCaretForeground(self.carret_color)
264
265
265 #self.StyleClearAll()
266 #self.StyleClearAll()
266 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
267 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
267 "fore:#FF0000,back:#0000FF,bold")
268 "fore:#FF0000,back:#0000FF,bold")
268 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
269 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
269 "fore:#000000,back:#FF0000,bold")
270 "fore:#000000,back:#FF0000,bold")
270
271
271 for style in self.ANSI_STYLES.values():
272 for style in self.ANSI_STYLES.values():
272 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
273 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
273
274
274
275
275 def write_completion(self, possibilities):
276 def write_completion(self, possibilities):
276 # FIXME: This is non Wx specific and needs to be moved into
277 # FIXME: This is non Wx specific and needs to be moved into
277 # the base class.
278 # the base class.
278 current_buffer = self.get_current_edit_buffer()
279 current_buffer = self.get_current_edit_buffer()
279
280
280 self.write('\n')
281 self.write('\n')
281 max_len = len(max(possibilities, key=len)) + 1
282 max_len = len(max(possibilities, key=len)) + 1
282
283
283 #now we check how much symbol we can put on a line...
284 #now we check how much symbol we can put on a line...
284 chars_per_line = self.GetSize()[0]/self.GetCharWidth()
285 chars_per_line = self.GetSize()[0]/self.GetCharWidth()
285 symbols_per_line = max(1, chars_per_line/max_len)
286 symbols_per_line = max(1, chars_per_line/max_len)
286
287
287 pos = 1
288 pos = 1
288 buf = []
289 buf = []
289 for symbol in possibilities:
290 for symbol in possibilities:
290 if pos < symbols_per_line:
291 if pos < symbols_per_line:
291 buf.append(symbol.ljust(max_len))
292 buf.append(symbol.ljust(max_len))
292 pos += 1
293 pos += 1
293 else:
294 else:
294 buf.append(symbol.rstrip() +'\n')
295 buf.append(symbol.rstrip() +'\n')
295 pos = 1
296 pos = 1
296 self.write(''.join(buf))
297 self.write(''.join(buf))
297 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
298 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
298 self.replace_current_edit_buffer(current_buffer)
299 self.replace_current_edit_buffer(current_buffer)
299
300
300
301
301 def pop_completion(self, possibilities, offset=0):
302 def pop_completion(self, possibilities, offset=0):
302 """ Pops up an autocompletion menu. Offset is the offset
303 """ Pops up an autocompletion menu. Offset is the offset
303 in characters of the position at which the menu should
304 in characters of the position at which the menu should
304 appear, relativ to the cursor.
305 appear, relativ to the cursor.
305 """
306 """
306 self.AutoCompSetIgnoreCase(False)
307 self.AutoCompSetIgnoreCase(False)
307 self.AutoCompSetAutoHide(False)
308 self.AutoCompSetAutoHide(False)
308 self.AutoCompSetMaxHeight(len(possibilities))
309 self.AutoCompSetMaxHeight(len(possibilities))
309 self.AutoCompShow(offset, " ".join(possibilities))
310 self.AutoCompShow(offset, " ".join(possibilities))
310
311
311
312
312 def scroll_to_bottom(self):
313 def scroll_to_bottom(self):
313 maxrange = self.GetScrollRange(wx.VERTICAL)
314 maxrange = self.GetScrollRange(wx.VERTICAL)
314 self.ScrollLines(maxrange)
315 self.ScrollLines(maxrange)
315
316
316
317
317 def _on_enter(self):
318 def _on_enter(self):
318 """ Called when the return key is hit.
319 """ Called when the return key is hit.
319 """
320 """
320 pass
321 pass
321
322
322
323
323 def _on_key_down(self, event, skip=True):
324 def _on_key_down(self, event, skip=True):
324 """ Key press callback used for correcting behavior for
325 """ Key press callback used for correcting behavior for
325 console-like interfaces: the cursor is constraint to be after
326 console-like interfaces: the cursor is constraint to be after
326 the last prompt.
327 the last prompt.
327
328
328 Return True if event as been catched.
329 Return True if event as been catched.
329 """
330 """
330 catched = True
331 catched = True
331 # Intercept some specific keys.
332 # Intercept some specific keys.
332 if event.KeyCode == ord('L') and event.ControlDown() :
333 if event.KeyCode == ord('L') and event.ControlDown() :
333 self.scroll_to_bottom()
334 self.scroll_to_bottom()
334 elif event.KeyCode == ord('K') and event.ControlDown() :
335 elif event.KeyCode == ord('K') and event.ControlDown() :
335 self.replace_current_edit_buffer('')
336 self.replace_current_edit_buffer('')
336 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
337 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
337 self.ScrollPages(-1)
338 self.ScrollPages(-1)
338 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
339 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
339 self.ScrollPages(1)
340 self.ScrollPages(1)
340 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
341 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
341 self.ScrollLines(-1)
342 self.ScrollLines(-1)
342 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
343 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
343 self.ScrollLinees(1)
344 self.ScrollLinees(1)
344 else:
345 else:
345 catched = False
346 catched = False
346
347
347 if self.AutoCompActive():
348 if self.AutoCompActive():
348 event.Skip()
349 event.Skip()
349 else:
350 else:
350 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
351 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
351 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
352 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
352 catched = True
353 catched = True
354 self.CallTipCancel()
353 self.write('\n')
355 self.write('\n')
354 self._on_enter()
356 self._on_enter()
355
357
356 elif event.KeyCode == wx.WXK_HOME:
358 elif event.KeyCode == wx.WXK_HOME:
357 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
359 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
358 self.GotoPos(self.current_prompt_pos)
360 self.GotoPos(self.current_prompt_pos)
359 catched = True
361 catched = True
360
362
361 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
363 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
362 # FIXME: This behavior is not ideal: if the selection
364 # FIXME: This behavior is not ideal: if the selection
363 # is already started, it will jump.
365 # is already started, it will jump.
364 self.SetSelectionStart(self.current_prompt_pos)
366 self.SetSelectionStart(self.current_prompt_pos)
365 self.SetSelectionEnd(self.GetCurrentPos())
367 self.SetSelectionEnd(self.GetCurrentPos())
366 catched = True
368 catched = True
367
369
368 elif event.KeyCode == wx.WXK_UP:
370 elif event.KeyCode == wx.WXK_UP:
369 if self.GetCurrentLine() > self.current_prompt_line:
371 if self.GetCurrentLine() > self.current_prompt_line:
370 if self.GetCurrentLine() == self.current_prompt_line + 1 \
372 if self.GetCurrentLine() == self.current_prompt_line + 1 \
371 and self.GetColumn(self.GetCurrentPos()) < \
373 and self.GetColumn(self.GetCurrentPos()) < \
372 self.GetColumn(self.current_prompt_pos):
374 self.GetColumn(self.current_prompt_pos):
373 self.GotoPos(self.current_prompt_pos)
375 self.GotoPos(self.current_prompt_pos)
374 else:
376 else:
375 event.Skip()
377 event.Skip()
376 catched = True
378 catched = True
377
379
378 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
380 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
379 if self.GetCurrentPos() > self.current_prompt_pos:
381 if self.GetCurrentPos() > self.current_prompt_pos:
380 event.Skip()
382 event.Skip()
381 catched = True
383 catched = True
382
384
383 if skip and not catched:
385 if skip and not catched:
384 event.Skip()
386 event.Skip()
385
387
386 return catched
388 return catched
387
389
388
390
389 def _on_key_up(self, event, skip=True):
391 def _on_key_up(self, event, skip=True):
390 """ If cursor is outside the editing region, put it back.
392 """ If cursor is outside the editing region, put it back.
391 """
393 """
392 event.Skip()
394 event.Skip()
393 if self.GetCurrentPos() < self.current_prompt_pos:
395 if self.GetCurrentPos() < self.current_prompt_pos:
394 self.GotoPos(self.current_prompt_pos)
396 self.GotoPos(self.current_prompt_pos)
395
397
396
398
397
399
398
400
399 if __name__ == '__main__':
401 if __name__ == '__main__':
400 # Some simple code to test the console widget.
402 # Some simple code to test the console widget.
401 class MainWindow(wx.Frame):
403 class MainWindow(wx.Frame):
402 def __init__(self, parent, id, title):
404 def __init__(self, parent, id, title):
403 wx.Frame.__init__(self, parent, id, title, size=(300,250))
405 wx.Frame.__init__(self, parent, id, title, size=(300,250))
404 self._sizer = wx.BoxSizer(wx.VERTICAL)
406 self._sizer = wx.BoxSizer(wx.VERTICAL)
405 self.console_widget = ConsoleWidget(self)
407 self.console_widget = ConsoleWidget(self)
406 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
408 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
407 self.SetSizer(self._sizer)
409 self.SetSizer(self._sizer)
408 self.SetAutoLayout(1)
410 self.SetAutoLayout(1)
409 self.Show(True)
411 self.Show(True)
410
412
411 app = wx.PySimpleApp()
413 app = wx.PySimpleApp()
412 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
414 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
413 w.SetSize((780, 460))
415 w.SetSize((780, 460))
414 w.Show()
416 w.Show()
415
417
416 app.MainLoop()
418 app.MainLoop()
417
419
418
420
@@ -1,218 +1,218 b''
1 # encoding: utf-8 -*- test-case-name:
1 # encoding: utf-8 -*- test-case-name:
2 # FIXME: Need to add tests.
2 # FIXME: Need to add tests.
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
4
4
5 """Classes to provide a Wx frontend to the
5 """Classes to provide a Wx frontend to the
6 IPython.kernel.core.interpreter.
6 IPython.kernel.core.interpreter.
7
7
8 """
8 """
9
9
10 __docformat__ = "restructuredtext en"
10 __docformat__ = "restructuredtext en"
11
11
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2008 The IPython Development Team
13 # Copyright (C) 2008 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22
22
23
23
24 import wx
24 import wx
25 import re
25 import re
26 from wx import stc
26 from wx import stc
27 from console_widget import ConsoleWidget
27 from console_widget import ConsoleWidget
28 import __builtin__
28 import __builtin__
29
29
30 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
30 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
31
31
32 #_COMMAND_BG = '#FAFAF1' # Nice green
32 #_COMMAND_BG = '#FAFAF1' # Nice green
33 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
33 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
34
34
35 _RUNNING_BUFFER_MARKER = 31
35 _RUNNING_BUFFER_MARKER = 31
36
36
37
37
38 #-------------------------------------------------------------------------------
38 #-------------------------------------------------------------------------------
39 # Classes to implement the Wx frontend
39 # Classes to implement the Wx frontend
40 #-------------------------------------------------------------------------------
40 #-------------------------------------------------------------------------------
41 class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
41 class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
42
42
43 output_prompt = \
43 output_prompt = \
44 '\n\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
44 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
45
45
46 #--------------------------------------------------------------------------
46 #--------------------------------------------------------------------------
47 # Public API
47 # Public API
48 #--------------------------------------------------------------------------
48 #--------------------------------------------------------------------------
49
49
50 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
50 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
51 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
51 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
52 *args, **kwds):
52 *args, **kwds):
53 """ Create Shell instance.
53 """ Create Shell instance.
54 """
54 """
55 ConsoleWidget.__init__(self, parent, id, pos, size, style)
55 ConsoleWidget.__init__(self, parent, id, pos, size, style)
56 PrefilterFrontEnd.__init__(self)
56 PrefilterFrontEnd.__init__(self)
57
57
58 # Capture Character keys
58 # Capture Character keys
59 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
59 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
60
60
61 # Marker for running buffer.
61 # Marker for running buffer.
62 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
62 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
63 background=_RUNNING_BUFFER_BG)
63 background=_RUNNING_BUFFER_BG)
64
64
65
65
66
66
67 def do_completion(self):
67 def do_completion(self):
68 """ Do code completion.
68 """ Do code completion.
69 """
69 """
70 line = self.get_current_edit_buffer()
70 line = self.get_current_edit_buffer()
71 new_line, completions = self.complete(line)
71 new_line, completions = self.complete(line)
72 if len(completions)>1:
72 if len(completions)>1:
73 self.write_completion(completions)
73 self.write_completion(completions)
74 self.replace_current_edit_buffer(new_line)
74 self.replace_current_edit_buffer(new_line)
75
75
76
76
77 def do_calltip(self):
77 def do_calltip(self):
78 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
78 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
79 symbol = self.get_current_edit_buffer()
79 symbol = self.get_current_edit_buffer()
80 symbol_string = separators.split(symbol)[-1]
80 symbol_string = separators.split(symbol)[-1]
81 base_symbol_string = symbol_string.split('.')[0]
81 base_symbol_string = symbol_string.split('.')[0]
82 if base_symbol_string in self.shell.user_ns:
82 if base_symbol_string in self.shell.user_ns:
83 symbol = self.shell.user_ns[base_symbol_string]
83 symbol = self.shell.user_ns[base_symbol_string]
84 elif base_symbol_string in self.shell.user_global_ns:
84 elif base_symbol_string in self.shell.user_global_ns:
85 symbol = self.shell.user_global_ns[base_symbol_string]
85 symbol = self.shell.user_global_ns[base_symbol_string]
86 elif base_symbol_string in __builtin__.__dict__:
86 elif base_symbol_string in __builtin__.__dict__:
87 symbol = __builtin__.__dict__[base_symbol_string]
87 symbol = __builtin__.__dict__[base_symbol_string]
88 else:
88 else:
89 return False
89 return False
90 for name in symbol_string.split('.')[1:] + ['__doc__']:
90 for name in symbol_string.split('.')[1:] + ['__doc__']:
91 symbol = getattr(symbol, name)
91 symbol = getattr(symbol, name)
92 try:
92 try:
93 self.AutoCompCancel()
93 self.AutoCompCancel()
94 wx.Yield()
94 wx.Yield()
95 self.CallTipShow(self.GetCurrentPos(), symbol)
95 self.CallTipShow(self.GetCurrentPos(), symbol)
96 except:
96 except:
97 # The retrieve symbol couldn't be converted to a string
97 # The retrieve symbol couldn't be converted to a string
98 pass
98 pass
99
99
100
100
101 def popup_completion(self, create=False):
101 def popup_completion(self, create=False):
102 """ Updates the popup completion menu if it exists. If create is
102 """ Updates the popup completion menu if it exists. If create is
103 true, open the menu.
103 true, open the menu.
104 """
104 """
105 line = self.get_current_edit_buffer()
105 line = self.get_current_edit_buffer()
106 if (self.AutoCompActive() and not line[-1] == '.') \
106 if (self.AutoCompActive() and not line[-1] == '.') \
107 or create==True:
107 or create==True:
108 suggestion, completions = self.complete(line)
108 suggestion, completions = self.complete(line)
109 offset=0
109 offset=0
110 if completions:
110 if completions:
111 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
111 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
112 residual = complete_sep.split(line)[-1]
112 residual = complete_sep.split(line)[-1]
113 offset = len(residual)
113 offset = len(residual)
114 self.pop_completion(completions, offset=offset)
114 self.pop_completion(completions, offset=offset)
115
115
116
116
117 def execute(self, python_string, raw_string=None):
117 def execute(self, python_string, raw_string=None):
118 self.CallTipCancel()
118 self.CallTipCancel()
119 self._cursor = wx.BusyCursor()
119 self._cursor = wx.BusyCursor()
120 if raw_string is None:
120 if raw_string is None:
121 raw_string = python_string
121 raw_string = python_string
122 end_line = self.current_prompt_line \
122 end_line = self.current_prompt_line \
123 + max(1, len(raw_string.split('\n'))-1)
123 + max(1, len(raw_string.split('\n'))-1)
124 for i in range(self.current_prompt_line, end_line):
124 for i in range(self.current_prompt_line, end_line):
125 self.MarkerAdd(i, 31)
125 self.MarkerAdd(i, 31)
126 # Update the display:
126 # Update the display:
127 wx.Yield()
127 wx.Yield()
128 # Remove the trailing "\n" for cleaner display
128 ## Remove the trailing "\n" for cleaner display
129 self.SetSelection(self.GetLength()-1, self.GetLength())
129 #self.SetSelection(self.GetLength()-1, self.GetLength())
130 self.ReplaceSelection('')
130 #self.ReplaceSelection('')
131 self.GotoPos(self.GetLength())
131 self.GotoPos(self.GetLength())
132 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
132 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
133
133
134
134
135 def after_execute(self):
135 def after_execute(self):
136 PrefilterFrontEnd.after_execute(self)
136 PrefilterFrontEnd.after_execute(self)
137 if hasattr(self, '_cursor'):
137 if hasattr(self, '_cursor'):
138 del self._cursor
138 del self._cursor
139
139
140 #--------------------------------------------------------------------------
140 #--------------------------------------------------------------------------
141 # Private API
141 # Private API
142 #--------------------------------------------------------------------------
142 #--------------------------------------------------------------------------
143
143
144
144
145 def _on_key_down(self, event, skip=True):
145 def _on_key_down(self, event, skip=True):
146 """ Capture the character events, let the parent
146 """ Capture the character events, let the parent
147 widget handle them, and put our logic afterward.
147 widget handle them, and put our logic afterward.
148 """
148 """
149 current_line_number = self.GetCurrentLine()
149 current_line_number = self.GetCurrentLine()
150 if event.KeyCode == ord('('):
150 if event.KeyCode == ord('('):
151 event.Skip()
151 event.Skip()
152 self.do_calltip()
152 self.do_calltip()
153 elif self.AutoCompActive():
153 elif self.AutoCompActive():
154 event.Skip()
154 event.Skip()
155 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
155 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
156 wx.CallAfter(self.popup_completion, create=True)
156 wx.CallAfter(self.popup_completion, create=True)
157 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
157 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
158 wx.WXK_RIGHT):
158 wx.WXK_RIGHT):
159 wx.CallAfter(self.popup_completion)
159 wx.CallAfter(self.popup_completion)
160 else:
160 else:
161 # Up history
161 # Up history
162 if event.KeyCode == wx.WXK_UP and (
162 if event.KeyCode == wx.WXK_UP and (
163 ( current_line_number == self.current_prompt_line and
163 ( current_line_number == self.current_prompt_line and
164 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
164 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
165 or event.ControlDown() ):
165 or event.ControlDown() ):
166 new_buffer = self.get_history_previous(
166 new_buffer = self.get_history_previous(
167 self.get_current_edit_buffer())
167 self.get_current_edit_buffer())
168 if new_buffer is not None:
168 if new_buffer is not None:
169 self.replace_current_edit_buffer(new_buffer)
169 self.replace_current_edit_buffer(new_buffer)
170 if self.GetCurrentLine() > self.current_prompt_line:
170 if self.GetCurrentLine() > self.current_prompt_line:
171 # Go to first line, for seemless history up.
171 # Go to first line, for seemless history up.
172 self.GotoPos(self.current_prompt_pos)
172 self.GotoPos(self.current_prompt_pos)
173 # Down history
173 # Down history
174 elif event.KeyCode == wx.WXK_DOWN and (
174 elif event.KeyCode == wx.WXK_DOWN and (
175 ( current_line_number == self.LineCount -1 and
175 ( current_line_number == self.LineCount -1 and
176 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
176 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
177 or event.ControlDown() ):
177 or event.ControlDown() ):
178 new_buffer = self.get_history_next()
178 new_buffer = self.get_history_next()
179 if new_buffer is not None:
179 if new_buffer is not None:
180 self.replace_current_edit_buffer(new_buffer)
180 self.replace_current_edit_buffer(new_buffer)
181 elif event.KeyCode == ord('\t'):
181 elif event.KeyCode == ord('\t'):
182 last_line = self.get_current_edit_buffer().split('\n')[-1]
182 last_line = self.get_current_edit_buffer().split('\n')[-1]
183 if not re.match(r'^\s*$', last_line):
183 if not re.match(r'^\s*$', last_line):
184 self.do_completion()
184 self.do_completion()
185 else:
185 else:
186 event.Skip()
186 event.Skip()
187 else:
187 else:
188 ConsoleWidget._on_key_down(self, event, skip=skip)
188 ConsoleWidget._on_key_down(self, event, skip=skip)
189
189
190
190
191 def _on_key_up(self, event, skip=True):
191 def _on_key_up(self, event, skip=True):
192 if event.KeyCode == 59:
192 if event.KeyCode == 59:
193 # Intercepting '.'
193 # Intercepting '.'
194 event.Skip()
194 event.Skip()
195 self.popup_completion(create=True)
195 self.popup_completion(create=True)
196 else:
196 else:
197 ConsoleWidget._on_key_up(self, event, skip=skip)
197 ConsoleWidget._on_key_up(self, event, skip=skip)
198
198
199
199
200 if __name__ == '__main__':
200 if __name__ == '__main__':
201 class MainWindow(wx.Frame):
201 class MainWindow(wx.Frame):
202 def __init__(self, parent, id, title):
202 def __init__(self, parent, id, title):
203 wx.Frame.__init__(self, parent, id, title, size=(300,250))
203 wx.Frame.__init__(self, parent, id, title, size=(300,250))
204 self._sizer = wx.BoxSizer(wx.VERTICAL)
204 self._sizer = wx.BoxSizer(wx.VERTICAL)
205 self.shell = IPythonWxController(self)
205 self.shell = IPythonWxController(self)
206 self._sizer.Add(self.shell, 1, wx.EXPAND)
206 self._sizer.Add(self.shell, 1, wx.EXPAND)
207 self.SetSizer(self._sizer)
207 self.SetSizer(self._sizer)
208 self.SetAutoLayout(1)
208 self.SetAutoLayout(1)
209 self.Show(True)
209 self.Show(True)
210
210
211 app = wx.PySimpleApp()
211 app = wx.PySimpleApp()
212 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
212 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
213 frame.shell.SetFocus()
213 frame.shell.SetFocus()
214 frame.SetSize((680, 460))
214 frame.SetSize((680, 460))
215 self = frame.shell
215 self = frame.shell
216
216
217 app.MainLoop()
217 app.MainLoop()
218
218
@@ -1,749 +1,747 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """Central interpreter object for an IPython engine.
3 """Central interpreter object for an IPython engine.
4
4
5 The interpreter is the object whose job is to process lines of user input and
5 The interpreter is the object whose job is to process lines of user input and
6 actually execute them in the user's namespace.
6 actually execute them in the user's namespace.
7 """
7 """
8
8
9 __docformat__ = "restructuredtext en"
9 __docformat__ = "restructuredtext en"
10
10
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12 # Copyright (C) 2008 The IPython Development Team
12 # Copyright (C) 2008 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 # Standard library imports.
22 # Standard library imports.
23 from compiler.ast import Discard
24 from types import FunctionType
23 from types import FunctionType
25
24
26 import __builtin__
25 import __builtin__
27 import codeop
26 import codeop
28 import compiler
27 import compiler
29 import pprint
30 import sys
28 import sys
31 import traceback
29 import traceback
32
30
33 # Local imports.
31 # Local imports.
34 from IPython.kernel.core import ultraTB
32 from IPython.kernel.core import ultraTB
35 from IPython.kernel.core.display_trap import DisplayTrap
33 from IPython.kernel.core.display_trap import DisplayTrap
36 from IPython.kernel.core.macro import Macro
34 from IPython.kernel.core.macro import Macro
37 from IPython.kernel.core.prompts import CachedOutput
35 from IPython.kernel.core.prompts import CachedOutput
38 from IPython.kernel.core.traceback_trap import TracebackTrap
36 from IPython.kernel.core.traceback_trap import TracebackTrap
39 from IPython.kernel.core.util import Bunch, system_shell
37 from IPython.kernel.core.util import Bunch, system_shell
40 from IPython.external.Itpl import ItplNS
38 from IPython.external.Itpl import ItplNS
41
39
42 # Global constants
40 # Global constants
43 COMPILER_ERROR = 'error'
41 COMPILER_ERROR = 'error'
44 INCOMPLETE_INPUT = 'incomplete'
42 INCOMPLETE_INPUT = 'incomplete'
45 COMPLETE_INPUT = 'complete'
43 COMPLETE_INPUT = 'complete'
46
44
47 ##############################################################################
45 ##############################################################################
48 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
46 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
49 # not
47 # not
50
48
51 rc = Bunch()
49 rc = Bunch()
52 rc.cache_size = 100
50 rc.cache_size = 100
53 rc.pprint = True
51 rc.pprint = True
54 rc.separate_in = '\n'
52 rc.separate_in = '\n'
55 rc.separate_out = '\n'
53 rc.separate_out = '\n'
56 rc.separate_out2 = ''
54 rc.separate_out2 = ''
57 rc.prompt_in1 = r'In [\#]: '
55 rc.prompt_in1 = r'In [\#]: '
58 rc.prompt_in2 = r' .\\D.: '
56 rc.prompt_in2 = r' .\\D.: '
59 rc.prompt_out = ''
57 rc.prompt_out = ''
60 rc.prompts_pad_left = False
58 rc.prompts_pad_left = False
61
59
62 ##############################################################################
60 ##############################################################################
63
61
64 # Top-level utilities
62 # Top-level utilities
65 def default_display_formatters():
63 def default_display_formatters():
66 """ Return a list of default display formatters.
64 """ Return a list of default display formatters.
67 """
65 """
68
66
69 from display_formatter import PPrintDisplayFormatter, ReprDisplayFormatter
67 from display_formatter import PPrintDisplayFormatter, ReprDisplayFormatter
70 return [PPrintDisplayFormatter(), ReprDisplayFormatter()]
68 return [PPrintDisplayFormatter(), ReprDisplayFormatter()]
71
69
72 def default_traceback_formatters():
70 def default_traceback_formatters():
73 """ Return a list of default traceback formatters.
71 """ Return a list of default traceback formatters.
74 """
72 """
75
73
76 from traceback_formatter import PlainTracebackFormatter
74 from traceback_formatter import PlainTracebackFormatter
77 return [PlainTracebackFormatter()]
75 return [PlainTracebackFormatter()]
78
76
79 # Top-level classes
77 # Top-level classes
80 class NotDefined(object): pass
78 class NotDefined(object): pass
81
79
82 class Interpreter(object):
80 class Interpreter(object):
83 """ An interpreter object.
81 """ An interpreter object.
84
82
85 fixme: needs to negotiate available formatters with frontends.
83 fixme: needs to negotiate available formatters with frontends.
86
84
87 Important: the interpeter should be built so that it exposes a method
85 Important: the interpeter should be built so that it exposes a method
88 for each attribute/method of its sub-object. This way it can be
86 for each attribute/method of its sub-object. This way it can be
89 replaced by a network adapter.
87 replaced by a network adapter.
90 """
88 """
91
89
92 def __init__(self, user_ns=None, global_ns=None,translator=None,
90 def __init__(self, user_ns=None, global_ns=None,translator=None,
93 magic=None, display_formatters=None,
91 magic=None, display_formatters=None,
94 traceback_formatters=None, output_trap=None, history=None,
92 traceback_formatters=None, output_trap=None, history=None,
95 message_cache=None, filename='<string>', config=None):
93 message_cache=None, filename='<string>', config=None):
96
94
97 # The local/global namespaces for code execution
95 # The local/global namespaces for code execution
98 local_ns = user_ns # compatibility name
96 local_ns = user_ns # compatibility name
99 if local_ns is None:
97 if local_ns is None:
100 local_ns = {}
98 local_ns = {}
101 self.user_ns = local_ns
99 self.user_ns = local_ns
102 # The local namespace
100 # The local namespace
103 if global_ns is None:
101 if global_ns is None:
104 global_ns = {}
102 global_ns = {}
105 self.user_global_ns = global_ns
103 self.user_global_ns = global_ns
106
104
107 # An object that will translate commands into executable Python.
105 # An object that will translate commands into executable Python.
108 # The current translator does not work properly so for now we are going
106 # The current translator does not work properly so for now we are going
109 # without!
107 # without!
110 # if translator is None:
108 # if translator is None:
111 # from IPython.kernel.core.translator import IPythonTranslator
109 # from IPython.kernel.core.translator import IPythonTranslator
112 # translator = IPythonTranslator()
110 # translator = IPythonTranslator()
113 self.translator = translator
111 self.translator = translator
114
112
115 # An object that maintains magic commands.
113 # An object that maintains magic commands.
116 if magic is None:
114 if magic is None:
117 from IPython.kernel.core.magic import Magic
115 from IPython.kernel.core.magic import Magic
118 magic = Magic(self)
116 magic = Magic(self)
119 self.magic = magic
117 self.magic = magic
120
118
121 # A list of formatters for the displayhook.
119 # A list of formatters for the displayhook.
122 if display_formatters is None:
120 if display_formatters is None:
123 display_formatters = default_display_formatters()
121 display_formatters = default_display_formatters()
124 self.display_formatters = display_formatters
122 self.display_formatters = display_formatters
125
123
126 # A list of formatters for tracebacks.
124 # A list of formatters for tracebacks.
127 if traceback_formatters is None:
125 if traceback_formatters is None:
128 traceback_formatters = default_traceback_formatters()
126 traceback_formatters = default_traceback_formatters()
129 self.traceback_formatters = traceback_formatters
127 self.traceback_formatters = traceback_formatters
130
128
131 # The object trapping stdout/stderr.
129 # The object trapping stdout/stderr.
132 if output_trap is None:
130 if output_trap is None:
133 from IPython.kernel.core.output_trap import OutputTrap
131 from IPython.kernel.core.output_trap import OutputTrap
134 output_trap = OutputTrap()
132 output_trap = OutputTrap()
135 self.output_trap = output_trap
133 self.output_trap = output_trap
136
134
137 # An object that manages the history.
135 # An object that manages the history.
138 if history is None:
136 if history is None:
139 from IPython.kernel.core.history import InterpreterHistory
137 from IPython.kernel.core.history import InterpreterHistory
140 history = InterpreterHistory()
138 history = InterpreterHistory()
141 self.history = history
139 self.history = history
142 self.get_history_item = history.get_history_item
140 self.get_history_item = history.get_history_item
143 self.get_history_input_cache = history.get_input_cache
141 self.get_history_input_cache = history.get_input_cache
144 self.get_history_input_after = history.get_input_after
142 self.get_history_input_after = history.get_input_after
145
143
146 # An object that caches all of the return messages.
144 # An object that caches all of the return messages.
147 if message_cache is None:
145 if message_cache is None:
148 from IPython.kernel.core.message_cache import SimpleMessageCache
146 from IPython.kernel.core.message_cache import SimpleMessageCache
149 message_cache = SimpleMessageCache()
147 message_cache = SimpleMessageCache()
150 self.message_cache = message_cache
148 self.message_cache = message_cache
151
149
152 # The "filename" of the code that is executed in this interpreter.
150 # The "filename" of the code that is executed in this interpreter.
153 self.filename = filename
151 self.filename = filename
154
152
155 # An object that contains much configuration information.
153 # An object that contains much configuration information.
156 if config is None:
154 if config is None:
157 # fixme: Move this constant elsewhere!
155 # fixme: Move this constant elsewhere!
158 config = Bunch(ESC_MAGIC='%')
156 config = Bunch(ESC_MAGIC='%')
159 self.config = config
157 self.config = config
160
158
161 # Hook managers.
159 # Hook managers.
162 # fixme: make the display callbacks configurable. In the meantime,
160 # fixme: make the display callbacks configurable. In the meantime,
163 # enable macros.
161 # enable macros.
164 self.display_trap = DisplayTrap(
162 self.display_trap = DisplayTrap(
165 formatters=self.display_formatters,
163 formatters=self.display_formatters,
166 callbacks=[self._possible_macro],
164 callbacks=[self._possible_macro],
167 )
165 )
168 self.traceback_trap = TracebackTrap(
166 self.traceback_trap = TracebackTrap(
169 formatters=self.traceback_formatters)
167 formatters=self.traceback_formatters)
170
168
171 # This is used temporarily for reformating exceptions in certain
169 # This is used temporarily for reformating exceptions in certain
172 # cases. It will go away once the ultraTB stuff is ported
170 # cases. It will go away once the ultraTB stuff is ported
173 # to ipython1
171 # to ipython1
174 self.tbHandler = ultraTB.FormattedTB(color_scheme='NoColor',
172 self.tbHandler = ultraTB.FormattedTB(color_scheme='NoColor',
175 mode='Context',
173 mode='Context',
176 tb_offset=2)
174 tb_offset=2)
177
175
178 # An object that can compile commands and remember __future__
176 # An object that can compile commands and remember __future__
179 # statements.
177 # statements.
180 self.command_compiler = codeop.CommandCompiler()
178 self.command_compiler = codeop.CommandCompiler()
181
179
182 # A replacement for the raw_input() and input() builtins. Change these
180 # A replacement for the raw_input() and input() builtins. Change these
183 # attributes later to configure them.
181 # attributes later to configure them.
184 self.raw_input_builtin = raw_input
182 self.raw_input_builtin = raw_input
185 self.input_builtin = input
183 self.input_builtin = input
186
184
187 # The number of the current cell.
185 # The number of the current cell.
188 self.current_cell_number = 1
186 self.current_cell_number = 1
189
187
190 # Initialize cache, set in/out prompts and printing system
188 # Initialize cache, set in/out prompts and printing system
191 self.outputcache = CachedOutput(self,
189 self.outputcache = CachedOutput(self,
192 rc.cache_size,
190 rc.cache_size,
193 rc.pprint,
191 rc.pprint,
194 input_sep = rc.separate_in,
192 input_sep = rc.separate_in,
195 output_sep = rc.separate_out,
193 output_sep = rc.separate_out,
196 output_sep2 = rc.separate_out2,
194 output_sep2 = rc.separate_out2,
197 ps1 = rc.prompt_in1,
195 ps1 = rc.prompt_in1,
198 ps2 = rc.prompt_in2,
196 ps2 = rc.prompt_in2,
199 ps_out = rc.prompt_out,
197 ps_out = rc.prompt_out,
200 pad_left = rc.prompts_pad_left)
198 pad_left = rc.prompts_pad_left)
201
199
202 # Need to decide later if this is the right approach, but clients
200 # Need to decide later if this is the right approach, but clients
203 # commonly use sys.ps1/2, so it may be best to just set them here
201 # commonly use sys.ps1/2, so it may be best to just set them here
204 sys.ps1 = self.outputcache.prompt1.p_str
202 sys.ps1 = self.outputcache.prompt1.p_str
205 sys.ps2 = self.outputcache.prompt2.p_str
203 sys.ps2 = self.outputcache.prompt2.p_str
206
204
207 # This is the message dictionary assigned temporarily when running the
205 # This is the message dictionary assigned temporarily when running the
208 # code.
206 # code.
209 self.message = None
207 self.message = None
210
208
211 self.setup_namespace()
209 self.setup_namespace()
212
210
213
211
214 #### Public 'Interpreter' interface ########################################
212 #### Public 'Interpreter' interface ########################################
215
213
216 def formatTraceback(self, et, ev, tb, message=''):
214 def formatTraceback(self, et, ev, tb, message=''):
217 """Put a formatted version of the traceback into value and reraise.
215 """Put a formatted version of the traceback into value and reraise.
218
216
219 When exceptions have to be sent over the network, the traceback
217 When exceptions have to be sent over the network, the traceback
220 needs to be put into the value of the exception in a nicely
218 needs to be put into the value of the exception in a nicely
221 formatted way. The method takes the type, value and tb of an
219 formatted way. The method takes the type, value and tb of an
222 exception and puts a string representation of the tb into the
220 exception and puts a string representation of the tb into the
223 value of the exception and reraises it.
221 value of the exception and reraises it.
224
222
225 Currently this method uses the ultraTb formatter from IPython trunk.
223 Currently this method uses the ultraTb formatter from IPython trunk.
226 Eventually it should simply use the traceback formatters in core
224 Eventually it should simply use the traceback formatters in core
227 that are loaded into self.tracback_trap.formatters.
225 that are loaded into self.tracback_trap.formatters.
228 """
226 """
229 tbinfo = self.tbHandler.text(et,ev,tb)
227 tbinfo = self.tbHandler.text(et,ev,tb)
230 ev._ipython_traceback_text = tbinfo
228 ev._ipython_traceback_text = tbinfo
231 return et, ev, tb
229 return et, ev, tb
232
230
233 def execute(self, commands, raiseException=True):
231 def execute(self, commands, raiseException=True):
234 """ Execute some IPython commands.
232 """ Execute some IPython commands.
235
233
236 1. Translate them into Python.
234 1. Translate them into Python.
237 2. Run them.
235 2. Run them.
238 3. Trap stdout/stderr.
236 3. Trap stdout/stderr.
239 4. Trap sys.displayhook().
237 4. Trap sys.displayhook().
240 5. Trap exceptions.
238 5. Trap exceptions.
241 6. Return a message object.
239 6. Return a message object.
242
240
243 Parameters
241 Parameters
244 ----------
242 ----------
245 commands : str
243 commands : str
246 The raw commands that the user typed into the prompt.
244 The raw commands that the user typed into the prompt.
247
245
248 Returns
246 Returns
249 -------
247 -------
250 message : dict
248 message : dict
251 The dictionary of responses. See the README.txt in this directory
249 The dictionary of responses. See the README.txt in this directory
252 for an explanation of the format.
250 for an explanation of the format.
253 """
251 """
254
252
255 # Create a message dictionary with all of the information we will be
253 # Create a message dictionary with all of the information we will be
256 # returning to the frontend and other listeners.
254 # returning to the frontend and other listeners.
257 message = self.setup_message()
255 message = self.setup_message()
258
256
259 # Massage the input and store the raw and translated commands into
257 # Massage the input and store the raw and translated commands into
260 # a dict.
258 # a dict.
261 user_input = dict(raw=commands)
259 user_input = dict(raw=commands)
262 if self.translator is not None:
260 if self.translator is not None:
263 python = self.translator(commands, message)
261 python = self.translator(commands, message)
264 if python is None:
262 if python is None:
265 # Something went wrong with the translation. The translator
263 # Something went wrong with the translation. The translator
266 # should have added an appropriate entry to the message object.
264 # should have added an appropriate entry to the message object.
267 return message
265 return message
268 else:
266 else:
269 python = commands
267 python = commands
270 user_input['translated'] = python
268 user_input['translated'] = python
271 message['input'] = user_input
269 message['input'] = user_input
272
270
273 # Set the message object so that any magics executed in the code have
271 # Set the message object so that any magics executed in the code have
274 # access.
272 # access.
275 self.message = message
273 self.message = message
276
274
277 # Set all of the output/exception traps.
275 # Set all of the output/exception traps.
278 self.set_traps()
276 self.set_traps()
279
277
280 # Actually execute the Python code.
278 # Actually execute the Python code.
281 status = self.execute_python(python)
279 status = self.execute_python(python)
282
280
283 # Unset all of the traps.
281 # Unset all of the traps.
284 self.unset_traps()
282 self.unset_traps()
285
283
286 # Unset the message object.
284 # Unset the message object.
287 self.message = None
285 self.message = None
288
286
289 # Update the history variables in the namespace.
287 # Update the history variables in the namespace.
290 # E.g. In, Out, _, __, ___
288 # E.g. In, Out, _, __, ___
291 if self.history is not None:
289 if self.history is not None:
292 self.history.update_history(self, python)
290 self.history.update_history(self, python)
293
291
294 # Let all of the traps contribute to the message and then clear their
292 # Let all of the traps contribute to the message and then clear their
295 # stored information.
293 # stored information.
296 self.output_trap.add_to_message(message)
294 self.output_trap.add_to_message(message)
297 self.output_trap.clear()
295 self.output_trap.clear()
298 self.display_trap.add_to_message(message)
296 self.display_trap.add_to_message(message)
299 self.display_trap.clear()
297 self.display_trap.clear()
300 self.traceback_trap.add_to_message(message)
298 self.traceback_trap.add_to_message(message)
301 # Pull out the type, value and tb of the current exception
299 # Pull out the type, value and tb of the current exception
302 # before clearing it.
300 # before clearing it.
303 einfo = self.traceback_trap.args
301 einfo = self.traceback_trap.args
304 self.traceback_trap.clear()
302 self.traceback_trap.clear()
305
303
306 # Cache the message.
304 # Cache the message.
307 self.message_cache.add_message(self.current_cell_number, message)
305 self.message_cache.add_message(self.current_cell_number, message)
308
306
309 # Bump the number.
307 # Bump the number.
310 self.current_cell_number += 1
308 self.current_cell_number += 1
311
309
312 # This conditional lets the execute method either raise any
310 # This conditional lets the execute method either raise any
313 # exception that has occured in user code OR return the message
311 # exception that has occured in user code OR return the message
314 # dict containing the traceback and other useful info.
312 # dict containing the traceback and other useful info.
315 if raiseException and einfo:
313 if raiseException and einfo:
316 raise einfo[0],einfo[1],einfo[2]
314 raise einfo[0],einfo[1],einfo[2]
317 else:
315 else:
318 return message
316 return message
319
317
320 def generate_prompt(self, is_continuation):
318 def generate_prompt(self, is_continuation):
321 """Calculate and return a string with the prompt to display.
319 """Calculate and return a string with the prompt to display.
322
320
323 :Parameters:
321 :Parameters:
324 is_continuation : bool
322 is_continuation : bool
325 Whether the input line is continuing multiline input or not, so
323 Whether the input line is continuing multiline input or not, so
326 that a proper continuation prompt can be computed."""
324 that a proper continuation prompt can be computed."""
327
325
328 if is_continuation:
326 if is_continuation:
329 return str(self.outputcache.prompt2)
327 return str(self.outputcache.prompt2)
330 else:
328 else:
331 return str(self.outputcache.prompt1)
329 return str(self.outputcache.prompt1)
332
330
333 def execute_python(self, python):
331 def execute_python(self, python):
334 """ Actually run the Python code in the namespace.
332 """ Actually run the Python code in the namespace.
335
333
336 :Parameters:
334 :Parameters:
337
335
338 python : str
336 python : str
339 Pure, exec'able Python code. Special IPython commands should have
337 Pure, exec'able Python code. Special IPython commands should have
340 already been translated into pure Python.
338 already been translated into pure Python.
341 """
339 """
342
340
343 # We use a CommandCompiler instance to compile the code so as to keep
341 # We use a CommandCompiler instance to compile the code so as to keep
344 # track of __future__ imports.
342 # track of __future__ imports.
345 try:
343 try:
346 commands = self.split_commands(python)
344 commands = self.split_commands(python)
347 except (SyntaxError, IndentationError), e:
345 except (SyntaxError, IndentationError), e:
348 # Save the exc_info so compilation related exceptions can be
346 # Save the exc_info so compilation related exceptions can be
349 # reraised
347 # reraised
350 self.traceback_trap.args = sys.exc_info()
348 self.traceback_trap.args = sys.exc_info()
351 self.pack_exception(self.message,e)
349 self.pack_exception(self.message,e)
352 return None
350 return None
353
351
354 for cmd in commands:
352 for cmd in commands:
355 try:
353 try:
356 code = self.command_compiler(cmd, self.filename, 'single')
354 code = self.command_compiler(cmd, self.filename, 'single')
357 except (SyntaxError, OverflowError, ValueError), e:
355 except (SyntaxError, OverflowError, ValueError), e:
358 self.traceback_trap.args = sys.exc_info()
356 self.traceback_trap.args = sys.exc_info()
359 self.pack_exception(self.message,e)
357 self.pack_exception(self.message,e)
360 # No point in continuing if one block raised
358 # No point in continuing if one block raised
361 return None
359 return None
362 else:
360 else:
363 self.execute_block(code)
361 self.execute_block(code)
364
362
365 def execute_block(self,code):
363 def execute_block(self,code):
366 """Execute a single block of code in the user namespace.
364 """Execute a single block of code in the user namespace.
367
365
368 Return value: a flag indicating whether the code to be run completed
366 Return value: a flag indicating whether the code to be run completed
369 successfully:
367 successfully:
370
368
371 - 0: successful execution.
369 - 0: successful execution.
372 - 1: an error occurred.
370 - 1: an error occurred.
373 """
371 """
374
372
375 outflag = 1 # start by assuming error, success will reset it
373 outflag = 1 # start by assuming error, success will reset it
376 try:
374 try:
377 exec code in self.user_ns
375 exec code in self.user_ns
378 outflag = 0
376 outflag = 0
379 except SystemExit:
377 except SystemExit:
380 self.resetbuffer()
378 self.resetbuffer()
381 self.traceback_trap.args = sys.exc_info()
379 self.traceback_trap.args = sys.exc_info()
382 except:
380 except:
383 self.traceback_trap.args = sys.exc_info()
381 self.traceback_trap.args = sys.exc_info()
384
382
385 return outflag
383 return outflag
386
384
387 def execute_macro(self, macro):
385 def execute_macro(self, macro):
388 """ Execute the value of a macro.
386 """ Execute the value of a macro.
389
387
390 Parameters
388 Parameters
391 ----------
389 ----------
392 macro : Macro
390 macro : Macro
393 """
391 """
394
392
395 python = macro.value
393 python = macro.value
396 if self.translator is not None:
394 if self.translator is not None:
397 python = self.translator(python)
395 python = self.translator(python)
398 self.execute_python(python)
396 self.execute_python(python)
399
397
400 def getCommand(self, i=None):
398 def getCommand(self, i=None):
401 """Gets the ith message in the message_cache.
399 """Gets the ith message in the message_cache.
402
400
403 This is implemented here for compatibility with the old ipython1 shell
401 This is implemented here for compatibility with the old ipython1 shell
404 I am not sure we need this though. I even seem to remember that we
402 I am not sure we need this though. I even seem to remember that we
405 were going to get rid of it.
403 were going to get rid of it.
406 """
404 """
407 return self.message_cache.get_message(i)
405 return self.message_cache.get_message(i)
408
406
409 def reset(self):
407 def reset(self):
410 """Reset the interpreter.
408 """Reset the interpreter.
411
409
412 Currently this only resets the users variables in the namespace.
410 Currently this only resets the users variables in the namespace.
413 In the future we might want to also reset the other stateful
411 In the future we might want to also reset the other stateful
414 things like that the Interpreter has, like In, Out, etc.
412 things like that the Interpreter has, like In, Out, etc.
415 """
413 """
416 self.user_ns.clear()
414 self.user_ns.clear()
417 self.setup_namespace()
415 self.setup_namespace()
418
416
419 def complete(self,line,text=None, pos=None):
417 def complete(self,line,text=None, pos=None):
420 """Complete the given text.
418 """Complete the given text.
421
419
422 :Parameters:
420 :Parameters:
423
421
424 text : str
422 text : str
425 Text fragment to be completed on. Typically this is
423 Text fragment to be completed on. Typically this is
426 """
424 """
427 # fixme: implement
425 # fixme: implement
428 raise NotImplementedError
426 raise NotImplementedError
429
427
430 def push(self, ns):
428 def push(self, ns):
431 """ Put value into the namespace with name key.
429 """ Put value into the namespace with name key.
432
430
433 Parameters
431 Parameters
434 ----------
432 ----------
435 **kwds
433 **kwds
436 """
434 """
437
435
438 self.user_ns.update(ns)
436 self.user_ns.update(ns)
439
437
440 def push_function(self, ns):
438 def push_function(self, ns):
441 # First set the func_globals for all functions to self.user_ns
439 # First set the func_globals for all functions to self.user_ns
442 new_kwds = {}
440 new_kwds = {}
443 for k, v in ns.iteritems():
441 for k, v in ns.iteritems():
444 if not isinstance(v, FunctionType):
442 if not isinstance(v, FunctionType):
445 raise TypeError("function object expected")
443 raise TypeError("function object expected")
446 new_kwds[k] = FunctionType(v.func_code, self.user_ns)
444 new_kwds[k] = FunctionType(v.func_code, self.user_ns)
447 self.user_ns.update(new_kwds)
445 self.user_ns.update(new_kwds)
448
446
449 def pack_exception(self,message,exc):
447 def pack_exception(self,message,exc):
450 message['exception'] = exc.__class__
448 message['exception'] = exc.__class__
451 message['exception_value'] = \
449 message['exception_value'] = \
452 traceback.format_exception_only(exc.__class__, exc)
450 traceback.format_exception_only(exc.__class__, exc)
453
451
454 def feed_block(self, source, filename='<input>', symbol='single'):
452 def feed_block(self, source, filename='<input>', symbol='single'):
455 """Compile some source in the interpreter.
453 """Compile some source in the interpreter.
456
454
457 One several things can happen:
455 One several things can happen:
458
456
459 1) The input is incorrect; compile_command() raised an
457 1) The input is incorrect; compile_command() raised an
460 exception (SyntaxError or OverflowError).
458 exception (SyntaxError or OverflowError).
461
459
462 2) The input is incomplete, and more input is required;
460 2) The input is incomplete, and more input is required;
463 compile_command() returned None. Nothing happens.
461 compile_command() returned None. Nothing happens.
464
462
465 3) The input is complete; compile_command() returned a code
463 3) The input is complete; compile_command() returned a code
466 object. The code is executed by calling self.runcode() (which
464 object. The code is executed by calling self.runcode() (which
467 also handles run-time exceptions, except for SystemExit).
465 also handles run-time exceptions, except for SystemExit).
468
466
469 The return value is:
467 The return value is:
470
468
471 - True in case 2
469 - True in case 2
472
470
473 - False in the other cases, unless an exception is raised, where
471 - False in the other cases, unless an exception is raised, where
474 None is returned instead. This can be used by external callers to
472 None is returned instead. This can be used by external callers to
475 know whether to continue feeding input or not.
473 know whether to continue feeding input or not.
476
474
477 The return value can be used to decide whether to use sys.ps1 or
475 The return value can be used to decide whether to use sys.ps1 or
478 sys.ps2 to prompt the next line."""
476 sys.ps2 to prompt the next line."""
479
477
480 self.message = self.setup_message()
478 self.message = self.setup_message()
481
479
482 try:
480 try:
483 code = self.command_compiler(source,filename,symbol)
481 code = self.command_compiler(source,filename,symbol)
484 except (OverflowError, SyntaxError, IndentationError, ValueError ), e:
482 except (OverflowError, SyntaxError, IndentationError, ValueError ), e:
485 # Case 1
483 # Case 1
486 self.traceback_trap.args = sys.exc_info()
484 self.traceback_trap.args = sys.exc_info()
487 self.pack_exception(self.message,e)
485 self.pack_exception(self.message,e)
488 return COMPILER_ERROR,False
486 return COMPILER_ERROR,False
489
487
490 if code is None:
488 if code is None:
491 # Case 2: incomplete input. This means that the input can span
489 # Case 2: incomplete input. This means that the input can span
492 # multiple lines. But we still need to decide when to actually
490 # multiple lines. But we still need to decide when to actually
493 # stop taking user input. Later we'll add auto-indentation support
491 # stop taking user input. Later we'll add auto-indentation support
494 # somehow. In the meantime, we'll just stop if there are two lines
492 # somehow. In the meantime, we'll just stop if there are two lines
495 # of pure whitespace at the end.
493 # of pure whitespace at the end.
496 last_two = source.rsplit('\n',2)[-2:]
494 last_two = source.rsplit('\n',2)[-2:]
497 print 'last two:',last_two # dbg
495 print 'last two:',last_two # dbg
498 if len(last_two)==2 and all(s.isspace() for s in last_two):
496 if len(last_two)==2 and all(s.isspace() for s in last_two):
499 return COMPLETE_INPUT,False
497 return COMPLETE_INPUT,False
500 else:
498 else:
501 return INCOMPLETE_INPUT, True
499 return INCOMPLETE_INPUT, True
502 else:
500 else:
503 # Case 3
501 # Case 3
504 return COMPLETE_INPUT, False
502 return COMPLETE_INPUT, False
505
503
506 def pull(self, keys):
504 def pull(self, keys):
507 """ Get an item out of the namespace by key.
505 """ Get an item out of the namespace by key.
508
506
509 Parameters
507 Parameters
510 ----------
508 ----------
511 key : str
509 key : str
512
510
513 Returns
511 Returns
514 -------
512 -------
515 value : object
513 value : object
516
514
517 Raises
515 Raises
518 ------
516 ------
519 TypeError if the key is not a string.
517 TypeError if the key is not a string.
520 NameError if the object doesn't exist.
518 NameError if the object doesn't exist.
521 """
519 """
522
520
523 if isinstance(keys, str):
521 if isinstance(keys, str):
524 result = self.user_ns.get(keys, NotDefined())
522 result = self.user_ns.get(keys, NotDefined())
525 if isinstance(result, NotDefined):
523 if isinstance(result, NotDefined):
526 raise NameError('name %s is not defined' % keys)
524 raise NameError('name %s is not defined' % keys)
527 elif isinstance(keys, (list, tuple)):
525 elif isinstance(keys, (list, tuple)):
528 result = []
526 result = []
529 for key in keys:
527 for key in keys:
530 if not isinstance(key, str):
528 if not isinstance(key, str):
531 raise TypeError("objects must be keyed by strings.")
529 raise TypeError("objects must be keyed by strings.")
532 else:
530 else:
533 r = self.user_ns.get(key, NotDefined())
531 r = self.user_ns.get(key, NotDefined())
534 if isinstance(r, NotDefined):
532 if isinstance(r, NotDefined):
535 raise NameError('name %s is not defined' % key)
533 raise NameError('name %s is not defined' % key)
536 else:
534 else:
537 result.append(r)
535 result.append(r)
538 if len(keys)==1:
536 if len(keys)==1:
539 result = result[0]
537 result = result[0]
540 else:
538 else:
541 raise TypeError("keys must be a strong or a list/tuple of strings")
539 raise TypeError("keys must be a strong or a list/tuple of strings")
542 return result
540 return result
543
541
544 def pull_function(self, keys):
542 def pull_function(self, keys):
545 return self.pull(keys)
543 return self.pull(keys)
546
544
547 #### Interactive user API ##################################################
545 #### Interactive user API ##################################################
548
546
549 def ipsystem(self, command):
547 def ipsystem(self, command):
550 """ Execute a command in a system shell while expanding variables in the
548 """ Execute a command in a system shell while expanding variables in the
551 current namespace.
549 current namespace.
552
550
553 Parameters
551 Parameters
554 ----------
552 ----------
555 command : str
553 command : str
556 """
554 """
557
555
558 # Expand $variables.
556 # Expand $variables.
559 command = self.var_expand(command)
557 command = self.var_expand(command)
560
558
561 system_shell(command,
559 system_shell(command,
562 header='IPython system call: ',
560 header='IPython system call: ',
563 verbose=self.rc.system_verbose,
561 verbose=self.rc.system_verbose,
564 )
562 )
565
563
566 def ipmagic(self, arg_string):
564 def ipmagic(self, arg_string):
567 """ Call a magic function by name.
565 """ Call a magic function by name.
568
566
569 ipmagic('name -opt foo bar') is equivalent to typing at the ipython
567 ipmagic('name -opt foo bar') is equivalent to typing at the ipython
570 prompt:
568 prompt:
571
569
572 In[1]: %name -opt foo bar
570 In[1]: %name -opt foo bar
573
571
574 To call a magic without arguments, simply use ipmagic('name').
572 To call a magic without arguments, simply use ipmagic('name').
575
573
576 This provides a proper Python function to call IPython's magics in any
574 This provides a proper Python function to call IPython's magics in any
577 valid Python code you can type at the interpreter, including loops and
575 valid Python code you can type at the interpreter, including loops and
578 compound statements. It is added by IPython to the Python builtin
576 compound statements. It is added by IPython to the Python builtin
579 namespace upon initialization.
577 namespace upon initialization.
580
578
581 Parameters
579 Parameters
582 ----------
580 ----------
583 arg_string : str
581 arg_string : str
584 A string containing the name of the magic function to call and any
582 A string containing the name of the magic function to call and any
585 additional arguments to be passed to the magic.
583 additional arguments to be passed to the magic.
586
584
587 Returns
585 Returns
588 -------
586 -------
589 something : object
587 something : object
590 The return value of the actual object.
588 The return value of the actual object.
591 """
589 """
592
590
593 # Taken from IPython.
591 # Taken from IPython.
594 raise NotImplementedError('Not ported yet')
592 raise NotImplementedError('Not ported yet')
595
593
596 args = arg_string.split(' ', 1)
594 args = arg_string.split(' ', 1)
597 magic_name = args[0]
595 magic_name = args[0]
598 magic_name = magic_name.lstrip(self.config.ESC_MAGIC)
596 magic_name = magic_name.lstrip(self.config.ESC_MAGIC)
599
597
600 try:
598 try:
601 magic_args = args[1]
599 magic_args = args[1]
602 except IndexError:
600 except IndexError:
603 magic_args = ''
601 magic_args = ''
604 fn = getattr(self.magic, 'magic_'+magic_name, None)
602 fn = getattr(self.magic, 'magic_'+magic_name, None)
605 if fn is None:
603 if fn is None:
606 self.error("Magic function `%s` not found." % magic_name)
604 self.error("Magic function `%s` not found." % magic_name)
607 else:
605 else:
608 magic_args = self.var_expand(magic_args)
606 magic_args = self.var_expand(magic_args)
609 return fn(magic_args)
607 return fn(magic_args)
610
608
611
609
612 #### Private 'Interpreter' interface #######################################
610 #### Private 'Interpreter' interface #######################################
613
611
614 def setup_message(self):
612 def setup_message(self):
615 """Return a message object.
613 """Return a message object.
616
614
617 This method prepares and returns a message dictionary. This dict
615 This method prepares and returns a message dictionary. This dict
618 contains the various fields that are used to transfer information about
616 contains the various fields that are used to transfer information about
619 execution, results, tracebacks, etc, to clients (either in or out of
617 execution, results, tracebacks, etc, to clients (either in or out of
620 process ones). Because of the need to work with possibly out of
618 process ones). Because of the need to work with possibly out of
621 process clients, this dict MUST contain strictly pickle-safe values.
619 process clients, this dict MUST contain strictly pickle-safe values.
622 """
620 """
623
621
624 return dict(number=self.current_cell_number)
622 return dict(number=self.current_cell_number)
625
623
626 def setup_namespace(self):
624 def setup_namespace(self):
627 """ Add things to the namespace.
625 """ Add things to the namespace.
628 """
626 """
629
627
630 self.user_ns.setdefault('__name__', '__main__')
628 self.user_ns.setdefault('__name__', '__main__')
631 self.user_ns.setdefault('__builtins__', __builtin__)
629 self.user_ns.setdefault('__builtins__', __builtin__)
632 self.user_ns['__IP'] = self
630 self.user_ns['__IP'] = self
633 if self.raw_input_builtin is not None:
631 if self.raw_input_builtin is not None:
634 self.user_ns['raw_input'] = self.raw_input_builtin
632 self.user_ns['raw_input'] = self.raw_input_builtin
635 if self.input_builtin is not None:
633 if self.input_builtin is not None:
636 self.user_ns['input'] = self.input_builtin
634 self.user_ns['input'] = self.input_builtin
637
635
638 builtin_additions = dict(
636 builtin_additions = dict(
639 ipmagic=self.ipmagic,
637 ipmagic=self.ipmagic,
640 )
638 )
641 __builtin__.__dict__.update(builtin_additions)
639 __builtin__.__dict__.update(builtin_additions)
642
640
643 if self.history is not None:
641 if self.history is not None:
644 self.history.setup_namespace(self.user_ns)
642 self.history.setup_namespace(self.user_ns)
645
643
646 def set_traps(self):
644 def set_traps(self):
647 """ Set all of the output, display, and traceback traps.
645 """ Set all of the output, display, and traceback traps.
648 """
646 """
649
647
650 self.output_trap.set()
648 self.output_trap.set()
651 self.display_trap.set()
649 self.display_trap.set()
652 self.traceback_trap.set()
650 self.traceback_trap.set()
653
651
654 def unset_traps(self):
652 def unset_traps(self):
655 """ Unset all of the output, display, and traceback traps.
653 """ Unset all of the output, display, and traceback traps.
656 """
654 """
657
655
658 self.output_trap.unset()
656 self.output_trap.unset()
659 self.display_trap.unset()
657 self.display_trap.unset()
660 self.traceback_trap.unset()
658 self.traceback_trap.unset()
661
659
662 def split_commands(self, python):
660 def split_commands(self, python):
663 """ Split multiple lines of code into discrete commands that can be
661 """ Split multiple lines of code into discrete commands that can be
664 executed singly.
662 executed singly.
665
663
666 Parameters
664 Parameters
667 ----------
665 ----------
668 python : str
666 python : str
669 Pure, exec'able Python code.
667 Pure, exec'able Python code.
670
668
671 Returns
669 Returns
672 -------
670 -------
673 commands : list of str
671 commands : list of str
674 Separate commands that can be exec'ed independently.
672 Separate commands that can be exec'ed independently.
675 """
673 """
676
674
677 # compiler.parse treats trailing spaces after a newline as a
675 # compiler.parse treats trailing spaces after a newline as a
678 # SyntaxError. This is different than codeop.CommandCompiler, which
676 # SyntaxError. This is different than codeop.CommandCompiler, which
679 # will compile the trailng spaces just fine. We simply strip any
677 # will compile the trailng spaces just fine. We simply strip any
680 # trailing whitespace off. Passing a string with trailing whitespace
678 # trailing whitespace off. Passing a string with trailing whitespace
681 # to exec will fail however. There seems to be some inconsistency in
679 # to exec will fail however. There seems to be some inconsistency in
682 # how trailing whitespace is handled, but this seems to work.
680 # how trailing whitespace is handled, but this seems to work.
683 python = python.strip()
681 python = python.strip()
684
682
685 # The compiler module will parse the code into an abstract syntax tree.
683 # The compiler module will parse the code into an abstract syntax tree.
686 ast = compiler.parse(python)
684 ast = compiler.parse(python)
687
685
688 # Uncomment to help debug the ast tree
686 # Uncomment to help debug the ast tree
689 # for n in ast.node:
687 # for n in ast.node:
690 # print n.lineno,'->',n
688 # print n.lineno,'->',n
691
689
692 # Each separate command is available by iterating over ast.node. The
690 # Each separate command is available by iterating over ast.node. The
693 # lineno attribute is the line number (1-indexed) beginning the commands
691 # lineno attribute is the line number (1-indexed) beginning the commands
694 # suite.
692 # suite.
695 # lines ending with ";" yield a Discard Node that doesn't have a lineno
693 # lines ending with ";" yield a Discard Node that doesn't have a lineno
696 # attribute. These nodes can and should be discarded. But there are
694 # attribute. These nodes can and should be discarded. But there are
697 # other situations that cause Discard nodes that shouldn't be discarded.
695 # other situations that cause Discard nodes that shouldn't be discarded.
698 # We might eventually discover other cases where lineno is None and have
696 # We might eventually discover other cases where lineno is None and have
699 # to put in a more sophisticated test.
697 # to put in a more sophisticated test.
700 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
698 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
701
699
702 # When we finally get the slices, we will need to slice all the way to
700 # When we finally get the slices, we will need to slice all the way to
703 # the end even though we don't have a line number for it. Fortunately,
701 # the end even though we don't have a line number for it. Fortunately,
704 # None does the job nicely.
702 # None does the job nicely.
705 linenos.append(None)
703 linenos.append(None)
706 lines = python.splitlines()
704 lines = python.splitlines()
707
705
708 # Create a list of atomic commands.
706 # Create a list of atomic commands.
709 cmds = []
707 cmds = []
710 for i, j in zip(linenos[:-1], linenos[1:]):
708 for i, j in zip(linenos[:-1], linenos[1:]):
711 cmd = lines[i:j]
709 cmd = lines[i:j]
712 if cmd:
710 if cmd:
713 cmds.append('\n'.join(cmd)+'\n')
711 cmds.append('\n'.join(cmd)+'\n')
714
712
715 return cmds
713 return cmds
716
714
717 def error(self, text):
715 def error(self, text):
718 """ Pass an error message back to the shell.
716 """ Pass an error message back to the shell.
719
717
720 Preconditions
718 Preconditions
721 -------------
719 -------------
722 This should only be called when self.message is set. In other words,
720 This should only be called when self.message is set. In other words,
723 when code is being executed.
721 when code is being executed.
724
722
725 Parameters
723 Parameters
726 ----------
724 ----------
727 text : str
725 text : str
728 """
726 """
729
727
730 errors = self.message.get('IPYTHON_ERROR', [])
728 errors = self.message.get('IPYTHON_ERROR', [])
731 errors.append(text)
729 errors.append(text)
732
730
733 def var_expand(self, template):
731 def var_expand(self, template):
734 """ Expand $variables in the current namespace using Itpl.
732 """ Expand $variables in the current namespace using Itpl.
735
733
736 Parameters
734 Parameters
737 ----------
735 ----------
738 template : str
736 template : str
739 """
737 """
740
738
741 return str(ItplNS(template, self.user_ns))
739 return str(ItplNS(template, self.user_ns))
742
740
743 def _possible_macro(self, obj):
741 def _possible_macro(self, obj):
744 """ If the object is a macro, execute it.
742 """ If the object is a macro, execute it.
745 """
743 """
746
744
747 if isinstance(obj, Macro):
745 if isinstance(obj, Macro):
748 self.execute_macro(obj)
746 self.execute_macro(obj)
749
747
General Comments 0
You need to be logged in to leave comments. Login now