##// END OF EJS Templates
More tests of the frontend. Improve the ease of testing.
Gael Varoquaux -
Show More
@@ -0,0 +1,90 b''
1 # encoding: utf-8
2 """
3 Test process execution and IO redirection.
4 """
5
6 __docformat__ = "restructuredtext en"
7
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is
12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
14
15 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
16 from cStringIO import StringIO
17 import string
18
19 class TestPrefilterFrontEnd(PrefilterFrontEnd):
20
21 input_prompt_template = string.Template('')
22 output_prompt_template = string.Template('')
23
24
25 def __init__(self, edit_buffer=''):
26 self.edit_buffer = edit_buffer
27 self.out = StringIO()
28 PrefilterFrontEnd.__init__(self)
29
30 def get_current_edit_buffer(self):
31 return self.edit_buffer
32
33 def add_to_edit_buffer(self, string):
34 self.edit_buffer += string
35
36 def write(self, string):
37 self.out.write(string)
38
39 def _on_enter(self):
40 self.add_to_edit_buffer('\n')
41 PrefilterFrontEnd._on_enter(self)
42
43
44 def test_execution():
45 """ Test execution of a command.
46 """
47 f = TestPrefilterFrontEnd(edit_buffer='print 1\n')
48 f._on_enter()
49 assert f.out.getvalue() == '1\n'
50
51
52 def test_multiline():
53 """ Test execution of a multiline command.
54 """
55 f = TestPrefilterFrontEnd(edit_buffer='if True:')
56 f._on_enter()
57 f.add_to_edit_buffer('print 1')
58 f._on_enter()
59 assert f.out.getvalue() == ''
60 f._on_enter()
61 assert f.out.getvalue() == '1\n'
62 f = TestPrefilterFrontEnd(edit_buffer='(1 +')
63 f._on_enter()
64 f.add_to_edit_buffer('0)')
65 f._on_enter()
66 assert f.out.getvalue() == ''
67 f._on_enter()
68 assert f.out.getvalue() == '1\n'
69
70
71 def test_capture():
72 """ Test the capture of output in different channels.
73 """
74 f = TestPrefilterFrontEnd(
75 edit_buffer='import os; out=os.fdopen(1, "w"); out.write("1")')
76 f._on_enter()
77 f._on_enter()
78 assert f.out.getvalue() == '1'
79 f = TestPrefilterFrontEnd(
80 edit_buffer='import os; out=os.fdopen(2, "w"); out.write("1")')
81 f._on_enter()
82 f._on_enter()
83 assert f.out.getvalue() == '1'
84
85
86
87 if __name__ == '__main__':
88 test_execution()
89 test_multiline()
90 test_capture()
@@ -173,9 +173,6 b' class FrontEndBase(object):'
173
173
174 history_cursor = 0
174 history_cursor = 0
175
175
176 current_indent_level = 0
177
178
179 input_prompt_template = string.Template(rc.prompt_in1)
176 input_prompt_template = string.Template(rc.prompt_in1)
180 output_prompt_template = string.Template(rc.prompt_out)
177 output_prompt_template = string.Template(rc.prompt_out)
181 continuation_prompt_template = string.Template(rc.prompt_in2)
178 continuation_prompt_template = string.Template(rc.prompt_in2)
@@ -336,7 +333,7 b' class FrontEndBase(object):'
336 return result when finished.
333 return result when finished.
337 """
334 """
338
335
339 return result
336 raise NotImplementedError
340
337
341
338
342 def render_result(self, result):
339 def render_result(self, result):
@@ -347,7 +344,7 b' class FrontEndBase(object):'
347 should thus return result when finished.
344 should thus return result when finished.
348 """
345 """
349
346
350 return result
347 raise NotImplementedError
351
348
352
349
353 def render_error(self, failure):
350 def render_error(self, failure):
@@ -358,7 +355,7 b' class FrontEndBase(object):'
358 should thus return result when finished.
355 should thus return result when finished.
359 """
356 """
360
357
361 return failure
358 raise NotImplementedError
362
359
363
360
364
361
@@ -19,6 +19,7 b' __docformat__ = "restructuredtext en"'
19 import re
19 import re
20
20
21 import IPython
21 import IPython
22 import sys
22
23
23 from frontendbase import FrontEndBase
24 from frontendbase import FrontEndBase
24 from IPython.kernel.core.interpreter import Interpreter
25 from IPython.kernel.core.interpreter import Interpreter
@@ -63,9 +64,7 b' class LineFrontEndBase(FrontEndBase):'
63 shell = Interpreter()
64 shell = Interpreter()
64 FrontEndBase.__init__(self, shell=shell, history=history)
65 FrontEndBase.__init__(self, shell=shell, history=history)
65
66
66 #FIXME: print banner.
67 self.new_prompt(self.input_prompt_template.substitute(number=1))
67 banner = """IPython1 %s -- An enhanced Interactive Python.""" \
68 % IPython.__version__
69
68
70
69
71 def complete(self, line):
70 def complete(self, line):
@@ -96,7 +95,8 b' class LineFrontEndBase(FrontEndBase):'
96 self.write('\n' + result['stdout'])
95 self.write('\n' + result['stdout'])
97 if 'display' in result and result['display']:
96 if 'display' in result and result['display']:
98 self.write("%s%s\n" % (
97 self.write("%s%s\n" % (
99 self.output_prompt % result['number'],
98 self.output_prompt_template.substitute(
99 number=result['number']),
100 result['display']['pprint']
100 result['display']['pprint']
101 ) )
101 ) )
102
102
@@ -104,7 +104,7 b' class LineFrontEndBase(FrontEndBase):'
104 def render_error(self, failure):
104 def render_error(self, failure):
105 """ Frontend-specific rendering of error.
105 """ Frontend-specific rendering of error.
106 """
106 """
107 self.insert_text('\n\n'+str(failure)+'\n\n')
107 self.write('\n\n'+str(failure)+'\n\n')
108 return failure
108 return failure
109
109
110
110
@@ -129,7 +129,37 b' class LineFrontEndBase(FrontEndBase):'
129 # Add line returns here, to make sure that the statement is
129 # Add line returns here, to make sure that the statement is
130 # complete.
130 # complete.
131 return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
131 return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
132
133
134 def get_current_edit_buffer(self):
135 """ Return the current buffer being entered.
136 """
137 raise NotImplementedError
138
139
140 def write(self, string):
141 """ Write some characters to the display.
142
143 Subclass should overide this method.
144 """
145 print >>sys.__stderr__, string
146
132
147
148 def add_to_edit_buffer(self, string):
149 """ Add the given string to the current edit buffer.
150 """
151 raise NotImplementedError
152
153
154 def new_prompt(self, prompt):
155 """ Prints a prompt and starts a new editing buffer.
156
157 Subclasses should use this method to make sure that the
158 terminal is put in a state favorable for a new line
159 input.
160 """
161 self.write(prompt)
162
133
163
134 def execute(self, python_string, raw_string=None):
164 def execute(self, python_string, raw_string=None):
135 """ Stores the raw_string in the history, and sends the
165 """ Stores the raw_string in the history, and sends the
@@ -167,7 +197,8 b' class LineFrontEndBase(FrontEndBase):'
167 terminal back in a shape where it is usable.
197 terminal back in a shape where it is usable.
168 """
198 """
169 self.prompt_number += 1
199 self.prompt_number += 1
170 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
200 self.new_prompt(self.input_prompt_template.substitute(
201 number=(self.last_result['number'] + 1)))
171 # Start a new empty history entry
202 # Start a new empty history entry
172 self._add_history(None, '')
203 self._add_history(None, '')
173 self.history_cursor = len(self.history.input_cache) - 1
204 self.history_cursor = len(self.history.input_cache) - 1
@@ -186,10 +217,10 b' class LineFrontEndBase(FrontEndBase):'
186 if self.is_complete(cleaned_buffer):
217 if self.is_complete(cleaned_buffer):
187 self.execute(cleaned_buffer, raw_string=current_buffer)
218 self.execute(cleaned_buffer, raw_string=current_buffer)
188 else:
219 else:
189 self.write(self._get_indent_string(
220 self.add_to_edit_buffer(self._get_indent_string(
190 current_buffer[:-1]))
221 current_buffer[:-1]))
191 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
222 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
192 self.write('\t')
223 self.add_to_edit_buffer('\t')
193
224
194
225
195 def _get_indent_string(self, string):
226 def _get_indent_string(self, string):
@@ -175,7 +175,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
175
175
176
176
177 #--------------------------------------------------------------------------
177 #--------------------------------------------------------------------------
178 # PrefilterLineFrontEnd interface
178 # PrefilterFrontEnd interface
179 #--------------------------------------------------------------------------
179 #--------------------------------------------------------------------------
180
180
181 def system_call(self, command_string):
181 def system_call(self, command_string):
@@ -44,9 +44,6 b' def test_io():'
44
44
45
45
46 def test_kill():
46 def test_kill():
47 pass
48
49 if True:
50 """ Check that we can kill a process, and its subprocess.
47 """ Check that we can kill a process, and its subprocess.
51 """
48 """
52 s = StringIO()
49 s = StringIO()
@@ -106,11 +106,6 b' class ConsoleWidget(editwindow.EditWindow):'
106 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
106 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
107 self._configure_scintilla()
107 self._configure_scintilla()
108
108
109 # FIXME: we need to retrieve this from the interpreter.
110 self.prompt = \
111 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
112 self.new_prompt(self.prompt % 1)
113
114 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
109 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
115 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
110 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
116
111
@@ -229,7 +224,10 b' class ConsoleWidget(editwindow.EditWindow):'
229 buf.append(symbol.rstrip() + '\n')
224 buf.append(symbol.rstrip() + '\n')
230 pos = 1
225 pos = 1
231 self.write(''.join(buf))
226 self.write(''.join(buf))
232 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
227 # FIXME: I have some mixing of interfaces between console_widget
228 # and wx_frontend, here.
229 self.new_prompt(self.input_prompt_template.substitute(
230 number=self.last_result['number'] + 1))
233 self.replace_current_edit_buffer(current_buffer)
231 self.replace_current_edit_buffer(current_buffer)
234
232
235 #--------------------------------------------------------------------------
233 #--------------------------------------------------------------------------
@@ -38,7 +38,8 b' class IPythonXController(WxController):'
38 self.release_output()
38 self.release_output()
39 wx.Yield()
39 wx.Yield()
40 if not self.ipython0.exit_now:
40 if not self.ipython0.exit_now:
41 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
41 self.new_prompt(self.input_prompt_template.substitute(
42 number=self.last_result['number'] + 1))
42
43
43
44
44 def do_exit(self):
45 def do_exit(self):
@@ -28,6 +28,7 b' import __builtin__'
28 from time import sleep
28 from time import sleep
29 import sys
29 import sys
30 from threading import Lock
30 from threading import Lock
31 import string
31
32
32 import wx
33 import wx
33 from wx import stc
34 from wx import stc
@@ -48,6 +49,12 b" _ERROR_BG = '#FFF1F1' # Nice red"
48 _RUNNING_BUFFER_MARKER = 31
49 _RUNNING_BUFFER_MARKER = 31
49 _ERROR_MARKER = 30
50 _ERROR_MARKER = 30
50
51
52 prompt_in1 = \
53 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
54
55 prompt_out = \
56 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
57
51 #-------------------------------------------------------------------------------
58 #-------------------------------------------------------------------------------
52 # Classes to implement the Wx frontend
59 # Classes to implement the Wx frontend
53 #-------------------------------------------------------------------------------
60 #-------------------------------------------------------------------------------
@@ -59,9 +66,9 b' class WxController(PrefilterFrontEnd, ConsoleWidget):'
59 widget to provide a text-rendering widget suitable for a terminal.
66 widget to provide a text-rendering widget suitable for a terminal.
60 """
67 """
61
68
62 # FIXME: this shouldn't be there.
69 output_prompt_template = string.Template(prompt_out)
63 output_prompt = \
70
64 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
71 input_prompt_template = string.Template(prompt_in1)
65
72
66 # Print debug info on what is happening to the console.
73 # Print debug info on what is happening to the console.
67 debug = True
74 debug = True
@@ -283,6 +290,13 b' class WxController(PrefilterFrontEnd, ConsoleWidget):'
283 self.MarkerAdd(i, _ERROR_MARKER)
290 self.MarkerAdd(i, _ERROR_MARKER)
284
291
285
292
293 def add_to_edit_buffer(self, string):
294 """ Add the given string to the current edit buffer.
295 """
296 # XXX: I should check the input_state here.
297 self.write(string)
298
299
286 #--------------------------------------------------------------------------
300 #--------------------------------------------------------------------------
287 # ConsoleWidget interface
301 # ConsoleWidget interface
288 #--------------------------------------------------------------------------
302 #--------------------------------------------------------------------------
@@ -294,6 +308,18 b' class WxController(PrefilterFrontEnd, ConsoleWidget):'
294 ConsoleWidget.new_prompt(self, prompt)
308 ConsoleWidget.new_prompt(self, prompt)
295
309
296
310
311 def write(self, *args, **kwargs):
312 # Avoid multiple inheritence, be explicit about which
313 # parent method class gets called
314 ConsoleWidget.write(self, *args, **kwargs)
315
316
317 def get_current_edit_buffer(self):
318 # Avoid multiple inheritence, be explicit about which
319 # parent method class gets called
320 return ConsoleWidget.get_current_edit_buffer(self)
321
322
297 def _on_key_down(self, event, skip=True):
323 def _on_key_down(self, event, skip=True):
298 """ Capture the character events, let the parent
324 """ Capture the character events, let the parent
299 widget handle them, and put our logic afterward.
325 widget handle them, and put our logic afterward.
@@ -399,7 +425,6 b' class WxController(PrefilterFrontEnd, ConsoleWidget):'
399 def _end_system_call(self):
425 def _end_system_call(self):
400 """ Called at the end of a system call.
426 """ Called at the end of a system call.
401 """
427 """
402 print >>sys.__stderr__, 'End of system call'
403 self._input_state = 'buffering'
428 self._input_state = 'buffering'
404 self._running_process = False
429 self._running_process = False
405
430
General Comments 0
You need to be logged in to leave comments. Login now