##// END OF EJS Templates
Merged Gael's ipython-sync-frontend branch with important mods....
Brian Granger -
r1949:a093b34b merge
parent child Browse files
Show More
@@ -0,0 +1,37 b''
1 # encoding: utf-8
2 """
3 Test the LineFrontEnd
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.linefrontendbase import LineFrontEndBase
16 from copy import deepcopy
17 import nose.tools as nt
18
19 class ConcreteLineFrontEnd(LineFrontEndBase):
20 """ A concrete class to test the LineFrontEndBase.
21 """
22 def capture_output(self):
23 pass
24
25 def release_output(self):
26 pass
27
28
29 def test_is_complete():
30 """ Tests line completion heuristic.
31 """
32 frontend = ConcreteLineFrontEnd()
33 yield nt.assert_true, not frontend.is_complete('for x in \\')
34 yield nt.assert_true, not frontend.is_complete('for x in (1, ):')
35 yield nt.assert_true, frontend.is_complete('for x in (1, ):\n pass')
36
37
@@ -1,333 +1,372 b''
1 """
1 """
2 Base front end class for all line-oriented frontends, rather than
2 Base front end class for all line-oriented frontends, rather than
3 block-oriented.
3 block-oriented.
4
4
5 Currently this focuses on synchronous frontends.
5 Currently this focuses on synchronous frontends.
6 """
6 """
7 __docformat__ = "restructuredtext en"
7 __docformat__ = "restructuredtext en"
8
8
9 #-------------------------------------------------------------------------------
9 #-------------------------------------------------------------------------------
10 # Copyright (C) 2008 The IPython Development Team
10 # Copyright (C) 2008 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15
15
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 import re
19 import re
20
20
21 import IPython
22 import sys
21 import sys
23 import codeop
22 import codeop
24 import traceback
25
23
26 from frontendbase import FrontEndBase
24 from frontendbase import FrontEndBase
27 from IPython.kernel.core.interpreter import Interpreter
25 from IPython.kernel.core.interpreter import Interpreter
28
26
29 def common_prefix(strings):
27 def common_prefix(strings):
30 """ Given a list of strings, return the common prefix between all
28 """ Given a list of strings, return the common prefix between all
31 these strings.
29 these strings.
32 """
30 """
33 ref = strings[0]
31 ref = strings[0]
34 prefix = ''
32 prefix = ''
35 for size in range(len(ref)):
33 for size in range(len(ref)):
36 test_prefix = ref[:size+1]
34 test_prefix = ref[:size+1]
37 for string in strings[1:]:
35 for string in strings[1:]:
38 if not string.startswith(test_prefix):
36 if not string.startswith(test_prefix):
39 return prefix
37 return prefix
40 prefix = test_prefix
38 prefix = test_prefix
41
39
42 return prefix
40 return prefix
43
41
44 #-------------------------------------------------------------------------------
42 #-------------------------------------------------------------------------------
45 # Base class for the line-oriented front ends
43 # Base class for the line-oriented front ends
46 #-------------------------------------------------------------------------------
44 #-------------------------------------------------------------------------------
47 class LineFrontEndBase(FrontEndBase):
45 class LineFrontEndBase(FrontEndBase):
48 """ Concrete implementation of the FrontEndBase class. This is meant
46 """ Concrete implementation of the FrontEndBase class. This is meant
49 to be the base class behind all the frontend that are line-oriented,
47 to be the base class behind all the frontend that are line-oriented,
50 rather than block-oriented.
48 rather than block-oriented.
51 """
49 """
52
50
53 # We need to keep the prompt number, to be able to increment
51 # We need to keep the prompt number, to be able to increment
54 # it when there is an exception.
52 # it when there is an exception.
55 prompt_number = 1
53 prompt_number = 1
56
54
57 # We keep a reference to the last result: it helps testing and
55 # We keep a reference to the last result: it helps testing and
58 # programatic control of the frontend.
56 # programatic control of the frontend.
59 last_result = dict(number=0)
57 last_result = dict(number=0)
60
58
59 # The last prompt displayed. Useful for continuation prompts.
60 last_prompt = ''
61
61 # The input buffer being edited
62 # The input buffer being edited
62 input_buffer = ''
63 input_buffer = ''
63
64
64 # Set to true for debug output
65 # Set to true for debug output
65 debug = False
66 debug = False
66
67
67 # A banner to print at startup
68 # A banner to print at startup
68 banner = None
69 banner = None
69
70
70 #--------------------------------------------------------------------------
71 #--------------------------------------------------------------------------
71 # FrontEndBase interface
72 # FrontEndBase interface
72 #--------------------------------------------------------------------------
73 #--------------------------------------------------------------------------
73
74
74 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
75 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
75 if shell is None:
76 if shell is None:
76 shell = Interpreter()
77 shell = Interpreter()
77 FrontEndBase.__init__(self, shell=shell, history=history)
78 FrontEndBase.__init__(self, shell=shell, history=history)
78
79
79 if banner is not None:
80 if banner is not None:
80 self.banner = banner
81 self.banner = banner
81
82
82 def start(self):
83 def start(self):
83 """ Put the frontend in a state where it is ready for user
84 """ Put the frontend in a state where it is ready for user
84 interaction.
85 interaction.
85 """
86 """
86 if self.banner is not None:
87 if self.banner is not None:
87 self.write(self.banner, refresh=False)
88 self.write(self.banner, refresh=False)
88
89
89 self.new_prompt(self.input_prompt_template.substitute(number=1))
90 self.new_prompt(self.input_prompt_template.substitute(number=1))
90
91
91
92
92 def complete(self, line):
93 def complete(self, line):
93 """Complete line in engine's user_ns
94 """Complete line in engine's user_ns
94
95
95 Parameters
96 Parameters
96 ----------
97 ----------
97 line : string
98 line : string
98
99
99 Result
100 Result
100 ------
101 ------
101 The replacement for the line and the list of possible completions.
102 The replacement for the line and the list of possible completions.
102 """
103 """
103 completions = self.shell.complete(line)
104 completions = self.shell.complete(line)
104 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
105 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
105 if completions:
106 if completions:
106 prefix = common_prefix(completions)
107 prefix = common_prefix(completions)
107 residual = complete_sep.split(line)[:-1]
108 residual = complete_sep.split(line)[:-1]
108 line = line[:-len(residual)] + prefix
109 line = line[:-len(residual)] + prefix
109 return line, completions
110 return line, completions
110
111
111
112
112 def render_result(self, result):
113 def render_result(self, result):
113 """ Frontend-specific rendering of the result of a calculation
114 """ Frontend-specific rendering of the result of a calculation
114 that has been sent to an engine.
115 that has been sent to an engine.
115 """
116 """
116 if 'stdout' in result and result['stdout']:
117 if 'stdout' in result and result['stdout']:
117 self.write('\n' + result['stdout'])
118 self.write('\n' + result['stdout'])
118 if 'display' in result and result['display']:
119 if 'display' in result and result['display']:
119 self.write("%s%s\n" % (
120 self.write("%s%s\n" % (
120 self.output_prompt_template.substitute(
121 self.output_prompt_template.substitute(
121 number=result['number']),
122 number=result['number']),
122 result['display']['pprint']
123 result['display']['pprint']
123 ) )
124 ) )
124
125
125
126
126 def render_error(self, failure):
127 def render_error(self, failure):
127 """ Frontend-specific rendering of error.
128 """ Frontend-specific rendering of error.
128 """
129 """
129 self.write('\n\n'+str(failure)+'\n\n')
130 self.write('\n\n'+str(failure)+'\n\n')
130 return failure
131 return failure
131
132
132
133
133 def is_complete(self, string):
134 def is_complete(self, string):
134 """ Check if a string forms a complete, executable set of
135 """ Check if a string forms a complete, executable set of
135 commands.
136 commands.
136
137
137 For the line-oriented frontend, multi-line code is not executed
138 For the line-oriented frontend, multi-line code is not executed
138 as soon as it is complete: the users has to enter two line
139 as soon as it is complete: the users has to enter two line
139 returns.
140 returns.
140 """
141 """
141 if string in ('', '\n'):
142 if string in ('', '\n'):
142 # Prefiltering, eg through ipython0, may return an empty
143 # Prefiltering, eg through ipython0, may return an empty
143 # string although some operations have been accomplished. We
144 # string although some operations have been accomplished. We
144 # thus want to consider an empty string as a complete
145 # thus want to consider an empty string as a complete
145 # statement.
146 # statement.
146 return True
147 return True
147 elif ( len(self.input_buffer.split('\n'))>2
148 elif ( len(self.input_buffer.split('\n'))>2
148 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
149 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
149 return False
150 return False
150 else:
151 else:
151 self.capture_output()
152 self.capture_output()
152 try:
153 try:
153 # Add line returns here, to make sure that the statement is
154 # Add line returns here, to make sure that the statement is
154 # complete.
155 # complete (except if '\' was used).
155 is_complete = codeop.compile_command(string.rstrip() + '\n\n',
156 # This should probably be done in a different place (like
157 # maybe 'prefilter_input' method? For now, this works.
158 clean_string = string.rstrip('\n')
159 if not clean_string.endswith('\\'): clean_string +='\n\n'
160 is_complete = codeop.compile_command(clean_string,
156 "<string>", "exec")
161 "<string>", "exec")
157 self.release_output()
162 self.release_output()
158 except Exception, e:
163 except Exception, e:
159 # XXX: Hack: return True so that the
164 # XXX: Hack: return True so that the
160 # code gets executed and the error captured.
165 # code gets executed and the error captured.
161 is_complete = True
166 is_complete = True
162 return is_complete
167 return is_complete
163
168
164
169
165 def write(self, string, refresh=True):
170 def write(self, string, refresh=True):
166 """ Write some characters to the display.
171 """ Write some characters to the display.
167
172
168 Subclass should overide this method.
173 Subclass should overide this method.
169
174
170 The refresh keyword argument is used in frontends with an
175 The refresh keyword argument is used in frontends with an
171 event loop, to choose whether the write should trigget an UI
176 event loop, to choose whether the write should trigget an UI
172 refresh, and thus be syncrhonous, or not.
177 refresh, and thus be syncrhonous, or not.
173 """
178 """
174 print >>sys.__stderr__, string
179 print >>sys.__stderr__, string
175
180
176
181
177 def execute(self, python_string, raw_string=None):
182 def execute(self, python_string, raw_string=None):
178 """ Stores the raw_string in the history, and sends the
183 """ Stores the raw_string in the history, and sends the
179 python string to the interpreter.
184 python string to the interpreter.
180 """
185 """
181 if raw_string is None:
186 if raw_string is None:
182 raw_string = python_string
187 raw_string = python_string
183 # Create a false result, in case there is an exception
188 # Create a false result, in case there is an exception
184 self.last_result = dict(number=self.prompt_number)
189 self.last_result = dict(number=self.prompt_number)
185
190
186 ## try:
187 ## self.history.input_cache[-1] = raw_string.rstrip()
188 ## result = self.shell.execute(python_string)
189 ## self.last_result = result
190 ## self.render_result(result)
191 ## except:
192 ## self.show_traceback()
193 ## finally:
194 ## self.after_execute()
195
196 try:
191 try:
197 try:
192 try:
198 self.history.input_cache[-1] = raw_string.rstrip()
193 self.history.input_cache[-1] = raw_string.rstrip()
199 result = self.shell.execute(python_string)
194 result = self.shell.execute(python_string)
200 self.last_result = result
195 self.last_result = result
201 self.render_result(result)
196 self.render_result(result)
202 except:
197 except:
203 self.show_traceback()
198 self.show_traceback()
204 finally:
199 finally:
205 self.after_execute()
200 self.after_execute()
206
201
207
202
208 #--------------------------------------------------------------------------
203 #--------------------------------------------------------------------------
209 # LineFrontEndBase interface
204 # LineFrontEndBase interface
210 #--------------------------------------------------------------------------
205 #--------------------------------------------------------------------------
211
206
212 def prefilter_input(self, string):
207 def prefilter_input(self, string):
213 """ Prefilter the input to turn it in valid python.
208 """ Prefilter the input to turn it in valid python.
214 """
209 """
215 string = string.replace('\r\n', '\n')
210 string = string.replace('\r\n', '\n')
216 string = string.replace('\t', 4*' ')
211 string = string.replace('\t', 4*' ')
217 # Clean the trailing whitespace
212 # Clean the trailing whitespace
218 string = '\n'.join(l.rstrip() for l in string.split('\n'))
213 string = '\n'.join(l.rstrip() for l in string.split('\n'))
219 return string
214 return string
220
215
221
216
222 def after_execute(self):
217 def after_execute(self):
223 """ All the operations required after an execution to put the
218 """ All the operations required after an execution to put the
224 terminal back in a shape where it is usable.
219 terminal back in a shape where it is usable.
225 """
220 """
226 self.prompt_number += 1
221 self.prompt_number += 1
227 self.new_prompt(self.input_prompt_template.substitute(
222 self.new_prompt(self.input_prompt_template.substitute(
228 number=(self.last_result['number'] + 1)))
223 number=(self.last_result['number'] + 1)))
229 # Start a new empty history entry
224 # Start a new empty history entry
230 self._add_history(None, '')
225 self._add_history(None, '')
231 self.history_cursor = len(self.history.input_cache) - 1
226 self.history_cursor = len(self.history.input_cache) - 1
232
227
233
228
234 def complete_current_input(self):
229 def complete_current_input(self):
235 """ Do code completion on current line.
230 """ Do code completion on current line.
236 """
231 """
237 if self.debug:
232 if self.debug:
238 print >>sys.__stdout__, "complete_current_input",
233 print >>sys.__stdout__, "complete_current_input",
239 line = self.input_buffer
234 line = self.input_buffer
240 new_line, completions = self.complete(line)
235 new_line, completions = self.complete(line)
241 if len(completions)>1:
236 if len(completions)>1:
242 self.write_completion(completions, new_line=new_line)
237 self.write_completion(completions, new_line=new_line)
243 elif not line == new_line:
238 elif not line == new_line:
244 self.input_buffer = new_line
239 self.input_buffer = new_line
245 if self.debug:
240 if self.debug:
246 print >>sys.__stdout__, 'line', line
241 print >>sys.__stdout__, 'line', line
247 print >>sys.__stdout__, 'new_line', new_line
242 print >>sys.__stdout__, 'new_line', new_line
248 print >>sys.__stdout__, completions
243 print >>sys.__stdout__, completions
249
244
250
245
251 def get_line_width(self):
246 def get_line_width(self):
252 """ Return the width of the line in characters.
247 """ Return the width of the line in characters.
253 """
248 """
254 return 80
249 return 80
255
250
256
251
257 def write_completion(self, possibilities, new_line=None):
252 def write_completion(self, possibilities, new_line=None):
258 """ Write the list of possible completions.
253 """ Write the list of possible completions.
259
254
260 new_line is the completed input line that should be displayed
255 new_line is the completed input line that should be displayed
261 after the completion are writen. If None, the input_buffer
256 after the completion are writen. If None, the input_buffer
262 before the completion is used.
257 before the completion is used.
263 """
258 """
264 if new_line is None:
259 if new_line is None:
265 new_line = self.input_buffer
260 new_line = self.input_buffer
266
261
267 self.write('\n')
262 self.write('\n')
268 max_len = len(max(possibilities, key=len)) + 1
263 max_len = len(max(possibilities, key=len)) + 1
269
264
270 # Now we check how much symbol we can put on a line...
265 # Now we check how much symbol we can put on a line...
271 chars_per_line = self.get_line_width()
266 chars_per_line = self.get_line_width()
272 symbols_per_line = max(1, chars_per_line/max_len)
267 symbols_per_line = max(1, chars_per_line/max_len)
273
268
274 pos = 1
269 pos = 1
275 buf = []
270 completion_string = []
276 for symbol in possibilities:
271 for symbol in possibilities:
277 if pos < symbols_per_line:
272 if pos < symbols_per_line:
278 buf.append(symbol.ljust(max_len))
273 completion_string.append(symbol.ljust(max_len))
279 pos += 1
274 pos += 1
280 else:
275 else:
281 buf.append(symbol.rstrip() + '\n')
276 completion_string.append(symbol.rstrip() + '\n')
282 pos = 1
277 pos = 1
283 self.write(''.join(buf))
278 self.write(''.join(completion_string))
284 self.new_prompt(self.input_prompt_template.substitute(
279 self.new_prompt(self.input_prompt_template.substitute(
285 number=self.last_result['number'] + 1))
280 number=self.last_result['number'] + 1))
286 self.input_buffer = new_line
281 self.input_buffer = new_line
287
282
288
283
289 def new_prompt(self, prompt):
284 def new_prompt(self, prompt):
290 """ Prints a prompt and starts a new editing buffer.
285 """ Prints a prompt and starts a new editing buffer.
291
286
292 Subclasses should use this method to make sure that the
287 Subclasses should use this method to make sure that the
293 terminal is put in a state favorable for a new line
288 terminal is put in a state favorable for a new line
294 input.
289 input.
295 """
290 """
296 self.input_buffer = ''
291 self.input_buffer = ''
297 self.write(prompt)
292 self.write(prompt)
298
293
299
294
295 def continuation_prompt(self):
296 """Returns the current continuation prompt.
297 """
298 return ("."*(len(self.last_prompt)-2) + ': ')
299
300
301 def execute_command(self, command, hidden=False):
302 """ Execute a command, not only in the model, but also in the
303 view, if any.
304 """
305 return self.shell.execute(command)
306
300 #--------------------------------------------------------------------------
307 #--------------------------------------------------------------------------
301 # Private API
308 # Private API
302 #--------------------------------------------------------------------------
309 #--------------------------------------------------------------------------
303
310
304 def _on_enter(self):
311 def _on_enter(self, new_line_pos=0):
305 """ Called when the return key is pressed in a line editing
312 """ Called when the return key is pressed in a line editing
306 buffer.
313 buffer.
314
315 Parameters
316 ----------
317 new_line_pos : integer, optional
318 Position of the new line to add, starting from the
319 end (0 adds a new line after the last line, -1 before
320 the last line...)
321
322 Returns
323 -------
324 True if execution is triggered
307 """
325 """
308 current_buffer = self.input_buffer
326 current_buffer = self.input_buffer
309 cleaned_buffer = self.prefilter_input(current_buffer)
327 # XXX: This string replace is ugly, but there should be no way it
328 # fails.
329 prompt_less_buffer = re.sub('^' + self.continuation_prompt(),
330 '', current_buffer).replace('\n' + self.continuation_prompt(),
331 '\n')
332 cleaned_buffer = self.prefilter_input(prompt_less_buffer)
310 if self.is_complete(cleaned_buffer):
333 if self.is_complete(cleaned_buffer):
311 self.execute(cleaned_buffer, raw_string=current_buffer)
334 self.execute(cleaned_buffer, raw_string=current_buffer)
335 return True
312 else:
336 else:
313 self.input_buffer += self._get_indent_string(
337 # Start a new line.
314 current_buffer[:-1])
338 new_line_pos = -new_line_pos
315 if len(current_buffer.split('\n')) == 2:
339 lines = current_buffer.split('\n')[:-1]
316 self.input_buffer += '\t\t'
340 prompt_less_lines = prompt_less_buffer.split('\n')
317 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
341 # Create the new line, with the continuation prompt, and the
318 self.input_buffer += '\t'
342 # same amount of indent than the line above it.
319
343 new_line = self.continuation_prompt() + \
344 self._get_indent_string('\n'.join(
345 prompt_less_lines[:new_line_pos-1]))
346 if len(lines) == 1:
347 # We are starting a first continuation line. Indent it.
348 new_line += '\t'
349 elif current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
350 # The last line ends with ":", autoindent the new line.
351 new_line += '\t'
352
353 if new_line_pos == 0:
354 lines.append(new_line)
355 else:
356 lines.insert(new_line_pos, new_line)
357 self.input_buffer = '\n'.join(lines)
358
320
359
321 def _get_indent_string(self, string):
360 def _get_indent_string(self, string):
322 """ Return the string of whitespace that prefixes a line. Used to
361 """ Return the string of whitespace that prefixes a line. Used to
323 add the right amount of indendation when creating a new line.
362 add the right amount of indendation when creating a new line.
324 """
363 """
325 string = string.replace('\t', ' '*4)
364 string = string.replace('\t', ' '*4)
326 string = string.split('\n')[-1]
365 string = string.split('\n')[-1]
327 indent_chars = len(string) - len(string.lstrip())
366 indent_chars = len(string) - len(string.lstrip())
328 indent_string = '\t'*(indent_chars // 4) + \
367 indent_string = '\t'*(indent_chars // 4) + \
329 ' '*(indent_chars % 4)
368 ' '*(indent_chars % 4)
330
369
331 return indent_string
370 return indent_string
332
371
333
372
@@ -1,246 +1,280 b''
1 """
1 """
2 Frontend class that uses IPython0 to prefilter the inputs.
2 Frontend class that uses IPython0 to prefilter the inputs.
3
3
4 Using the IPython0 mechanism gives us access to the magics.
4 Using the IPython0 mechanism gives us access to the magics.
5
5
6 This is a transitory class, used here to do the transition between
6 This is a transitory class, used here to do the transition between
7 ipython0 and ipython1. This class is meant to be short-lived as more
7 ipython0 and ipython1. This class is meant to be short-lived as more
8 functionnality is abstracted out of ipython0 in reusable functions and
8 functionnality is abstracted out of ipython0 in reusable functions and
9 is added on the interpreter. This class can be a used to guide this
9 is added on the interpreter. This class can be a used to guide this
10 refactoring.
10 refactoring.
11 """
11 """
12 __docformat__ = "restructuredtext en"
12 __docformat__ = "restructuredtext en"
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Copyright (C) 2008 The IPython Development Team
15 # Copyright (C) 2008 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24 import sys
24 import sys
25
25 import pydoc
26 from linefrontendbase import LineFrontEndBase, common_prefix
26 import os
27 from frontendbase import FrontEndBase
27 import re
28 import __builtin__
28
29
29 from IPython.ipmaker import make_IPython
30 from IPython.ipmaker import make_IPython
30 from IPython.ipapi import IPApi
31 from IPython.ipapi import IPApi
31 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
32 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
32
33
33 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
34 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
34
35
35 from IPython.genutils import Term
36 from IPython.genutils import Term
36 import pydoc
37
37 import os
38 from linefrontendbase import LineFrontEndBase, common_prefix
38 import sys
39
39
40
40
41 def mk_system_call(system_call_function, command):
41 def mk_system_call(system_call_function, command):
42 """ given a os.system replacement, and a leading string command,
42 """ given a os.system replacement, and a leading string command,
43 returns a function that will execute the command with the given
43 returns a function that will execute the command with the given
44 argument string.
44 argument string.
45 """
45 """
46 def my_system_call(args):
46 def my_system_call(args):
47 system_call_function("%s %s" % (command, args))
47 system_call_function("%s %s" % (command, args))
48
49 my_system_call.__doc__ = "Calls %s" % command
48 return my_system_call
50 return my_system_call
49
51
50 #-------------------------------------------------------------------------------
52 #-------------------------------------------------------------------------------
51 # Frontend class using ipython0 to do the prefiltering.
53 # Frontend class using ipython0 to do the prefiltering.
52 #-------------------------------------------------------------------------------
54 #-------------------------------------------------------------------------------
53 class PrefilterFrontEnd(LineFrontEndBase):
55 class PrefilterFrontEnd(LineFrontEndBase):
54 """ Class that uses ipython0 to do prefilter the input, do the
56 """ Class that uses ipython0 to do prefilter the input, do the
55 completion and the magics.
57 completion and the magics.
56
58
57 The core trick is to use an ipython0 instance to prefilter the
59 The core trick is to use an ipython0 instance to prefilter the
58 input, and share the namespace between the interpreter instance used
60 input, and share the namespace between the interpreter instance used
59 to execute the statements and the ipython0 used for code
61 to execute the statements and the ipython0 used for code
60 completion...
62 completion...
61 """
63 """
62
64
63 debug = False
65 debug = False
64
66
65 def __init__(self, ipython0=None, *args, **kwargs):
67 def __init__(self, ipython0=None, *args, **kwargs):
66 """ Parameters:
68 """ Parameters:
67 -----------
69 -----------
68
70
69 ipython0: an optional ipython0 instance to use for command
71 ipython0: an optional ipython0 instance to use for command
70 prefiltering and completion.
72 prefiltering and completion.
71 """
73 """
74 # This is a hack to avoid the IPython exception hook to trigger
75 # on exceptions (https://bugs.launchpad.net/bugs/337105)
76 # XXX: This is horrible: module-leve monkey patching -> side
77 # effects.
78 from IPython import iplib
79 iplib.InteractiveShell.isthreaded = True
80
72 LineFrontEndBase.__init__(self, *args, **kwargs)
81 LineFrontEndBase.__init__(self, *args, **kwargs)
73 self.shell.output_trap = RedirectorOutputTrap(
82 self.shell.output_trap = RedirectorOutputTrap(
74 out_callback=self.write,
83 out_callback=self.write,
75 err_callback=self.write,
84 err_callback=self.write,
76 )
85 )
77 self.shell.traceback_trap = SyncTracebackTrap(
86 self.shell.traceback_trap = SyncTracebackTrap(
78 formatters=self.shell.traceback_trap.formatters,
87 formatters=self.shell.traceback_trap.formatters,
79 )
88 )
80
89
81 # Start the ipython0 instance:
90 # Start the ipython0 instance:
82 self.save_output_hooks()
91 self.save_output_hooks()
83 if ipython0 is None:
92 if ipython0 is None:
84 # Instanciate an IPython0 interpreter to be able to use the
93 # Instanciate an IPython0 interpreter to be able to use the
85 # prefiltering.
94 # prefiltering.
95 # Suppress all key input, to avoid waiting
96 def my_rawinput(x=None):
97 return '\n'
98 old_rawinput = __builtin__.raw_input
99 __builtin__.raw_input = my_rawinput
86 # XXX: argv=[] is a bit bold.
100 # XXX: argv=[] is a bit bold.
87 ipython0 = make_IPython(argv=[],
101 ipython0 = make_IPython(argv=[],
88 user_ns=self.shell.user_ns,
102 user_ns=self.shell.user_ns,
89 user_global_ns=self.shell.user_global_ns)
103 user_global_ns=self.shell.user_global_ns)
104 __builtin__.raw_input = old_rawinput
90 self.ipython0 = ipython0
105 self.ipython0 = ipython0
91 # Set the pager:
106 # Set the pager:
92 self.ipython0.set_hook('show_in_pager',
107 self.ipython0.set_hook('show_in_pager',
93 lambda s, string: self.write("\n" + string))
108 lambda s, string: self.write("\n" + string))
94 self.ipython0.write = self.write
109 self.ipython0.write = self.write
95 self._ip = _ip = IPApi(self.ipython0)
110 self._ip = _ip = IPApi(self.ipython0)
96 # Make sure the raw system call doesn't get called, as we don't
111 # Make sure the raw system call doesn't get called, as we don't
97 # have a stdin accessible.
112 # have a stdin accessible.
98 self._ip.system = self.system_call
113 self._ip.system = self.system_call
99 # XXX: Muck around with magics so that they work better
114 # XXX: Muck around with magics so that they work better
100 # in our environment
115 # in our environment
101 self.ipython0.magic_ls = mk_system_call(self.system_call,
116 if not sys.platform.startswith('win'):
102 'ls -CF')
117 self.ipython0.magic_ls = mk_system_call(self.system_call,
118 'ls -CF')
103 # And now clean up the mess created by ipython0
119 # And now clean up the mess created by ipython0
104 self.release_output()
120 self.release_output()
105
121
106
122
107 if not 'banner' in kwargs and self.banner is None:
123 if not 'banner' in kwargs and self.banner is None:
108 self.banner = self.ipython0.BANNER + """
124 self.banner = self.ipython0.BANNER
109 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
110
125
126 # FIXME: __init__ and start should be two different steps
111 self.start()
127 self.start()
112
128
113 #--------------------------------------------------------------------------
129 #--------------------------------------------------------------------------
114 # FrontEndBase interface
130 # FrontEndBase interface
115 #--------------------------------------------------------------------------
131 #--------------------------------------------------------------------------
116
132
117 def show_traceback(self):
133 def show_traceback(self):
118 """ Use ipython0 to capture the last traceback and display it.
134 """ Use ipython0 to capture the last traceback and display it.
119 """
135 """
120 self.capture_output()
136 # Don't do the capture; the except_hook has already done some
137 # modifications to the IO streams, if we store them, we'll be
138 # storing the wrong ones.
139 #self.capture_output()
121 self.ipython0.showtraceback(tb_offset=-1)
140 self.ipython0.showtraceback(tb_offset=-1)
122 self.release_output()
141 self.release_output()
123
142
124
143
125 def execute(self, python_string, raw_string=None):
144 def execute(self, python_string, raw_string=None):
126 if self.debug:
145 if self.debug:
127 print 'Executing Python code:', repr(python_string)
146 print 'Executing Python code:', repr(python_string)
128 self.capture_output()
147 self.capture_output()
129 LineFrontEndBase.execute(self, python_string,
148 LineFrontEndBase.execute(self, python_string,
130 raw_string=raw_string)
149 raw_string=raw_string)
131 self.release_output()
150 self.release_output()
132
151
133
152
134 def save_output_hooks(self):
153 def save_output_hooks(self):
135 """ Store all the output hooks we can think of, to be able to
154 """ Store all the output hooks we can think of, to be able to
136 restore them.
155 restore them.
137
156
138 We need to do this early, as starting the ipython0 instance will
157 We need to do this early, as starting the ipython0 instance will
139 screw ouput hooks.
158 screw ouput hooks.
140 """
159 """
141 self.__old_cout_write = Term.cout.write
160 self.__old_cout_write = Term.cout.write
142 self.__old_cerr_write = Term.cerr.write
161 self.__old_cerr_write = Term.cerr.write
143 self.__old_stdout = sys.stdout
162 self.__old_stdout = sys.stdout
144 self.__old_stderr= sys.stderr
163 self.__old_stderr= sys.stderr
145 self.__old_help_output = pydoc.help.output
164 self.__old_help_output = pydoc.help.output
146 self.__old_display_hook = sys.displayhook
165 self.__old_display_hook = sys.displayhook
147
166
148
167
149 def capture_output(self):
168 def capture_output(self):
150 """ Capture all the output mechanisms we can think of.
169 """ Capture all the output mechanisms we can think of.
151 """
170 """
152 self.save_output_hooks()
171 self.save_output_hooks()
153 Term.cout.write = self.write
172 Term.cout.write = self.write
154 Term.cerr.write = self.write
173 Term.cerr.write = self.write
155 sys.stdout = Term.cout
174 sys.stdout = Term.cout
156 sys.stderr = Term.cerr
175 sys.stderr = Term.cerr
157 pydoc.help.output = self.shell.output_trap.out
176 pydoc.help.output = self.shell.output_trap.out
158
177
159
178
160 def release_output(self):
179 def release_output(self):
161 """ Release all the different captures we have made.
180 """ Release all the different captures we have made.
162 """
181 """
163 Term.cout.write = self.__old_cout_write
182 Term.cout.write = self.__old_cout_write
164 Term.cerr.write = self.__old_cerr_write
183 Term.cerr.write = self.__old_cerr_write
165 sys.stdout = self.__old_stdout
184 sys.stdout = self.__old_stdout
166 sys.stderr = self.__old_stderr
185 sys.stderr = self.__old_stderr
167 pydoc.help.output = self.__old_help_output
186 pydoc.help.output = self.__old_help_output
168 sys.displayhook = self.__old_display_hook
187 sys.displayhook = self.__old_display_hook
169
188
170
189
171 def complete(self, line):
190 def complete(self, line):
172 # FIXME: This should be factored out in the linefrontendbase
191 # FIXME: This should be factored out in the linefrontendbase
173 # method.
192 # method.
174 word = line.split('\n')[-1].split(' ')[-1]
193 word = self._get_completion_text(line)
175 completions = self.ipython0.complete(word)
194 completions = self.ipython0.complete(word)
176 # FIXME: The proper sort should be done in the complete method.
195 # FIXME: The proper sort should be done in the complete method.
177 key = lambda x: x.replace('_', '')
196 key = lambda x: x.replace('_', '')
178 completions.sort(key=key)
197 completions.sort(key=key)
179 if completions:
198 if completions:
180 prefix = common_prefix(completions)
199 prefix = common_prefix(completions)
181 line = line[:-len(word)] + prefix
200 line = line[:-len(word)] + prefix
182 return line, completions
201 return line, completions
183
202
184
203
185 #--------------------------------------------------------------------------
204 #--------------------------------------------------------------------------
186 # LineFrontEndBase interface
205 # LineFrontEndBase interface
187 #--------------------------------------------------------------------------
206 #--------------------------------------------------------------------------
188
207
189 def prefilter_input(self, input_string):
208 def prefilter_input(self, input_string):
190 """ Using IPython0 to prefilter the commands to turn them
209 """ Using IPython0 to prefilter the commands to turn them
191 in executable statements that are valid Python strings.
210 in executable statements that are valid Python strings.
192 """
211 """
193 input_string = LineFrontEndBase.prefilter_input(self, input_string)
212 input_string = LineFrontEndBase.prefilter_input(self, input_string)
194 filtered_lines = []
213 filtered_lines = []
195 # The IPython0 prefilters sometime produce output. We need to
214 # The IPython0 prefilters sometime produce output. We need to
196 # capture it.
215 # capture it.
197 self.capture_output()
216 self.capture_output()
198 self.last_result = dict(number=self.prompt_number)
217 self.last_result = dict(number=self.prompt_number)
199
218
200 ## try:
219 ## try:
201 ## for line in input_string.split('\n'):
220 ## for line in input_string.split('\n'):
202 ## filtered_lines.append(
221 ## filtered_lines.append(
203 ## self.ipython0.prefilter(line, False).rstrip())
222 ## self.ipython0.prefilter(line, False).rstrip())
204 ## except:
223 ## except:
205 ## # XXX: probably not the right thing to do.
224 ## # XXX: probably not the right thing to do.
206 ## self.ipython0.showsyntaxerror()
225 ## self.ipython0.showsyntaxerror()
207 ## self.after_execute()
226 ## self.after_execute()
208 ## finally:
227 ## finally:
209 ## self.release_output()
228 ## self.release_output()
210
229
211
230
212 try:
231 try:
213 try:
232 try:
214 for line in input_string.split('\n'):
233 for line in input_string.split('\n'):
215 filtered_lines.append(
234 filtered_lines.append(
216 self.ipython0.prefilter(line, False).rstrip())
235 self.ipython0.prefilter(line, False).rstrip())
217 except:
236 except:
218 # XXX: probably not the right thing to do.
237 # XXX: probably not the right thing to do.
219 self.ipython0.showsyntaxerror()
238 self.ipython0.showsyntaxerror()
220 self.after_execute()
239 self.after_execute()
221 finally:
240 finally:
222 self.release_output()
241 self.release_output()
223
242
224
243
225
244
226 # Clean up the trailing whitespace, to avoid indentation errors
245 # Clean up the trailing whitespace, to avoid indentation errors
227 filtered_string = '\n'.join(filtered_lines)
246 filtered_string = '\n'.join(filtered_lines)
228 return filtered_string
247 return filtered_string
229
248
230
249
231 #--------------------------------------------------------------------------
250 #--------------------------------------------------------------------------
232 # PrefilterFrontEnd interface
251 # PrefilterFrontEnd interface
233 #--------------------------------------------------------------------------
252 #--------------------------------------------------------------------------
234
253
235 def system_call(self, command_string):
254 def system_call(self, command_string):
236 """ Allows for frontend to define their own system call, to be
255 """ Allows for frontend to define their own system call, to be
237 able capture output and redirect input.
256 able capture output and redirect input.
238 """
257 """
239 return os.system(command_string)
258 return os.system(command_string)
240
259
241
260
242 def do_exit(self):
261 def do_exit(self):
243 """ Exit the shell, cleanup and save the history.
262 """ Exit the shell, cleanup and save the history.
244 """
263 """
245 self.ipython0.atexit_operations()
264 self.ipython0.atexit_operations()
246
265
266
267 def _get_completion_text(self, line):
268 """ Returns the text to be completed by breaking the line at specified
269 delimiters.
270 """
271 # Break at: spaces, '=', all parentheses (except if balanced).
272 # FIXME2: In the future, we need to make the implementation similar to
273 # that in the 'pyreadline' module (modes/basemode.py) where we break at
274 # each delimiter and try to complete the residual line, until we get a
275 # successful list of completions.
276 expression = '\s|=|,|:|\((?!.*\))|\[(?!.*\])|\{(?!.*\})'
277 complete_sep = re.compile(expression)
278 text = complete_sep.split(line)[-1]
279 return text
280
1 NO CONTENT: file renamed from IPython/frontend/_process/__init__.py to IPython/frontend/process/__init__.py
NO CONTENT: file renamed from IPython/frontend/_process/__init__.py to IPython/frontend/process/__init__.py
@@ -1,179 +1,184 b''
1 # Addapted from killableprocess.py.
1 # Addapted from killableprocess.py.
2 #______________________________________________________________________________
2 #______________________________________________________________________________
3 #
3 #
4 # killableprocess - subprocesses which can be reliably killed
4 # killableprocess - subprocesses which can be reliably killed
5 #
5 #
6 # Parts of this module are copied from the subprocess.py file contained
6 # Parts of this module are copied from the subprocess.py file contained
7 # in the Python distribution.
7 # in the Python distribution.
8 #
8 #
9 # Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
9 # Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
10 #
10 #
11 # Additions and modifications written by Benjamin Smedberg
11 # Additions and modifications written by Benjamin Smedberg
12 # <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
12 # <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
13 # <http://www.mozilla.org/>
13 # <http://www.mozilla.org/>
14 #
14 #
15 # By obtaining, using, and/or copying this software and/or its
15 # By obtaining, using, and/or copying this software and/or its
16 # associated documentation, you agree that you have read, understood,
16 # associated documentation, you agree that you have read, understood,
17 # and will comply with the following terms and conditions:
17 # and will comply with the following terms and conditions:
18 #
18 #
19 # Permission to use, copy, modify, and distribute this software and
19 # Permission to use, copy, modify, and distribute this software and
20 # its associated documentation for any purpose and without fee is
20 # its associated documentation for any purpose and without fee is
21 # hereby granted, provided that the above copyright notice appears in
21 # hereby granted, provided that the above copyright notice appears in
22 # all copies, and that both that copyright notice and this permission
22 # all copies, and that both that copyright notice and this permission
23 # notice appear in supporting documentation, and that the name of the
23 # notice appear in supporting documentation, and that the name of the
24 # author not be used in advertising or publicity pertaining to
24 # author not be used in advertising or publicity pertaining to
25 # distribution of the software without specific, written prior
25 # distribution of the software without specific, written prior
26 # permission.
26 # permission.
27 #
27 #
28 # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
28 # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
29 # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
29 # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
30 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
30 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
31 # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
31 # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
32 # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
32 # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
33 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
33 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
34 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
34 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35
35
36 r"""killableprocess - Subprocesses which can be reliably killed
36 r"""killableprocess - Subprocesses which can be reliably killed
37
37
38 This module is a subclass of the builtin "subprocess" module. It allows
38 This module is a subclass of the builtin "subprocess" module. It allows
39 processes that launch subprocesses to be reliably killed on Windows (via the Popen.kill() method.
39 processes that launch subprocesses to be reliably killed on Windows (via the Popen.kill() method.
40
40
41 It also adds a timeout argument to Wait() for a limited period of time before
41 It also adds a timeout argument to Wait() for a limited period of time before
42 forcefully killing the process.
42 forcefully killing the process.
43
43
44 Note: On Windows, this module requires Windows 2000 or higher (no support for
44 Note: On Windows, this module requires Windows 2000 or higher (no support for
45 Windows 95, 98, or NT 4.0). It also requires ctypes, which is bundled with
45 Windows 95, 98, or NT 4.0). It also requires ctypes, which is bundled with
46 Python 2.5+ or available from http://python.net/crew/theller/ctypes/
46 Python 2.5+ or available from http://python.net/crew/theller/ctypes/
47 """
47 """
48
48
49 import subprocess
49 import subprocess
50 from subprocess import PIPE
50 from subprocess import PIPE
51 import sys
51 import sys
52 import os
52 import os
53 import types
53 import types
54
54
55 try:
55 try:
56 from subprocess import CalledProcessError
56 from subprocess import CalledProcessError
57 except ImportError:
57 except ImportError:
58 # Python 2.4 doesn't implement CalledProcessError
58 # Python 2.4 doesn't implement CalledProcessError
59 class CalledProcessError(Exception):
59 class CalledProcessError(Exception):
60 """This exception is raised when a process run by check_call() returns
60 """This exception is raised when a process run by check_call() returns
61 a non-zero exit status. The exit status will be stored in the
61 a non-zero exit status. The exit status will be stored in the
62 returncode attribute."""
62 returncode attribute."""
63 def __init__(self, returncode, cmd):
63 def __init__(self, returncode, cmd):
64 self.returncode = returncode
64 self.returncode = returncode
65 self.cmd = cmd
65 self.cmd = cmd
66 def __str__(self):
66 def __str__(self):
67 return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
67 return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
68
68
69 mswindows = (sys.platform == "win32")
69 mswindows = (sys.platform == "win32")
70
70
71 skip = False
71 skip = False
72
72
73 if mswindows:
73 if mswindows:
74 import platform
74 import platform
75 if platform.uname()[3] == '' or platform.uname()[3] > '6.0.6000':
75 if platform.uname()[3] == '' or platform.uname()[3] > '6.0.6000':
76 # Killable process does not work under vista when starting for
76 # Killable process does not work under vista when starting for
77 # something else than cmd.
77 # something else than cmd.
78 skip = True
78 skip = True
79 else:
79 else:
80 import winprocess
80 import winprocess
81 else:
81 else:
82 import signal
82 import signal
83
83
84 if not mswindows:
84 if not mswindows:
85 def DoNothing(*args):
85 def DoNothing(*args):
86 pass
86 pass
87
87
88
88
89 if skip:
89 if skip:
90 Popen = subprocess.Popen
90 Popen = subprocess.Popen
91 else:
91 else:
92 class Popen(subprocess.Popen):
92 class Popen(subprocess.Popen):
93 if not mswindows:
93 if not mswindows:
94 # Override __init__ to set a preexec_fn
94 # Override __init__ to set a preexec_fn
95 def __init__(self, *args, **kwargs):
95 def __init__(self, *args, **kwargs):
96 if len(args) >= 7:
96 if len(args) >= 7:
97 raise Exception("Arguments preexec_fn and after must be passed by keyword.")
97 raise Exception("Arguments preexec_fn and after must be passed by keyword.")
98
98
99 real_preexec_fn = kwargs.pop("preexec_fn", None)
99 real_preexec_fn = kwargs.pop("preexec_fn", None)
100 def setpgid_preexec_fn():
100 def setpgid_preexec_fn():
101 os.setpgid(0, 0)
101 os.setpgid(0, 0)
102 if real_preexec_fn:
102 if real_preexec_fn:
103 apply(real_preexec_fn)
103 apply(real_preexec_fn)
104
104
105 kwargs['preexec_fn'] = setpgid_preexec_fn
105 kwargs['preexec_fn'] = setpgid_preexec_fn
106
106
107 subprocess.Popen.__init__(self, *args, **kwargs)
107 subprocess.Popen.__init__(self, *args, **kwargs)
108
108
109 if mswindows:
109 if mswindows:
110 def _execute_child(self, args, executable, preexec_fn, close_fds,
110 def _execute_child(self, args, executable, preexec_fn, close_fds,
111 cwd, env, universal_newlines, startupinfo,
111 cwd, env, universal_newlines, startupinfo,
112 creationflags, shell,
112 creationflags, shell,
113 p2cread, p2cwrite,
113 p2cread, p2cwrite,
114 c2pread, c2pwrite,
114 c2pread, c2pwrite,
115 errread, errwrite):
115 errread, errwrite):
116 if not isinstance(args, types.StringTypes):
116 if not isinstance(args, types.StringTypes):
117 args = subprocess.list2cmdline(args)
117 args = subprocess.list2cmdline(args)
118
118
119 if startupinfo is None:
119 if startupinfo is None:
120 startupinfo = winprocess.STARTUPINFO()
120 startupinfo = winprocess.STARTUPINFO()
121
121
122 if None not in (p2cread, c2pwrite, errwrite):
122 if None not in (p2cread, c2pwrite, errwrite):
123 startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
123 startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
124
124
125 startupinfo.hStdInput = int(p2cread)
125 startupinfo.hStdInput = int(p2cread)
126 startupinfo.hStdOutput = int(c2pwrite)
126 startupinfo.hStdOutput = int(c2pwrite)
127 startupinfo.hStdError = int(errwrite)
127 startupinfo.hStdError = int(errwrite)
128 if shell:
128 if shell:
129 startupinfo.dwFlags |= winprocess.STARTF_USESHOWWINDOW
129 startupinfo.dwFlags |= winprocess.STARTF_USESHOWWINDOW
130 startupinfo.wShowWindow = winprocess.SW_HIDE
130 startupinfo.wShowWindow = winprocess.SW_HIDE
131 comspec = os.environ.get("COMSPEC", "cmd.exe")
131 comspec = os.environ.get("COMSPEC", "cmd.exe")
132 args = comspec + " /c " + args
132 args = comspec + " /c " + args
133
133
134 # We create a new job for this process, so that we can kill
134 # We create a new job for this process, so that we can kill
135 # the process and any sub-processes
135 # the process and any sub-processes
136 self._job = winprocess.CreateJobObject()
136 self._job = winprocess.CreateJobObject()
137
137
138 creationflags |= winprocess.CREATE_SUSPENDED
138 creationflags |= winprocess.CREATE_SUSPENDED
139 creationflags |= winprocess.CREATE_UNICODE_ENVIRONMENT
139 creationflags |= winprocess.CREATE_UNICODE_ENVIRONMENT
140
140
141 hp, ht, pid, tid = winprocess.CreateProcess(
141 hp, ht, pid, tid = winprocess.CreateProcess(
142 executable, args,
142 executable, args,
143 None, None, # No special security
143 None, None, # No special security
144 1, # Must inherit handles!
144 1, # Must inherit handles!
145 creationflags,
145 creationflags,
146 winprocess.EnvironmentBlock(env),
146 winprocess.EnvironmentBlock(env),
147 cwd, startupinfo)
147 cwd, startupinfo)
148
148
149 self._child_created = True
149 self._child_created = True
150 self._handle = hp
150 self._handle = hp
151 self._thread = ht
151 self._thread = ht
152 self.pid = pid
152 self.pid = pid
153
153
154 winprocess.AssignProcessToJobObject(self._job, hp)
154 # XXX: A try/except to fix UAC-related problems under
155 # Windows Vista, when reparenting jobs.
156 try:
157 winprocess.AssignProcessToJobObject(self._job, hp)
158 except WindowsError:
159 pass
155 winprocess.ResumeThread(ht)
160 winprocess.ResumeThread(ht)
156
161
157 if p2cread is not None:
162 if p2cread is not None:
158 p2cread.Close()
163 p2cread.Close()
159 if c2pwrite is not None:
164 if c2pwrite is not None:
160 c2pwrite.Close()
165 c2pwrite.Close()
161 if errwrite is not None:
166 if errwrite is not None:
162 errwrite.Close()
167 errwrite.Close()
163
168
164 def kill(self, group=True):
169 def kill(self, group=True):
165 """Kill the process. If group=True, all sub-processes will also be killed."""
170 """Kill the process. If group=True, all sub-processes will also be killed."""
166 if mswindows:
171 if mswindows:
167 if group:
172 if group:
168 winprocess.TerminateJobObject(self._job, 127)
173 winprocess.TerminateJobObject(self._job, 127)
169 else:
174 else:
170 winprocess.TerminateProcess(self._handle, 127)
175 winprocess.TerminateProcess(self._handle, 127)
171 self.returncode = 127
176 self.returncode = 127
172 else:
177 else:
173 if group:
178 if group:
174 os.killpg(self.pid, signal.SIGKILL)
179 os.killpg(self.pid, signal.SIGKILL)
175 else:
180 else:
176 os.kill(self.pid, signal.SIGKILL)
181 os.kill(self.pid, signal.SIGKILL)
177 self.returncode = -9
182 self.returncode = -9
178
183
179
184
1 NO CONTENT: file renamed from IPython/frontend/_process/pipedprocess.py to IPython/frontend/process/pipedprocess.py
NO CONTENT: file renamed from IPython/frontend/_process/pipedprocess.py to IPython/frontend/process/pipedprocess.py
1 NO CONTENT: file renamed from IPython/frontend/_process/winprocess.py to IPython/frontend/process/winprocess.py
NO CONTENT: file renamed from IPython/frontend/_process/winprocess.py to IPython/frontend/process/winprocess.py
@@ -1,180 +1,244 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Test process execution and IO redirection.
3 Test process execution and IO redirection.
4 """
4 """
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is
11 # Distributed under the terms of the BSD License. The full license is
12 # in the file COPYING, distributed as part of this software.
12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 from cStringIO import StringIO
15 from cStringIO import StringIO
16 import string
16 import string
17
17
18 from nose.tools import assert_equal
19
18 from IPython.ipapi import get as get_ipython0
20 from IPython.ipapi import get as get_ipython0
19 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
21 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
20 from copy import deepcopy
22 from copy import copy, deepcopy
23
24 def safe_deepcopy(d):
25 """ Deep copy every key of the given dict, when possible. Elsewhere
26 do a copy.
27 """
28 copied_d = dict()
29 for key, value in d.iteritems():
30 try:
31 copied_d[key] = deepcopy(value)
32 except:
33 try:
34 copied_d[key] = copy(value)
35 except:
36 copied_d[key] = value
37 return copied_d
38
21
39
22 class TestPrefilterFrontEnd(PrefilterFrontEnd):
40 class TestPrefilterFrontEnd(PrefilterFrontEnd):
23
41
24 input_prompt_template = string.Template('')
42 input_prompt_template = string.Template('')
25 output_prompt_template = string.Template('')
43 output_prompt_template = string.Template('')
26 banner = ''
44 banner = ''
27
45
28 def __init__(self):
46 def __init__(self):
29 ipython0 = get_ipython0().IP
30 self.out = StringIO()
47 self.out = StringIO()
31 PrefilterFrontEnd.__init__(self, ipython0=ipython0)
48 PrefilterFrontEnd.__init__(self)
32 # Clean up the namespace for isolation between tests
33 user_ns = self.ipython0.user_ns
34 # We need to keep references to things so that they don't
35 # get garbage collected (this stinks).
36 self.shadow_ns = dict()
37 for i in self.ipython0.magic_who_ls():
38 self.shadow_ns[i] = user_ns.pop(i)
39 # Some more code for isolation (yeah, crazy)
49 # Some more code for isolation (yeah, crazy)
40 self._on_enter()
50 self._on_enter()
41 self.out.flush()
51 self.out.flush()
42 self.out.reset()
52 self.out.reset()
43 self.out.truncate()
53 self.out.truncate()
44
54
45 def write(self, string, *args, **kwargs):
55 def write(self, string, *args, **kwargs):
46 self.out.write(string)
56 self.out.write(string)
47
57
48 def _on_enter(self):
58 def _on_enter(self):
49 self.input_buffer += '\n'
59 self.input_buffer += '\n'
50 PrefilterFrontEnd._on_enter(self)
60 PrefilterFrontEnd._on_enter(self)
51
61
52
62
53 def isolate_ipython0(func):
63 def isolate_ipython0(func):
54 """ Decorator to isolate execution that involves an iptyhon0.
64 """ Decorator to isolate execution that involves an iptyhon0.
65
66 Notes
67 ------
68
69 Apply only to functions with no arguments. Nose skips functions
70 with arguments.
55 """
71 """
56 def my_func(*args, **kwargs):
72 def my_func():
57 ipython0 = get_ipython0().IP
73 iplib = get_ipython0()
58 user_ns = deepcopy(ipython0.user_ns)
74 if iplib is None:
59 global_ns = deepcopy(ipython0.global_ns)
75 return func()
76 ipython0 = iplib.IP
77 global_ns = safe_deepcopy(ipython0.user_global_ns)
78 user_ns = safe_deepcopy(ipython0.user_ns)
60 try:
79 try:
61 func(*args, **kwargs)
80 out = func()
62 finally:
81 finally:
63 ipython0.user_ns = user_ns
82 ipython0.user_ns = user_ns
64 ipython0.global_ns = global_ns
83 ipython0.user_global_ns = global_ns
84 # Undo the hack at creation of PrefilterFrontEnd
85 from IPython import iplib
86 iplib.InteractiveShell.isthreaded = False
87 return out
65
88
89 my_func.__name__ = func.__name__
66 return my_func
90 return my_func
67
91
68
92
69 @isolate_ipython0
93 @isolate_ipython0
70 def test_execution():
94 def test_execution():
71 """ Test execution of a command.
95 """ Test execution of a command.
72 """
96 """
73 f = TestPrefilterFrontEnd()
97 f = TestPrefilterFrontEnd()
74 f.input_buffer = 'print 1'
98 f.input_buffer = 'print 1'
75 f._on_enter()
99 f._on_enter()
76 out_value = f.out.getvalue()
100 out_value = f.out.getvalue()
77 assert out_value == '1\n'
101 assert_equal(out_value, '1\n')
78
102
79
103
80 @isolate_ipython0
104 @isolate_ipython0
81 def test_multiline():
105 def test_multiline():
82 """ Test execution of a multiline command.
106 """ Test execution of a multiline command.
83 """
107 """
84 f = TestPrefilterFrontEnd()
108 f = TestPrefilterFrontEnd()
85 f.input_buffer = 'if True:'
109 f.input_buffer = 'if True:'
86 f._on_enter()
110 f._on_enter()
87 f.input_buffer += 'print 1'
111 f.input_buffer += 'print 1'
88 f._on_enter()
112 f._on_enter()
89 out_value = f.out.getvalue()
113 out_value = f.out.getvalue()
90 assert out_value == ''
114 yield assert_equal, out_value, ''
91 f._on_enter()
115 f._on_enter()
92 out_value = f.out.getvalue()
116 out_value = f.out.getvalue()
93 assert out_value == '1\n'
117 yield assert_equal, out_value, '1\n'
94 f = TestPrefilterFrontEnd()
118 f = TestPrefilterFrontEnd()
95 f.input_buffer='(1 +'
119 f.input_buffer='(1 +'
96 f._on_enter()
120 f._on_enter()
97 f.input_buffer += '0)'
121 f.input_buffer += '0)'
98 f._on_enter()
122 f._on_enter()
99 out_value = f.out.getvalue()
123 out_value = f.out.getvalue()
100 assert out_value == ''
124 yield assert_equal, out_value, ''
101 f._on_enter()
125 f._on_enter()
102 out_value = f.out.getvalue()
126 out_value = f.out.getvalue()
103 assert out_value == '1\n'
127 yield assert_equal, out_value, '1\n'
104
128
105
129
106 @isolate_ipython0
130 @isolate_ipython0
107 def test_capture():
131 def test_capture():
108 """ Test the capture of output in different channels.
132 """ Test the capture of output in different channels.
109 """
133 """
110 # Test on the OS-level stdout, stderr.
134 # Test on the OS-level stdout, stderr.
111 f = TestPrefilterFrontEnd()
135 f = TestPrefilterFrontEnd()
112 f.input_buffer = \
136 f.input_buffer = \
113 'import os; out=os.fdopen(1, "w"); out.write("1") ; out.flush()'
137 'import os; out=os.fdopen(1, "w"); out.write("1") ; out.flush()'
114 f._on_enter()
138 f._on_enter()
115 out_value = f.out.getvalue()
139 out_value = f.out.getvalue()
116 assert out_value == '1'
140 yield assert_equal, out_value, '1'
117 f = TestPrefilterFrontEnd()
141 f = TestPrefilterFrontEnd()
118 f.input_buffer = \
142 f.input_buffer = \
119 'import os; out=os.fdopen(2, "w"); out.write("1") ; out.flush()'
143 'import os; out=os.fdopen(2, "w"); out.write("1") ; out.flush()'
120 f._on_enter()
144 f._on_enter()
121 out_value = f.out.getvalue()
145 out_value = f.out.getvalue()
122 assert out_value == '1'
146 yield assert_equal, out_value, '1'
123
147
124
148
125 @isolate_ipython0
149 @isolate_ipython0
126 def test_magic():
150 def test_magic():
127 """ Test the magic expansion and history.
151 """ Test the magic expansion and history.
128
152
129 This test is fairly fragile and will break when magics change.
153 This test is fairly fragile and will break when magics change.
130 """
154 """
131 f = TestPrefilterFrontEnd()
155 f = TestPrefilterFrontEnd()
132 f.input_buffer += '%who'
156 f.input_buffer += '%who'
133 f._on_enter()
157 f._on_enter()
134 out_value = f.out.getvalue()
158 out_value = f.out.getvalue()
135 assert out_value == 'Interactive namespace is empty.\n'
159 assert_equal(out_value, 'Interactive namespace is empty.\n')
136
160
137
161
138 @isolate_ipython0
162 @isolate_ipython0
139 def test_help():
163 def test_help():
140 """ Test object inspection.
164 """ Test object inspection.
141 """
165 """
142 f = TestPrefilterFrontEnd()
166 f = TestPrefilterFrontEnd()
143 f.input_buffer += "def f():"
167 f.input_buffer += "def f():"
144 f._on_enter()
168 f._on_enter()
145 f.input_buffer += "'foobar'"
169 f.input_buffer += "'foobar'"
146 f._on_enter()
170 f._on_enter()
147 f.input_buffer += "pass"
171 f.input_buffer += "pass"
148 f._on_enter()
172 f._on_enter()
149 f._on_enter()
173 f._on_enter()
150 f.input_buffer += "f?"
174 f.input_buffer += "f?"
151 f._on_enter()
175 f._on_enter()
152 assert 'traceback' not in f.last_result
176 assert 'traceback' not in f.last_result
153 ## XXX: ipython doctest magic breaks this. I have no clue why
177 ## XXX: ipython doctest magic breaks this. I have no clue why
154 #out_value = f.out.getvalue()
178 #out_value = f.out.getvalue()
155 #assert out_value.split()[-1] == 'foobar'
179 #assert out_value.split()[-1] == 'foobar'
156
180
157
181
158 @isolate_ipython0
182 @isolate_ipython0
159 def test_completion():
183 def test_completion_simple():
160 """ Test command-line completion.
184 """ Test command-line completion on trivial examples.
161 """
185 """
162 f = TestPrefilterFrontEnd()
186 f = TestPrefilterFrontEnd()
163 f.input_buffer = 'zzza = 1'
187 f.input_buffer = 'zzza = 1'
164 f._on_enter()
188 f._on_enter()
165 f.input_buffer = 'zzzb = 2'
189 f.input_buffer = 'zzzb = 2'
166 f._on_enter()
190 f._on_enter()
167 f.input_buffer = 'zz'
191 f.input_buffer = 'zz'
168 f.complete_current_input()
192 f.complete_current_input()
169 out_value = f.out.getvalue()
193 out_value = f.out.getvalue()
170 assert out_value == '\nzzza zzzb '
194 yield assert_equal, out_value, '\nzzza zzzb '
171 assert f.input_buffer == 'zzz'
195 yield assert_equal, f.input_buffer, 'zzz'
196
197
198 @isolate_ipython0
199 def test_completion_parenthesis():
200 """ Test command-line completion when a parenthesis is open.
201 """
202 f = TestPrefilterFrontEnd()
203 f.input_buffer = 'zzza = 1'
204 f._on_enter()
205 f.input_buffer = 'zzzb = 2'
206 f._on_enter()
207 f.input_buffer = 'map(zz'
208 f.complete_current_input()
209 out_value = f.out.getvalue()
210 yield assert_equal, out_value, '\nzzza zzzb '
211 yield assert_equal, f.input_buffer, 'map(zzz'
212
213
214 @isolate_ipython0
215 def test_completion_indexing():
216 """ Test command-line completion when indexing on objects.
217 """
218 f = TestPrefilterFrontEnd()
219 f.input_buffer = 'a = [0]'
220 f._on_enter()
221 f.input_buffer = 'a[0].'
222 f.complete_current_input()
223 assert_equal(f.input_buffer, 'a[0].__')
224
225
226 @isolate_ipython0
227 def test_completion_equal():
228 """ Test command-line completion when the delimiter is "=", not " ".
229 """
230 f = TestPrefilterFrontEnd()
231 f.input_buffer = 'a=1.'
232 f.complete_current_input()
233 assert_equal(f.input_buffer, 'a=1.__')
234
172
235
173
236
174 if __name__ == '__main__':
237 if __name__ == '__main__':
175 test_magic()
238 test_magic()
176 test_help()
239 test_help()
177 test_execution()
240 test_execution()
178 test_multiline()
241 test_multiline()
179 test_capture()
242 test_capture()
180 test_completion()
243 test_completion_simple()
244 test_completion_complex()
@@ -1,67 +1,67 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Test process execution and IO redirection.
3 Test process execution and IO redirection.
4 """
4 """
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is
11 # Distributed under the terms of the BSD License. The full license is
12 # in the file COPYING, distributed as part of this software.
12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 from cStringIO import StringIO
15 from cStringIO import StringIO
16 from time import sleep
16 from time import sleep
17 import sys
17 import sys
18
18
19 from IPython.frontend._process import PipedProcess
19 from IPython.frontend.process import PipedProcess
20 from IPython.testing import decorators as testdec
20 from IPython.testing import decorators as testdec
21
21
22
22
23 def test_capture_out():
23 def test_capture_out():
24 """ A simple test to see if we can execute a process and get the output.
24 """ A simple test to see if we can execute a process and get the output.
25 """
25 """
26 s = StringIO()
26 s = StringIO()
27 p = PipedProcess('echo 1', out_callback=s.write, )
27 p = PipedProcess('echo 1', out_callback=s.write, )
28 p.start()
28 p.start()
29 p.join()
29 p.join()
30 result = s.getvalue().rstrip()
30 result = s.getvalue().rstrip()
31 assert result == '1'
31 assert result == '1'
32
32
33
33
34 def test_io():
34 def test_io():
35 """ Checks that we can send characters on stdin to the process.
35 """ Checks that we can send characters on stdin to the process.
36 """
36 """
37 s = StringIO()
37 s = StringIO()
38 p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"',
38 p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"',
39 out_callback=s.write, )
39 out_callback=s.write, )
40 p.start()
40 p.start()
41 test_string = '12345\n'
41 test_string = '12345\n'
42 while not hasattr(p, 'process'):
42 while not hasattr(p, 'process'):
43 sleep(0.1)
43 sleep(0.1)
44 p.process.stdin.write(test_string)
44 p.process.stdin.write(test_string)
45 p.join()
45 p.join()
46 result = s.getvalue()
46 result = s.getvalue()
47 assert result == test_string
47 assert result == test_string
48
48
49
49
50 def test_kill():
50 def test_kill():
51 """ Check that we can kill a process, and its subprocess.
51 """ Check that we can kill a process, and its subprocess.
52 """
52 """
53 s = StringIO()
53 s = StringIO()
54 p = PipedProcess(sys.executable + ' -c "a = raw_input();"',
54 p = PipedProcess(sys.executable + ' -c "a = raw_input();"',
55 out_callback=s.write, )
55 out_callback=s.write, )
56 p.start()
56 p.start()
57 while not hasattr(p, 'process'):
57 while not hasattr(p, 'process'):
58 sleep(0.1)
58 sleep(0.1)
59 p.process.kill()
59 p.process.kill()
60 assert p.process.poll() is not None
60 assert p.process.poll() is not None
61
61
62
62
63 if __name__ == '__main__':
63 if __name__ == '__main__':
64 test_capture_out()
64 test_capture_out()
65 test_io()
65 test_io()
66 test_kill()
66 test_kill()
67
67
@@ -1,436 +1,625 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 import time
26 import time
27 import sys
27 import sys
28 import string
29
28 LINESEP = '\n'
30 LINESEP = '\n'
29 if sys.platform == 'win32':
31 if sys.platform == 'win32':
30 LINESEP = '\n\r'
32 LINESEP = '\n\r'
31
33
32 import re
34 import re
33
35
34 # FIXME: Need to provide an API for non user-generated display on the
36 # FIXME: Need to provide an API for non user-generated display on the
35 # screen: this should not be editable by the user.
37 # screen: this should not be editable by the user.
38 #-------------------------------------------------------------------------------
39 # Constants
40 #-------------------------------------------------------------------------------
41 _COMPLETE_BUFFER_MARKER = 31
42 _ERROR_MARKER = 30
43 _INPUT_MARKER = 29
36
44
37 _DEFAULT_SIZE = 10
45 _DEFAULT_SIZE = 10
38 if sys.platform == 'darwin':
46 if sys.platform == 'darwin':
39 _DEFAULT_SIZE = 12
47 _DEFAULT_SIZE = 12
40
48
41 _DEFAULT_STYLE = {
49 _DEFAULT_STYLE = {
42 'stdout' : 'fore:#0000FF',
50 #background definition
43 'stderr' : 'fore:#007f00',
44 'trace' : 'fore:#FF0000',
45
46 'default' : 'size:%d' % _DEFAULT_SIZE,
51 'default' : 'size:%d' % _DEFAULT_SIZE,
47 'bracegood' : 'fore:#00AA00,back:#000000,bold',
52 'bracegood' : 'fore:#00AA00,back:#000000,bold',
48 'bracebad' : 'fore:#FF0000,back:#000000,bold',
53 'bracebad' : 'fore:#FF0000,back:#000000,bold',
49
54
55 # Edge column: a number of None
56 'edge_column' : -1,
57
50 # properties for the various Python lexer styles
58 # properties for the various Python lexer styles
51 'comment' : 'fore:#007F00',
59 'comment' : 'fore:#007F00',
52 'number' : 'fore:#007F7F',
60 'number' : 'fore:#007F7F',
53 'string' : 'fore:#7F007F,italic',
61 'string' : 'fore:#7F007F,italic',
54 'char' : 'fore:#7F007F,italic',
62 'char' : 'fore:#7F007F,italic',
55 'keyword' : 'fore:#00007F,bold',
63 'keyword' : 'fore:#00007F,bold',
56 'triple' : 'fore:#7F0000',
64 'triple' : 'fore:#7F0000',
57 'tripledouble' : 'fore:#7F0000',
65 'tripledouble' : 'fore:#7F0000',
58 'class' : 'fore:#0000FF,bold,underline',
66 'class' : 'fore:#0000FF,bold,underline',
59 'def' : 'fore:#007F7F,bold',
67 'def' : 'fore:#007F7F,bold',
60 'operator' : 'bold'
68 'operator' : 'bold',
69
70 # Default colors
71 'trace' : '#FAFAF1', # Nice green
72 'stdout' : '#FDFFD3', # Nice yellow
73 'stderr' : '#FFF1F1', # Nice red
74
75 # Default scintilla settings
76 'antialiasing' : True,
77 'carret_color' : 'BLACK',
78 'background_color' :'WHITE',
79
80 #prompt definition
81 'prompt_in1' : \
82 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02',
83
84 'prompt_out': \
85 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02',
61 }
86 }
62
87
63 # new style numbers
88 # new style numbers
64 _STDOUT_STYLE = 15
89 _STDOUT_STYLE = 15
65 _STDERR_STYLE = 16
90 _STDERR_STYLE = 16
66 _TRACE_STYLE = 17
91 _TRACE_STYLE = 17
67
92
68
93
69 # system colors
94 # system colors
70 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
95 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
71
96
97 # Translation table from ANSI escape sequences to color.
98 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
99 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
100 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
101 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
102 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
103 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
104 '1;34': [12, 'LIGHT BLUE'], '1;35':
105 [13, 'MEDIUM VIOLET RED'],
106 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
107
108 # XXX: Maybe one day we should factor this code with ColorANSI. Right now
109 # ColorANSI is hard to reuse and makes our code more complex.
110
111 #we define platform specific fonts
112 if wx.Platform == '__WXMSW__':
113 FACES = { 'times': 'Times New Roman',
114 'mono' : 'Courier New',
115 'helv' : 'Arial',
116 'other': 'Comic Sans MS',
117 'size' : 10,
118 'size2': 8,
119 }
120 elif wx.Platform == '__WXMAC__':
121 FACES = { 'times': 'Times New Roman',
122 'mono' : 'Monaco',
123 'helv' : 'Arial',
124 'other': 'Comic Sans MS',
125 'size' : 10,
126 'size2': 8,
127 }
128 else:
129 FACES = { 'times': 'Times',
130 'mono' : 'Courier',
131 'helv' : 'Helvetica',
132 'other': 'new century schoolbook',
133 'size' : 10,
134 'size2': 8,
135 }
136
137
72 #-------------------------------------------------------------------------------
138 #-------------------------------------------------------------------------------
73 # The console widget class
139 # The console widget class
74 #-------------------------------------------------------------------------------
140 #-------------------------------------------------------------------------------
75 class ConsoleWidget(editwindow.EditWindow):
141 class ConsoleWidget(editwindow.EditWindow):
76 """ Specialized styled text control view for console-like workflow.
142 """ Specialized styled text control view for console-like workflow.
77
143
78 This widget is mainly interested in dealing with the prompt and
144 This widget is mainly interested in dealing with the prompt and
79 keeping the cursor inside the editing line.
145 keeping the cursor inside the editing line.
80 """
146 """
81
147
82 # This is where the title captured from the ANSI escape sequences are
148 # This is where the title captured from the ANSI escape sequences are
83 # stored.
149 # stored.
84 title = 'Console'
150 title = 'Console'
85
151
152 # Last prompt printed
153 last_prompt = ''
154
86 # The buffer being edited.
155 # The buffer being edited.
87 def _set_input_buffer(self, string):
156 def _set_input_buffer(self, string):
88 self.SetSelection(self.current_prompt_pos, self.GetLength())
157 self.SetSelection(self.current_prompt_pos, self.GetLength())
89 self.ReplaceSelection(string)
158 self.ReplaceSelection(string)
90 self.GotoPos(self.GetLength())
159 self.GotoPos(self.GetLength())
91
160
92 def _get_input_buffer(self):
161 def _get_input_buffer(self):
93 """ Returns the text in current edit buffer.
162 """ Returns the text in current edit buffer.
94 """
163 """
95 input_buffer = self.GetTextRange(self.current_prompt_pos,
164 input_buffer = self.GetTextRange(self.current_prompt_pos,
96 self.GetLength())
165 self.GetLength())
97 input_buffer = input_buffer.replace(LINESEP, '\n')
166 input_buffer = input_buffer.replace(LINESEP, '\n')
98 return input_buffer
167 return input_buffer
99
168
100 input_buffer = property(_get_input_buffer, _set_input_buffer)
169 input_buffer = property(_get_input_buffer, _set_input_buffer)
101
170
102 style = _DEFAULT_STYLE.copy()
171 style = _DEFAULT_STYLE.copy()
103
172
104 # Translation table from ANSI escape sequences to color. Override
173 # Translation table from ANSI escape sequences to color. Override
105 # this to specify your colors.
174 # this to specify your colors.
106 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
175 ANSI_STYLES = ANSI_STYLES.copy()
107 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
108 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
109 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
110 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
111 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
112 '1;34': [12, 'LIGHT BLUE'], '1;35':
113 [13, 'MEDIUM VIOLET RED'],
114 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
115
116 # The color of the carret (call _apply_style() after setting)
117 carret_color = 'BLACK'
118
176
177 # Font faces
178 faces = FACES.copy()
179
119 # Store the last time a refresh was done
180 # Store the last time a refresh was done
120 _last_refresh_time = 0
181 _last_refresh_time = 0
121
182
122 #--------------------------------------------------------------------------
183 #--------------------------------------------------------------------------
123 # Public API
184 # Public API
124 #--------------------------------------------------------------------------
185 #--------------------------------------------------------------------------
125
186
126 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
187 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
127 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
188 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
128 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
189 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
129 self._configure_scintilla()
190 self.configure_scintilla()
191 # Track if 'enter' key as ever been processed
192 # This variable will only be reallowed until key goes up
193 self.enter_catched = False
194 self.current_prompt_pos = 0
130
195
131 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
196 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
132 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
197 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
133
198
134
199
135 def write(self, text, refresh=True):
200 def write(self, text, refresh=True):
136 """ Write given text to buffer, while translating the ansi escape
201 """ Write given text to buffer, while translating the ansi escape
137 sequences.
202 sequences.
138 """
203 """
139 # XXX: do not put print statements to sys.stdout/sys.stderr in
204 # XXX: do not put print statements to sys.stdout/sys.stderr in
140 # this method, the print statements will call this method, as
205 # this method, the print statements will call this method, as
141 # you will end up with an infinit loop
206 # you will end up with an infinit loop
142 title = self.title_pat.split(text)
207 title = self.title_pat.split(text)
143 if len(title)>1:
208 if len(title)>1:
144 self.title = title[-2]
209 self.title = title[-2]
145
210
146 text = self.title_pat.sub('', text)
211 text = self.title_pat.sub('', text)
147 segments = self.color_pat.split(text)
212 segments = self.color_pat.split(text)
148 segment = segments.pop(0)
213 segment = segments.pop(0)
149 self.GotoPos(self.GetLength())
214 self.GotoPos(self.GetLength())
150 self.StartStyling(self.GetLength(), 0xFF)
215 self.StartStyling(self.GetLength(), 0xFF)
151 try:
216 try:
152 self.AppendText(segment)
217 self.AppendText(segment)
153 except UnicodeDecodeError:
218 except UnicodeDecodeError:
154 # XXX: Do I really want to skip the exception?
219 # XXX: Do I really want to skip the exception?
155 pass
220 pass
156
221
157 if segments:
222 if segments:
158 for ansi_tag, text in zip(segments[::2], segments[1::2]):
223 for ansi_tag, text in zip(segments[::2], segments[1::2]):
159 self.StartStyling(self.GetLength(), 0xFF)
224 self.StartStyling(self.GetLength(), 0xFF)
160 try:
225 try:
161 self.AppendText(text)
226 self.AppendText(text)
162 except UnicodeDecodeError:
227 except UnicodeDecodeError:
163 # XXX: Do I really want to skip the exception?
228 # XXX: Do I really want to skip the exception?
164 pass
229 pass
165
230
166 if ansi_tag not in self.ANSI_STYLES:
231 if ansi_tag not in self.ANSI_STYLES:
167 style = 0
232 style = 0
168 else:
233 else:
169 style = self.ANSI_STYLES[ansi_tag][0]
234 style = self.ANSI_STYLES[ansi_tag][0]
170
235
171 self.SetStyling(len(text), style)
236 self.SetStyling(len(text), style)
172
237
173 self.GotoPos(self.GetLength())
238 self.GotoPos(self.GetLength())
174 if refresh:
239 if refresh:
175 current_time = time.time()
240 current_time = time.time()
176 if current_time - self._last_refresh_time > 0.03:
241 if current_time - self._last_refresh_time > 0.03:
177 if sys.platform == 'win32':
242 if sys.platform == 'win32':
178 wx.SafeYield()
243 wx.SafeYield()
179 else:
244 else:
180 wx.Yield()
245 wx.Yield()
181 # self.ProcessEvent(wx.PaintEvent())
246 # self.ProcessEvent(wx.PaintEvent())
182 self._last_refresh_time = current_time
247 self._last_refresh_time = current_time
183
248
184
249
185 def new_prompt(self, prompt):
250 def new_prompt(self, prompt):
186 """ Prints a prompt at start of line, and move the start of the
251 """ Prints a prompt at start of line, and move the start of the
187 current block there.
252 current block there.
188
253
189 The prompt can be given with ascii escape sequences.
254 The prompt can be given with ascii escape sequences.
190 """
255 """
191 self.write(prompt, refresh=False)
256 self.write(prompt, refresh=False)
192 # now we update our cursor giving end of prompt
257 # now we update our cursor giving end of prompt
193 self.current_prompt_pos = self.GetLength()
258 self.current_prompt_pos = self.GetLength()
194 self.current_prompt_line = self.GetCurrentLine()
259 self.current_prompt_line = self.GetCurrentLine()
195 self.EnsureCaretVisible()
260 self.EnsureCaretVisible()
261 self.last_prompt = prompt
196
262
197
263
264 def continuation_prompt(self):
265 """ Returns the current continuation prompt.
266 We need to implement this method here to deal with the
267 ascii escape sequences cleaning up.
268 """
269 # ASCII-less prompt
270 ascii_less = ''.join(self.color_pat.split(self.last_prompt)[2::2])
271 return "."*(len(ascii_less)-2) + ': '
272
273
198 def scroll_to_bottom(self):
274 def scroll_to_bottom(self):
199 maxrange = self.GetScrollRange(wx.VERTICAL)
275 maxrange = self.GetScrollRange(wx.VERTICAL)
200 self.ScrollLines(maxrange)
276 self.ScrollLines(maxrange)
201
277
202
278
203 def pop_completion(self, possibilities, offset=0):
279 def pop_completion(self, possibilities, offset=0):
204 """ Pops up an autocompletion menu. Offset is the offset
280 """ Pops up an autocompletion menu. Offset is the offset
205 in characters of the position at which the menu should
281 in characters of the position at which the menu should
206 appear, relativ to the cursor.
282 appear, relativ to the cursor.
207 """
283 """
208 self.AutoCompSetIgnoreCase(False)
284 self.AutoCompSetIgnoreCase(False)
209 self.AutoCompSetAutoHide(False)
285 self.AutoCompSetAutoHide(False)
210 self.AutoCompSetMaxHeight(len(possibilities))
286 self.AutoCompSetMaxHeight(len(possibilities))
211 self.AutoCompShow(offset, " ".join(possibilities))
287 self.AutoCompShow(offset, " ".join(possibilities))
212
288
213
289
214 def get_line_width(self):
290 def get_line_width(self):
215 """ Return the width of the line in characters.
291 """ Return the width of the line in characters.
216 """
292 """
217 return self.GetSize()[0]/self.GetCharWidth()
293 return self.GetSize()[0]/self.GetCharWidth()
218
294
219 #--------------------------------------------------------------------------
220 # EditWindow API
221 #--------------------------------------------------------------------------
222
295
223 def OnUpdateUI(self, event):
296 def configure_scintilla(self):
224 """ Override the OnUpdateUI of the EditWindow class, to prevent
297 """ Set up all the styling option of the embedded scintilla
225 syntax highlighting both for faster redraw, and for more
298 widget.
226 consistent look and feel.
227 """
299 """
300 p = self.style.copy()
301
302 # Marker for complete buffer.
303 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
304 background=p['trace'])
228
305
229 #--------------------------------------------------------------------------
306 # Marker for current input buffer.
230 # Private API
307 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
231 #--------------------------------------------------------------------------
308 background=p['stdout'])
232
309 # Marker for tracebacks.
233 def _apply_style(self):
310 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
234 """ Applies the colors for the different text elements and the
311 background=p['stderr'])
235 carret.
236 """
237 self.SetCaretForeground(self.carret_color)
238
239 #self.StyleClearAll()
240 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
241 "fore:#FF0000,back:#0000FF,bold")
242 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
243 "fore:#000000,back:#FF0000,bold")
244
245 for style in self.ANSI_STYLES.values():
246 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
247
312
248
249 def _configure_scintilla(self):
250 self.SetEOLMode(stc.STC_EOL_LF)
313 self.SetEOLMode(stc.STC_EOL_LF)
251
314
252 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
315 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
253 # the widget
316 # the widget
254 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
317 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
255 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
318 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
256 # Also allow Ctrl Shift "=" for poor non US keyboard users.
319 # Also allow Ctrl Shift "=" for poor non US keyboard users.
257 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
320 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
258 stc.STC_CMD_ZOOMIN)
321 stc.STC_CMD_ZOOMIN)
259
322
260 # Keys: we need to clear some of the keys the that don't play
323 # Keys: we need to clear some of the keys the that don't play
261 # well with a console.
324 # well with a console.
262 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
325 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
263 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
326 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
264 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
327 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
265 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
328 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
266
329
267 self.SetEOLMode(stc.STC_EOL_CRLF)
330 self.SetEOLMode(stc.STC_EOL_CRLF)
268 self.SetWrapMode(stc.STC_WRAP_CHAR)
331 self.SetWrapMode(stc.STC_WRAP_CHAR)
269 self.SetWrapMode(stc.STC_WRAP_WORD)
332 self.SetWrapMode(stc.STC_WRAP_WORD)
270 self.SetBufferedDraw(True)
333 self.SetBufferedDraw(True)
271 self.SetUseAntiAliasing(True)
334
335 self.SetUseAntiAliasing(p['antialiasing'])
336
272 self.SetLayoutCache(stc.STC_CACHE_PAGE)
337 self.SetLayoutCache(stc.STC_CACHE_PAGE)
273 self.SetUndoCollection(False)
338 self.SetUndoCollection(False)
274 self.SetUseTabs(True)
339 self.SetUseTabs(True)
275 self.SetIndent(4)
340 self.SetIndent(4)
276 self.SetTabWidth(4)
341 self.SetTabWidth(4)
277
342
278 # we don't want scintilla's autocompletion to choose
343 # we don't want scintilla's autocompletion to choose
279 # automaticaly out of a single choice list, as we pop it up
344 # automaticaly out of a single choice list, as we pop it up
280 # automaticaly
345 # automaticaly
281 self.AutoCompSetChooseSingle(False)
346 self.AutoCompSetChooseSingle(False)
282 self.AutoCompSetMaxHeight(10)
347 self.AutoCompSetMaxHeight(10)
283 # XXX: this doesn't seem to have an effect.
348 # XXX: this doesn't seem to have an effect.
284 self.AutoCompSetFillUps('\n')
349 self.AutoCompSetFillUps('\n')
285
350
286 self.SetMargins(3, 3) #text is moved away from border with 3px
351 self.SetMargins(3, 3) #text is moved away from border with 3px
287 # Suppressing Scintilla margins
352 # Suppressing Scintilla margins
288 self.SetMarginWidth(0, 0)
353 self.SetMarginWidth(0, 0)
289 self.SetMarginWidth(1, 0)
354 self.SetMarginWidth(1, 0)
290 self.SetMarginWidth(2, 0)
355 self.SetMarginWidth(2, 0)
291
356
292 self._apply_style()
293
294 # Xterm escape sequences
357 # Xterm escape sequences
295 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
358 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
296 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
359 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
297
360
298 #self.SetEdgeMode(stc.STC_EDGE_LINE)
299 #self.SetEdgeColumn(80)
300
301 # styles
361 # styles
302 p = self.style
362
303 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
363 self.SetCaretForeground(p['carret_color'])
364
365 background_color = p['background_color']
366
367 if 'default' in p:
368 if 'back' not in p['default']:
369 p['default'] += ',back:%s' % background_color
370 if 'size' not in p['default']:
371 p['default'] += ',size:%s' % self.faces['size']
372 if 'face' not in p['default']:
373 p['default'] += ',face:%s' % self.faces['mono']
374
375 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
376 else:
377 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
378 "fore:%s,back:%s,size:%d,face:%s"
379 % (self.ANSI_STYLES['0;30'][1],
380 background_color,
381 self.faces['size'], self.faces['mono']))
382
304 self.StyleClearAll()
383 self.StyleClearAll()
384
385 # XXX: two lines below are usefull if not using the lexer
386 #for style in self.ANSI_STYLES.values():
387 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
388
389 # prompt definition
390 self.prompt_in1 = p['prompt_in1']
391 self.prompt_out = p['prompt_out']
392
393 self.output_prompt_template = string.Template(self.prompt_out)
394 self.input_prompt_template = string.Template(self.prompt_in1)
395
305 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
396 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
306 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
397 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
307 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
398 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
308
309 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
399 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
310 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
400 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
311 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
401 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
312 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
402 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
313 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
403 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
314 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
404 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
315 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
405 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
316 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
406 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
317 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
407 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
318 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
408 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
319 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
409 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
320 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
410 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
321 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
411 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
322 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
412 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
323
413
414 edge_column = p['edge_column']
415 if edge_column is not None and edge_column > 0:
416 #we add a vertical line to console widget
417 self.SetEdgeMode(stc.STC_EDGE_LINE)
418 self.SetEdgeColumn(edge_column)
419
420
421 #--------------------------------------------------------------------------
422 # EditWindow API
423 #--------------------------------------------------------------------------
424
425 def OnUpdateUI(self, event):
426 """ Override the OnUpdateUI of the EditWindow class, to prevent
427 syntax highlighting both for faster redraw, and for more
428 consistent look and feel.
429 """
430
431
432 #--------------------------------------------------------------------------
433 # Private API
434 #--------------------------------------------------------------------------
435
324 def _on_key_down(self, event, skip=True):
436 def _on_key_down(self, event, skip=True):
325 """ Key press callback used for correcting behavior for
437 """ Key press callback used for correcting behavior for
326 console-like interfaces: the cursor is constraint to be after
438 console-like interfaces: the cursor is constraint to be after
327 the last prompt.
439 the last prompt.
328
440
329 Return True if event as been catched.
441 Return True if event as been catched.
330 """
442 """
331 catched = True
443 catched = True
444 # XXX: Would the right way to do this be to have a
445 # dictionary at the instance level associating keys with
446 # callbacks? How would we deal with inheritance? And Do the
447 # different callbacks share local variables?
448
332 # Intercept some specific keys.
449 # Intercept some specific keys.
333 if event.KeyCode == ord('L') and event.ControlDown() :
450 if event.KeyCode == ord('L') and event.ControlDown() :
334 self.scroll_to_bottom()
451 self.scroll_to_bottom()
335 elif event.KeyCode == ord('K') and event.ControlDown() :
452 elif event.KeyCode == ord('K') and event.ControlDown() :
336 self.input_buffer = ''
453 self.input_buffer = ''
337 elif event.KeyCode == ord('A') and event.ControlDown() :
454 elif event.KeyCode == ord('A') and event.ControlDown() :
338 self.GotoPos(self.GetLength())
455 self.GotoPos(self.GetLength())
339 self.SetSelectionStart(self.current_prompt_pos)
456 self.SetSelectionStart(self.current_prompt_pos)
340 self.SetSelectionEnd(self.GetCurrentPos())
457 self.SetSelectionEnd(self.GetCurrentPos())
341 catched = True
458 catched = True
342 elif event.KeyCode == ord('E') and event.ControlDown() :
459 elif event.KeyCode == ord('E') and event.ControlDown() :
343 self.GotoPos(self.GetLength())
460 self.GotoPos(self.GetLength())
344 catched = True
461 catched = True
345 elif event.KeyCode == wx.WXK_PAGEUP:
462 elif event.KeyCode == wx.WXK_PAGEUP:
346 self.ScrollPages(-1)
463 self.ScrollPages(-1)
347 elif event.KeyCode == wx.WXK_PAGEDOWN:
464 elif event.KeyCode == wx.WXK_PAGEDOWN:
348 self.ScrollPages(1)
465 self.ScrollPages(1)
466 elif event.KeyCode == wx.WXK_HOME:
467 self.GotoPos(self.GetLength())
468 elif event.KeyCode == wx.WXK_END:
469 self.GotoPos(self.GetLength())
349 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
470 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
350 self.ScrollLines(-1)
471 self.ScrollLines(-1)
351 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
472 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
352 self.ScrollLines(1)
473 self.ScrollLines(1)
353 else:
474 else:
354 catched = False
475 catched = False
355
476
356 if self.AutoCompActive():
477 if self.AutoCompActive():
357 event.Skip()
478 event.Skip()
358 else:
479 else:
359 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
480 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
360 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
481 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN,
482 wx.MOD_SHIFT):
361 catched = True
483 catched = True
362 self.CallTipCancel()
484 if not self.enter_catched:
363 self.write('\n', refresh=False)
485 self.CallTipCancel()
364 # Under windows scintilla seems to be doing funny stuff to the
486 if event.Modifiers == wx.MOD_SHIFT:
365 # line returns here, but the getter for input_buffer filters
487 # Try to force execution
366 # this out.
488 self.GotoPos(self.GetLength())
367 if sys.platform == 'win32':
489 self.write('\n' + self.continuation_prompt(),
368 self.input_buffer = self.input_buffer
490 refresh=False)
369 self._on_enter()
491 self._on_enter()
492 else:
493 self._on_enter()
494 self.enter_catched = True
370
495
371 elif event.KeyCode == wx.WXK_HOME:
496 elif event.KeyCode == wx.WXK_HOME:
372 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
497 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
373 self.GotoPos(self.current_prompt_pos)
498 self.GotoPos(self.current_prompt_pos)
374 catched = True
499 catched = True
375
500
376 elif event.Modifiers == wx.MOD_SHIFT:
501 elif event.Modifiers == wx.MOD_SHIFT:
377 # FIXME: This behavior is not ideal: if the selection
502 # FIXME: This behavior is not ideal: if the selection
378 # is already started, it will jump.
503 # is already started, it will jump.
379 self.SetSelectionStart(self.current_prompt_pos)
504 self.SetSelectionStart(self.current_prompt_pos)
380 self.SetSelectionEnd(self.GetCurrentPos())
505 self.SetSelectionEnd(self.GetCurrentPos())
381 catched = True
506 catched = True
382
507
383 elif event.KeyCode == wx.WXK_UP:
508 elif event.KeyCode == wx.WXK_UP:
384 if self.GetCurrentLine() > self.current_prompt_line:
509 if self.GetCurrentLine() > self.current_prompt_line:
385 if self.GetCurrentLine() == self.current_prompt_line + 1 \
510 if self.GetCurrentLine() == self.current_prompt_line + 1 \
386 and self.GetColumn(self.GetCurrentPos()) < \
511 and self.GetColumn(self.GetCurrentPos()) < \
387 self.GetColumn(self.current_prompt_pos):
512 self.GetColumn(self.current_prompt_pos):
388 self.GotoPos(self.current_prompt_pos)
513 self.GotoPos(self.current_prompt_pos)
389 else:
514 else:
390 event.Skip()
515 event.Skip()
391 catched = True
516 catched = True
392
517
393 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
518 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
394 if self.GetCurrentPos() > self.current_prompt_pos:
519 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
520 event.Skip()
521 catched = True
522
523 elif event.KeyCode == wx.WXK_RIGHT:
524 if not self._keep_cursor_in_buffer(self.GetCurrentPos() + 1):
525 event.Skip()
526 catched = True
527
528
529 elif event.KeyCode == wx.WXK_DELETE:
530 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
395 event.Skip()
531 event.Skip()
396 catched = True
532 catched = True
397
533
398 if skip and not catched:
534 if skip and not catched:
399 # Put the cursor back in the edit region
535 # Put the cursor back in the edit region
400 if self.GetCurrentPos() < self.current_prompt_pos:
536 if not self._keep_cursor_in_buffer():
401 self.GotoPos(self.current_prompt_pos)
537 if not (self.GetCurrentPos() == self.GetLength()
402 else:
538 and event.KeyCode == wx.WXK_DELETE):
403 event.Skip()
539 event.Skip()
540 catched = True
404
541
405 return catched
542 return catched
406
543
407
544
408 def _on_key_up(self, event, skip=True):
545 def _on_key_up(self, event, skip=True):
409 """ If cursor is outside the editing region, put it back.
546 """ If cursor is outside the editing region, put it back.
410 """
547 """
411 event.Skip()
548 if skip:
412 if self.GetCurrentPos() < self.current_prompt_pos:
549 event.Skip()
413 self.GotoPos(self.current_prompt_pos)
550 self._keep_cursor_in_buffer()
551
552
553 # XXX: I need to avoid the problem of having an empty glass;
554 def _keep_cursor_in_buffer(self, pos=None):
555 """ Checks if the cursor is where it is allowed to be. If not,
556 put it back.
414
557
558 Returns
559 -------
560 cursor_moved: Boolean
561 whether or not the cursor was moved by this routine.
562
563 Notes
564 ------
565 WARNING: This does proper checks only for horizontal
566 movements.
567 """
568 if pos is None:
569 current_pos = self.GetCurrentPos()
570 else:
571 current_pos = pos
572 if current_pos < self.current_prompt_pos:
573 self.GotoPos(self.current_prompt_pos)
574 return True
575 line_num = self.LineFromPosition(current_pos)
576 if not current_pos > self.GetLength():
577 line_pos = self.GetColumn(current_pos)
578 else:
579 line_pos = self.GetColumn(self.GetLength())
580 line = self.GetLine(line_num)
581 # Jump the continuation prompt
582 continuation_prompt = self.continuation_prompt()
583 if ( line.startswith(continuation_prompt)
584 and line_pos < len(continuation_prompt)):
585 if line_pos < 2:
586 # We are at the beginning of the line, trying to move
587 # forward: jump forward.
588 self.GotoPos(current_pos + 1 +
589 len(continuation_prompt) - line_pos)
590 else:
591 # Jump back up
592 self.GotoPos(self.GetLineEndPosition(line_num-1))
593 return True
594 elif ( current_pos > self.GetLineEndPosition(line_num)
595 and not current_pos == self.GetLength()):
596 # Jump to next line
597 self.GotoPos(current_pos + 1 +
598 len(continuation_prompt))
599 return True
600
601 # We re-allow enter event processing
602 self.enter_catched = False
603 return False
415
604
416
605
417 if __name__ == '__main__':
606 if __name__ == '__main__':
418 # Some simple code to test the console widget.
607 # Some simple code to test the console widget.
419 class MainWindow(wx.Frame):
608 class MainWindow(wx.Frame):
420 def __init__(self, parent, id, title):
609 def __init__(self, parent, id, title):
421 wx.Frame.__init__(self, parent, id, title, size=(300,250))
610 wx.Frame.__init__(self, parent, id, title, size=(300, 250))
422 self._sizer = wx.BoxSizer(wx.VERTICAL)
611 self._sizer = wx.BoxSizer(wx.VERTICAL)
423 self.console_widget = ConsoleWidget(self)
612 self.console_widget = ConsoleWidget(self)
424 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
613 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
425 self.SetSizer(self._sizer)
614 self.SetSizer(self._sizer)
426 self.SetAutoLayout(1)
615 self.SetAutoLayout(1)
427 self.Show(True)
616 self.Show(True)
428
617
429 app = wx.PySimpleApp()
618 app = wx.PySimpleApp()
430 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
619 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
431 w.SetSize((780, 460))
620 w.SetSize((780, 460))
432 w.Show()
621 w.Show()
433
622
434 app.MainLoop()
623 app.MainLoop()
435
624
436
625
@@ -1,110 +1,119 b''
1 """
1 """
2 Entry point for a simple application giving a graphical frontend to
2 Entry point for a simple application giving a graphical frontend to
3 ipython.
3 ipython.
4 """
4 """
5
5
6 try:
6 try:
7 import wx
7 import wx
8 except ImportError, e:
8 except ImportError, e:
9 e.message = """%s
9 e.message = """%s
10 ________________________________________________________________________________
10 ________________________________________________________________________________
11 You need wxPython to run this application.
11 You need wxPython to run this application.
12 """ % e.message
12 """ % e.message
13 e.args = (e.message, ) + e.args[1:]
13 e.args = (e.message, ) + e.args[1:]
14 raise e
14 raise e
15
15
16 from wx_frontend import WxController
16 from wx_frontend import WxController
17 import __builtin__
17 import __builtin__
18
18
19
19
20 class IPythonXController(WxController):
20 class IPythonXController(WxController):
21 """ Sub class of WxController that adds some application-specific
21 """ Sub class of WxController that adds some application-specific
22 bindings.
22 bindings.
23 """
23 """
24
24
25 debug = False
25 debug = False
26
26
27 def __init__(self, *args, **kwargs):
27 def __init__(self, *args, **kwargs):
28 WxController.__init__(self, *args, **kwargs)
28 WxController.__init__(self, *args, **kwargs)
29 self.ipython0.ask_exit = self.do_exit
29 self.ipython0.ask_exit = self.do_exit
30 # Scroll to top
30 # Scroll to top
31 maxrange = self.GetScrollRange(wx.VERTICAL)
31 maxrange = self.GetScrollRange(wx.VERTICAL)
32 self.ScrollLines(-maxrange)
32 self.ScrollLines(-maxrange)
33
33
34
34
35 def _on_key_down(self, event, skip=True):
35 def _on_key_down(self, event, skip=True):
36 # Intercept Ctrl-D to quit
36 # Intercept Ctrl-D to quit
37 if event.KeyCode == ord('D') and event.ControlDown() and \
37 if event.KeyCode == ord('D') and event.ControlDown() and \
38 self.input_buffer == '' and \
38 self.input_buffer == '' and \
39 self._input_state == 'readline':
39 self._input_state == 'readline':
40 wx.CallAfter(self.ask_exit)
40 wx.CallAfter(self.ask_exit)
41 else:
41 else:
42 WxController._on_key_down(self, event, skip=skip)
42 WxController._on_key_down(self, event, skip=skip)
43
43
44
44
45 def ask_exit(self):
45 def ask_exit(self):
46 """ Ask the user whether to exit.
46 """ Ask the user whether to exit.
47 """
47 """
48 self._input_state = 'subprocess'
48 self._input_state = 'subprocess'
49 self.write('\n', refresh=False)
49 self.write('\n', refresh=False)
50 self.capture_output()
50 self.capture_output()
51 self.ipython0.shell.exit()
51 self.ipython0.shell.exit()
52 self.release_output()
52 self.release_output()
53 if not self.ipython0.exit_now:
53 if not self.ipython0.exit_now:
54 wx.CallAfter(self.new_prompt,
54 wx.CallAfter(self.new_prompt,
55 self.input_prompt_template.substitute(
55 self.input_prompt_template.substitute(
56 number=self.last_result['number'] + 1))
56 number=self.last_result['number'] + 1))
57 else:
57 else:
58 wx.CallAfter(wx.GetApp().Exit)
58 wx.CallAfter(wx.GetApp().Exit)
59 self.write('Exiting ...', refresh=False)
59 self.write('Exiting ...', refresh=False)
60
60
61
61
62 def do_exit(self):
62 def do_exit(self):
63 """ Exits the interpreter, kills the windows.
63 """ Exits the interpreter, kills the windows.
64 """
64 """
65 WxController.do_exit(self)
65 WxController.do_exit(self)
66 self.release_output()
66 self.release_output()
67 wx.CallAfter(wx.Exit)
67 wx.CallAfter(wx.Exit)
68
68
69
69
70
70
71 class IPythonX(wx.Frame):
71 class IPythonX(wx.Frame):
72 """ Main frame of the IPythonX app.
72 """ Main frame of the IPythonX app.
73 """
73 """
74
74
75 def __init__(self, parent, id, title, debug=False):
75 def __init__(self, parent, id, title, debug=False):
76 wx.Frame.__init__(self, parent, id, title, size=(300,250))
76 wx.Frame.__init__(self, parent, id, title, size=(300,250))
77 self._sizer = wx.BoxSizer(wx.VERTICAL)
77 self._sizer = wx.BoxSizer(wx.VERTICAL)
78 self.shell = IPythonXController(self, debug=debug)
78 self.shell = IPythonXController(self, debug=debug)
79 self._sizer.Add(self.shell, 1, wx.EXPAND)
79 self._sizer.Add(self.shell, 1, wx.EXPAND)
80 self.SetSizer(self._sizer)
80 self.SetSizer(self._sizer)
81 self.SetAutoLayout(1)
81 self.SetAutoLayout(1)
82 self.Show(True)
82 self.Show(True)
83 wx.EVT_CLOSE(self, self.on_close)
84
85
86 def on_close(self, event):
87 """ Called on closing the windows.
88
89 Stops the event loop, to close all the child windows.
90 """
91 wx.CallAfter(wx.Exit)
83
92
84
93
85 def main():
94 def main():
86 from optparse import OptionParser
95 from optparse import OptionParser
87 usage = """usage: %prog [options]
96 usage = """usage: %prog [options]
88
97
89 Simple graphical frontend to IPython, using WxWidgets."""
98 Simple graphical frontend to IPython, using WxWidgets."""
90 parser = OptionParser(usage=usage)
99 parser = OptionParser(usage=usage)
91 parser.add_option("-d", "--debug",
100 parser.add_option("-d", "--debug",
92 action="store_true", dest="debug", default=False,
101 action="store_true", dest="debug", default=False,
93 help="Enable debug message for the wx frontend.")
102 help="Enable debug message for the wx frontend.")
94
103
95 options, args = parser.parse_args()
104 options, args = parser.parse_args()
96
105
97 # Clear the options, to avoid having the ipython0 instance complain
106 # Clear the options, to avoid having the ipython0 instance complain
98 import sys
107 import sys
99 sys.argv = sys.argv[:1]
108 sys.argv = sys.argv[:1]
100
109
101 app = wx.PySimpleApp()
110 app = wx.PySimpleApp()
102 frame = IPythonX(None, wx.ID_ANY, 'IPythonX', debug=options.debug)
111 frame = IPythonX(None, wx.ID_ANY, 'IPythonX', debug=options.debug)
103 frame.shell.SetFocus()
112 frame.shell.SetFocus()
104 frame.shell.app = app
113 frame.shell.app = app
105 frame.SetSize((680, 460))
114 frame.SetSize((680, 460))
106
115
107 app.MainLoop()
116 app.MainLoop()
108
117
109 if __name__ == '__main__':
118 if __name__ == '__main__':
110 main()
119 main()
@@ -1,526 +1,601 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.wx.tests.test_wx_frontend -*-
3 # ipython1.frontend.wx.tests.test_wx_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 This class inherits from ConsoleWidget, that provides a console-like
8 This class inherits from ConsoleWidget, that provides a console-like
9 widget to provide a text-rendering widget suitable for a terminal.
9 widget to provide a text-rendering widget suitable for a terminal.
10 """
10 """
11
11
12 __docformat__ = "restructuredtext en"
12 __docformat__ = "restructuredtext en"
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Copyright (C) 2008 The IPython Development Team
15 # Copyright (C) 2008 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24
24
25 # Major library imports
25 # Major library imports
26 import re
26 import re
27 import __builtin__
27 import __builtin__
28 from time import sleep
29 import sys
28 import sys
30 from threading import Lock
29 from threading import Lock
31 import string
32
30
33 import wx
31 import wx
34 from wx import stc
32 from wx import stc
35
33
36 # Ipython-specific imports.
34 # Ipython-specific imports.
37 from IPython.frontend._process import PipedProcess
35 from IPython.frontend.process import PipedProcess
38 from console_widget import ConsoleWidget
36 from console_widget import ConsoleWidget, _COMPLETE_BUFFER_MARKER, \
37 _ERROR_MARKER, _INPUT_MARKER
39 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
38 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
40
39
41 #-------------------------------------------------------------------------------
40 #-------------------------------------------------------------------------------
42 # Constants
43 #-------------------------------------------------------------------------------
44
45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
47 _ERROR_BG = '#FFF1F1' # Nice red
48
49 _COMPLETE_BUFFER_MARKER = 31
50 _ERROR_MARKER = 30
51 _INPUT_MARKER = 29
52
53 prompt_in1 = \
54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
55
56 prompt_out = \
57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
58
59 #-------------------------------------------------------------------------------
60 # Classes to implement the Wx frontend
41 # Classes to implement the Wx frontend
61 #-------------------------------------------------------------------------------
42 #-------------------------------------------------------------------------------
62 class WxController(ConsoleWidget, PrefilterFrontEnd):
43 class WxController(ConsoleWidget, PrefilterFrontEnd):
63 """Classes to provide a Wx frontend to the
44 """Classes to provide a Wx frontend to the
64 IPython.kernel.core.interpreter.
45 IPython.kernel.core.interpreter.
65
46
66 This class inherits from ConsoleWidget, that provides a console-like
47 This class inherits from ConsoleWidget, that provides a console-like
67 widget to provide a text-rendering widget suitable for a terminal.
48 widget to provide a text-rendering widget suitable for a terminal.
68 """
49 """
69
50
70 output_prompt_template = string.Template(prompt_out)
71
72 input_prompt_template = string.Template(prompt_in1)
73
74 # Print debug info on what is happening to the console.
51 # Print debug info on what is happening to the console.
75 debug = False
52 debug = False
76
53
77 # The title of the terminal, as captured through the ANSI escape
54 # The title of the terminal, as captured through the ANSI escape
78 # sequences.
55 # sequences.
79 def _set_title(self, title):
56 def _set_title(self, title):
80 return self.Parent.SetTitle(title)
57 return self.Parent.SetTitle(title)
81
58
82 def _get_title(self):
59 def _get_title(self):
83 return self.Parent.GetTitle()
60 return self.Parent.GetTitle()
84
61
85 title = property(_get_title, _set_title)
62 title = property(_get_title, _set_title)
86
63
87
64
88 # The buffer being edited.
65 # The buffer being edited.
89 # We are duplicating the definition here because of multiple
66 # We are duplicating the definition here because of multiple
90 # inheritence
67 # inheritence
91 def _set_input_buffer(self, string):
68 def _set_input_buffer(self, string):
92 ConsoleWidget._set_input_buffer(self, string)
69 ConsoleWidget._set_input_buffer(self, string)
93 self._colorize_input_buffer()
70 self._colorize_input_buffer()
94
71
95 def _get_input_buffer(self):
72 def _get_input_buffer(self):
96 """ Returns the text in current edit buffer.
73 """ Returns the text in current edit buffer.
97 """
74 """
98 return ConsoleWidget._get_input_buffer(self)
75 return ConsoleWidget._get_input_buffer(self)
99
76
100 input_buffer = property(_get_input_buffer, _set_input_buffer)
77 input_buffer = property(_get_input_buffer, _set_input_buffer)
101
78
102
79
103 #--------------------------------------------------------------------------
80 #--------------------------------------------------------------------------
104 # Private Attributes
81 # Private Attributes
105 #--------------------------------------------------------------------------
82 #--------------------------------------------------------------------------
106
83
107 # A flag governing the behavior of the input. Can be:
84 # A flag governing the behavior of the input. Can be:
108 #
85 #
109 # 'readline' for readline-like behavior with a prompt
86 # 'readline' for readline-like behavior with a prompt
110 # and an edit buffer.
87 # and an edit buffer.
111 # 'raw_input' similar to readline, but triggered by a raw-input
88 # 'raw_input' similar to readline, but triggered by a raw-input
112 # call. Can be used by subclasses to act differently.
89 # call. Can be used by subclasses to act differently.
113 # 'subprocess' for sending the raw input directly to a
90 # 'subprocess' for sending the raw input directly to a
114 # subprocess.
91 # subprocess.
115 # 'buffering' for buffering of the input, that will be used
92 # 'buffering' for buffering of the input, that will be used
116 # when the input state switches back to another state.
93 # when the input state switches back to another state.
117 _input_state = 'readline'
94 _input_state = 'readline'
118
95
119 # Attribute to store reference to the pipes of a subprocess, if we
96 # Attribute to store reference to the pipes of a subprocess, if we
120 # are running any.
97 # are running any.
121 _running_process = False
98 _running_process = False
122
99
123 # A queue for writing fast streams to the screen without flooding the
100 # A queue for writing fast streams to the screen without flooding the
124 # event loop
101 # event loop
125 _out_buffer = []
102 _out_buffer = []
126
103
127 # A lock to lock the _out_buffer to make sure we don't empty it
104 # A lock to lock the _out_buffer to make sure we don't empty it
128 # while it is being swapped
105 # while it is being swapped
129 _out_buffer_lock = Lock()
106 _out_buffer_lock = Lock()
130
107
131 # The different line markers used to higlight the prompts.
108 # The different line markers used to higlight the prompts.
132 _markers = dict()
109 _markers = dict()
133
110
134 #--------------------------------------------------------------------------
111 #--------------------------------------------------------------------------
135 # Public API
112 # Public API
136 #--------------------------------------------------------------------------
113 #--------------------------------------------------------------------------
137
114
138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
115 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
139 size=wx.DefaultSize,
116 size=wx.DefaultSize,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
117 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
118 styledef=None,
141 *args, **kwds):
119 *args, **kwds):
142 """ Create Shell instance.
120 """ Create Shell instance.
121
122 Parameters
123 -----------
124 styledef : dict, optional
125 styledef is the dictionary of options used to define the
126 style.
143 """
127 """
128 if styledef is not None:
129 self.style = styledef
144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
130 ConsoleWidget.__init__(self, parent, id, pos, size, style)
145 PrefilterFrontEnd.__init__(self, **kwds)
131 PrefilterFrontEnd.__init__(self, **kwds)
146
132
147 # Stick in our own raw_input:
133 # Stick in our own raw_input:
148 self.ipython0.raw_input = self.raw_input
134 self.ipython0.raw_input = self.raw_input
149
135
150 # Marker for complete buffer.
151 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
152 background=_COMPLETE_BUFFER_BG)
153 # Marker for current input buffer.
154 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
155 background=_INPUT_BUFFER_BG)
156 # Marker for tracebacks.
157 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
158 background=_ERROR_BG)
159
160 # A time for flushing the write buffer
136 # A time for flushing the write buffer
161 BUFFER_FLUSH_TIMER_ID = 100
137 BUFFER_FLUSH_TIMER_ID = 100
162 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
138 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
163 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
139 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
164
140
165 if 'debug' in kwds:
141 if 'debug' in kwds:
166 self.debug = kwds['debug']
142 self.debug = kwds['debug']
167 kwds.pop('debug')
143 kwds.pop('debug')
168
144
169 # Inject self in namespace, for debug
145 # Inject self in namespace, for debug
170 if self.debug:
146 if self.debug:
171 self.shell.user_ns['self'] = self
147 self.shell.user_ns['self'] = self
172 # Inject our own raw_input in namespace
148 # Inject our own raw_input in namespace
173 self.shell.user_ns['raw_input'] = self.raw_input
149 self.shell.user_ns['raw_input'] = self.raw_input
174
150
175
176 def raw_input(self, prompt=''):
151 def raw_input(self, prompt=''):
177 """ A replacement from python's raw_input.
152 """ A replacement from python's raw_input.
178 """
153 """
179 self.new_prompt(prompt)
154 self.new_prompt(prompt)
180 self._input_state = 'raw_input'
155 self._input_state = 'raw_input'
181 if hasattr(self, '_cursor'):
156 if hasattr(self, '_cursor'):
182 del self._cursor
157 del self._cursor
183 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
158 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
184 self.__old_on_enter = self._on_enter
159 self.__old_on_enter = self._on_enter
185 event_loop = wx.EventLoop()
160 event_loop = wx.EventLoop()
186 def my_on_enter():
161 def my_on_enter():
187 event_loop.Exit()
162 event_loop.Exit()
188 self._on_enter = my_on_enter
163 self._on_enter = my_on_enter
189 # XXX: Running a separate event_loop. Ugly.
164 # XXX: Running a separate event_loop. Ugly.
190 event_loop.Run()
165 event_loop.Run()
191 self._on_enter = self.__old_on_enter
166 self._on_enter = self.__old_on_enter
192 self._input_state = 'buffering'
167 self._input_state = 'buffering'
193 self._cursor = wx.BusyCursor()
168 self._cursor = wx.BusyCursor()
194 return self.input_buffer.rstrip('\n')
169 return self.input_buffer.rstrip('\n')
195
170
196
171
197 def system_call(self, command_string):
172 def system_call(self, command_string):
198 self._input_state = 'subprocess'
173 self._input_state = 'subprocess'
199 event_loop = wx.EventLoop()
174 event_loop = wx.EventLoop()
200 def _end_system_call():
175 def _end_system_call():
201 self._input_state = 'buffering'
176 self._input_state = 'buffering'
202 self._running_process = False
177 self._running_process = False
203 event_loop.Exit()
178 event_loop.Exit()
204
179
205 self._running_process = PipedProcess(command_string,
180 self._running_process = PipedProcess(command_string,
206 out_callback=self.buffered_write,
181 out_callback=self.buffered_write,
207 end_callback = _end_system_call)
182 end_callback = _end_system_call)
208 self._running_process.start()
183 self._running_process.start()
209 # XXX: Running a separate event_loop. Ugly.
184 # XXX: Running a separate event_loop. Ugly.
210 event_loop.Run()
185 event_loop.Run()
211 # Be sure to flush the buffer.
186 # Be sure to flush the buffer.
212 self._buffer_flush(event=None)
187 self._buffer_flush(event=None)
213
188
214
189
215 def do_calltip(self):
190 def do_calltip(self):
216 """ Analyse current and displays useful calltip for it.
191 """ Analyse current and displays useful calltip for it.
217 """
192 """
218 if self.debug:
193 if self.debug:
219 print >>sys.__stdout__, "do_calltip"
194 print >>sys.__stdout__, "do_calltip"
220 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
195 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
221 symbol = self.input_buffer
196 symbol = self.input_buffer
222 symbol_string = separators.split(symbol)[-1]
197 symbol_string = separators.split(symbol)[-1]
223 base_symbol_string = symbol_string.split('.')[0]
198 base_symbol_string = symbol_string.split('.')[0]
224 if base_symbol_string in self.shell.user_ns:
199 if base_symbol_string in self.shell.user_ns:
225 symbol = self.shell.user_ns[base_symbol_string]
200 symbol = self.shell.user_ns[base_symbol_string]
226 elif base_symbol_string in self.shell.user_global_ns:
201 elif base_symbol_string in self.shell.user_global_ns:
227 symbol = self.shell.user_global_ns[base_symbol_string]
202 symbol = self.shell.user_global_ns[base_symbol_string]
228 elif base_symbol_string in __builtin__.__dict__:
203 elif base_symbol_string in __builtin__.__dict__:
229 symbol = __builtin__.__dict__[base_symbol_string]
204 symbol = __builtin__.__dict__[base_symbol_string]
230 else:
205 else:
231 return False
206 return False
232 try:
207 try:
233 for name in symbol_string.split('.')[1:] + ['__doc__']:
208 for name in symbol_string.split('.')[1:] + ['__doc__']:
234 symbol = getattr(symbol, name)
209 symbol = getattr(symbol, name)
235 self.AutoCompCancel()
210 self.AutoCompCancel()
236 # Check that the symbol can indeed be converted to a string:
211 # Check that the symbol can indeed be converted to a string:
237 symbol += ''
212 symbol += ''
238 wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol)
213 wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol)
239 except:
214 except:
240 # The retrieve symbol couldn't be converted to a string
215 # The retrieve symbol couldn't be converted to a string
241 pass
216 pass
242
217
243
218
244 def _popup_completion(self, create=False):
219 def _popup_completion(self, create=False):
245 """ Updates the popup completion menu if it exists. If create is
220 """ Updates the popup completion menu if it exists. If create is
246 true, open the menu.
221 true, open the menu.
247 """
222 """
248 if self.debug:
223 if self.debug:
249 print >>sys.__stdout__, "_popup_completion"
224 print >>sys.__stdout__, "_popup_completion"
250 line = self.input_buffer
225 line = self.input_buffer
251 if (self.AutoCompActive() and line and not line[-1] == '.') \
226 if (self.AutoCompActive() and line and not line[-1] == '.') \
252 or create==True:
227 or create==True:
253 suggestion, completions = self.complete(line)
228 suggestion, completions = self.complete(line)
254 offset=0
255 if completions:
229 if completions:
256 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
230 offset = len(self._get_completion_text(line))
257 residual = complete_sep.split(line)[-1]
258 offset = len(residual)
259 self.pop_completion(completions, offset=offset)
231 self.pop_completion(completions, offset=offset)
260 if self.debug:
232 if self.debug:
261 print >>sys.__stdout__, completions
233 print >>sys.__stdout__, completions
262
234
263
235
264 def buffered_write(self, text):
236 def buffered_write(self, text):
265 """ A write method for streams, that caches the stream in order
237 """ A write method for streams, that caches the stream in order
266 to avoid flooding the event loop.
238 to avoid flooding the event loop.
267
239
268 This can be called outside of the main loop, in separate
240 This can be called outside of the main loop, in separate
269 threads.
241 threads.
270 """
242 """
271 self._out_buffer_lock.acquire()
243 self._out_buffer_lock.acquire()
272 self._out_buffer.append(text)
244 self._out_buffer.append(text)
273 self._out_buffer_lock.release()
245 self._out_buffer_lock.release()
274 if not self._buffer_flush_timer.IsRunning():
246 if not self._buffer_flush_timer.IsRunning():
275 wx.CallAfter(self._buffer_flush_timer.Start,
247 wx.CallAfter(self._buffer_flush_timer.Start,
276 milliseconds=100, oneShot=True)
248 milliseconds=100, oneShot=True)
277
249
278
250
251 def clear_screen(self):
252 """ Empty completely the widget.
253 """
254 self.ClearAll()
255 self.new_prompt(self.input_prompt_template.substitute(
256 number=(self.last_result['number'] + 1)))
257
258
279 #--------------------------------------------------------------------------
259 #--------------------------------------------------------------------------
280 # LineFrontEnd interface
260 # LineFrontEnd interface
281 #--------------------------------------------------------------------------
261 #--------------------------------------------------------------------------
282
262
283 def execute(self, python_string, raw_string=None):
263 def execute(self, python_string, raw_string=None):
284 self._input_state = 'buffering'
264 self._input_state = 'buffering'
285 self.CallTipCancel()
265 self.CallTipCancel()
286 self._cursor = wx.BusyCursor()
266 self._cursor = wx.BusyCursor()
287 if raw_string is None:
267 if raw_string is None:
288 raw_string = python_string
268 raw_string = python_string
289 end_line = self.current_prompt_line \
269 end_line = self.current_prompt_line \
290 + max(1, len(raw_string.split('\n'))-1)
270 + max(1, len(raw_string.split('\n'))-1)
291 for i in range(self.current_prompt_line, end_line):
271 for i in range(self.current_prompt_line, end_line):
292 if i in self._markers:
272 if i in self._markers:
293 self.MarkerDeleteHandle(self._markers[i])
273 self.MarkerDeleteHandle(self._markers[i])
294 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
274 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
295 # Use a callafter to update the display robustly under windows
275 # Use a callafter to update the display robustly under windows
296 def callback():
276 def callback():
297 self.GotoPos(self.GetLength())
277 self.GotoPos(self.GetLength())
298 PrefilterFrontEnd.execute(self, python_string,
278 PrefilterFrontEnd.execute(self, python_string,
299 raw_string=raw_string)
279 raw_string=raw_string)
300 wx.CallAfter(callback)
280 wx.CallAfter(callback)
301
281
282
283 def execute_command(self, command, hidden=False):
284 """ Execute a command, not only in the model, but also in the
285 view.
286 """
287 # XXX: This method needs to be integrated in the base fronted
288 # interface
289 if hidden:
290 return self.shell.execute(command)
291 else:
292 # XXX: we are not storing the input buffer previous to the
293 # execution, as this forces us to run the execution
294 # input_buffer a yield, which is not good.
295 ##current_buffer = self.shell.control.input_buffer
296 command = command.rstrip()
297 if len(command.split('\n')) > 1:
298 # The input command is several lines long, we need to
299 # force the execution to happen
300 command += '\n'
301 cleaned_command = self.prefilter_input(command)
302 self.input_buffer = command
303 # Do not use wx.Yield() (aka GUI.process_events()) to avoid
304 # recursive yields.
305 self.ProcessEvent(wx.PaintEvent())
306 self.write('\n')
307 if not self.is_complete(cleaned_command + '\n'):
308 self._colorize_input_buffer()
309 self.render_error('Incomplete or invalid input')
310 self.new_prompt(self.input_prompt_template.substitute(
311 number=(self.last_result['number'] + 1)))
312 return False
313 self._on_enter()
314 return True
315
316
302 def save_output_hooks(self):
317 def save_output_hooks(self):
303 self.__old_raw_input = __builtin__.raw_input
318 self.__old_raw_input = __builtin__.raw_input
304 PrefilterFrontEnd.save_output_hooks(self)
319 PrefilterFrontEnd.save_output_hooks(self)
305
320
306 def capture_output(self):
321 def capture_output(self):
307 self.SetLexer(stc.STC_LEX_NULL)
322 self.SetLexer(stc.STC_LEX_NULL)
308 PrefilterFrontEnd.capture_output(self)
323 PrefilterFrontEnd.capture_output(self)
309 __builtin__.raw_input = self.raw_input
324 __builtin__.raw_input = self.raw_input
310
325
311
326
312 def release_output(self):
327 def release_output(self):
313 __builtin__.raw_input = self.__old_raw_input
328 __builtin__.raw_input = self.__old_raw_input
314 PrefilterFrontEnd.release_output(self)
329 PrefilterFrontEnd.release_output(self)
315 self.SetLexer(stc.STC_LEX_PYTHON)
330 self.SetLexer(stc.STC_LEX_PYTHON)
316
331
317
332
318 def after_execute(self):
333 def after_execute(self):
319 PrefilterFrontEnd.after_execute(self)
334 PrefilterFrontEnd.after_execute(self)
320 # Clear the wait cursor
335 # Clear the wait cursor
321 if hasattr(self, '_cursor'):
336 if hasattr(self, '_cursor'):
322 del self._cursor
337 del self._cursor
323 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
338 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
324
339
325
340
326 def show_traceback(self):
341 def show_traceback(self):
327 start_line = self.GetCurrentLine()
342 start_line = self.GetCurrentLine()
328 PrefilterFrontEnd.show_traceback(self)
343 PrefilterFrontEnd.show_traceback(self)
329 self.ProcessEvent(wx.PaintEvent())
344 self.ProcessEvent(wx.PaintEvent())
330 #wx.Yield()
345 #wx.Yield()
331 for i in range(start_line, self.GetCurrentLine()):
346 for i in range(start_line, self.GetCurrentLine()):
332 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
347 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
333
348
334
349
335 #--------------------------------------------------------------------------
350 #--------------------------------------------------------------------------
336 # FrontEndBase interface
351 # FrontEndBase interface
337 #--------------------------------------------------------------------------
352 #--------------------------------------------------------------------------
338
353
339 def render_error(self, e):
354 def render_error(self, e):
340 start_line = self.GetCurrentLine()
355 start_line = self.GetCurrentLine()
341 self.write('\n' + e + '\n')
356 self.write('\n' + e + '\n')
342 for i in range(start_line, self.GetCurrentLine()):
357 for i in range(start_line, self.GetCurrentLine()):
343 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
358 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
344
359
345
360
346 #--------------------------------------------------------------------------
361 #--------------------------------------------------------------------------
347 # ConsoleWidget interface
362 # ConsoleWidget interface
348 #--------------------------------------------------------------------------
363 #--------------------------------------------------------------------------
349
364
350 def new_prompt(self, prompt):
365 def new_prompt(self, prompt):
351 """ Display a new prompt, and start a new input buffer.
366 """ Display a new prompt, and start a new input buffer.
352 """
367 """
353 self._input_state = 'readline'
368 self._input_state = 'readline'
354 ConsoleWidget.new_prompt(self, prompt)
369 ConsoleWidget.new_prompt(self, prompt)
355 i = self.current_prompt_line
370 i = self.current_prompt_line
356 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
371 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
357
372
358
373
374 def continuation_prompt(self, *args, **kwargs):
375 # Avoid multiple inheritence, be explicit about which
376 # parent method class gets called
377 return ConsoleWidget.continuation_prompt(self, *args, **kwargs)
378
379
359 def write(self, *args, **kwargs):
380 def write(self, *args, **kwargs):
360 # Avoid multiple inheritence, be explicit about which
381 # Avoid multiple inheritence, be explicit about which
361 # parent method class gets called
382 # parent method class gets called
362 ConsoleWidget.write(self, *args, **kwargs)
383 return ConsoleWidget.write(self, *args, **kwargs)
363
384
364
385
365 def _on_key_down(self, event, skip=True):
386 def _on_key_down(self, event, skip=True):
366 """ Capture the character events, let the parent
387 """ Capture the character events, let the parent
367 widget handle them, and put our logic afterward.
388 widget handle them, and put our logic afterward.
368 """
389 """
369 # FIXME: This method needs to be broken down in smaller ones.
390 # FIXME: This method needs to be broken down in smaller ones.
370 current_line_number = self.GetCurrentLine()
391 current_line_num = self.GetCurrentLine()
371 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
392 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
372 # Capture Control-C
393 # Capture Control-C
373 if self._input_state == 'subprocess':
394 if self._input_state == 'subprocess':
374 if self.debug:
395 if self.debug:
375 print >>sys.__stderr__, 'Killing running process'
396 print >>sys.__stderr__, 'Killing running process'
376 if hasattr(self._running_process, 'process'):
397 if hasattr(self._running_process, 'process'):
377 self._running_process.process.kill()
398 self._running_process.process.kill()
378 elif self._input_state == 'buffering':
399 elif self._input_state == 'buffering':
379 if self.debug:
400 if self.debug:
380 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
401 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
381 raise KeyboardInterrupt
402 raise KeyboardInterrupt
382 # XXX: We need to make really sure we
403 # XXX: We need to make really sure we
383 # get back to a prompt.
404 # get back to a prompt.
384 elif self._input_state == 'subprocess' and (
405 elif self._input_state == 'subprocess' and (
385 ( event.KeyCode<256 and
406 ( event.KeyCode<256 and
386 not event.ControlDown() )
407 not event.ControlDown() )
387 or
408 or
388 ( event.KeyCode in (ord('d'), ord('D')) and
409 ( event.KeyCode in (ord('d'), ord('D')) and
389 event.ControlDown())):
410 event.ControlDown())):
390 # We are running a process, we redirect keys.
411 # We are running a process, we redirect keys.
391 ConsoleWidget._on_key_down(self, event, skip=skip)
412 ConsoleWidget._on_key_down(self, event, skip=skip)
392 char = chr(event.KeyCode)
413 char = chr(event.KeyCode)
393 # Deal with some inconsistency in wx keycodes:
414 # Deal with some inconsistency in wx keycodes:
394 if char == '\r':
415 if char == '\r':
395 char = '\n'
416 char = '\n'
396 elif not event.ShiftDown():
417 elif not event.ShiftDown():
397 char = char.lower()
418 char = char.lower()
398 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
419 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
399 char = '\04'
420 char = '\04'
400 self._running_process.process.stdin.write(char)
421 self._running_process.process.stdin.write(char)
401 self._running_process.process.stdin.flush()
422 self._running_process.process.stdin.flush()
402 elif event.KeyCode in (ord('('), 57, 53):
423 elif event.KeyCode in (ord('('), 57, 53):
403 # Calltips
424 # Calltips
404 event.Skip()
425 event.Skip()
405 self.do_calltip()
426 self.do_calltip()
406 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
427 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
407 event.Skip()
428 event.Skip()
408 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
429 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
409 wx.CallAfter(self._popup_completion, create=True)
430 wx.CallAfter(self._popup_completion, create=True)
410 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
431 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
411 wx.WXK_RIGHT, wx.WXK_ESCAPE):
432 wx.WXK_RIGHT, wx.WXK_ESCAPE):
412 wx.CallAfter(self._popup_completion)
433 wx.CallAfter(self._popup_completion)
413 else:
434 else:
414 # Up history
435 # Up history
415 if event.KeyCode == wx.WXK_UP and (
436 if event.KeyCode == wx.WXK_UP and (
416 ( current_line_number == self.current_prompt_line and
437 ( current_line_num == self.current_prompt_line and
417 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
438 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
418 or event.ControlDown() ):
439 or event.ControlDown() ):
419 new_buffer = self.get_history_previous(
440 new_buffer = self.get_history_previous(
420 self.input_buffer)
441 self.input_buffer)
421 if new_buffer is not None:
442 if new_buffer is not None:
422 self.input_buffer = new_buffer
443 self.input_buffer = new_buffer
423 if self.GetCurrentLine() > self.current_prompt_line:
444 if self.GetCurrentLine() > self.current_prompt_line:
424 # Go to first line, for seemless history up.
445 # Go to first line, for seemless history up.
425 self.GotoPos(self.current_prompt_pos)
446 self.GotoPos(self.current_prompt_pos)
426 # Down history
447 # Down history
427 elif event.KeyCode == wx.WXK_DOWN and (
448 elif event.KeyCode == wx.WXK_DOWN and (
428 ( current_line_number == self.LineCount -1 and
449 ( current_line_num == self.LineCount -1 and
429 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
450 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
430 or event.ControlDown() ):
451 or event.ControlDown() ):
431 new_buffer = self.get_history_next()
452 new_buffer = self.get_history_next()
432 if new_buffer is not None:
453 if new_buffer is not None:
433 self.input_buffer = new_buffer
454 self.input_buffer = new_buffer
434 # Tab-completion
455 # Tab-completion
435 elif event.KeyCode == ord('\t'):
456 elif event.KeyCode == ord('\t'):
436 current_line, current_line_number = self.CurLine
457 current_line, current_line_num = self.CurLine
437 if not re.match(r'^\s*$', current_line):
458 if not re.match(r'^\s*$', current_line):
438 self.complete_current_input()
459 self.complete_current_input()
439 if self.AutoCompActive():
460 if self.AutoCompActive():
440 wx.CallAfter(self._popup_completion, create=True)
461 wx.CallAfter(self._popup_completion, create=True)
441 else:
462 else:
442 event.Skip()
463 event.Skip()
464 elif event.KeyCode == wx.WXK_BACK:
465 # If characters where erased, check if we have to
466 # remove a line.
467 # XXX: What about DEL?
468 # FIXME: This logics should be in ConsoleWidget, as it is
469 # independant of IPython
470 current_line, _ = self.CurLine
471 current_pos = self.GetCurrentPos()
472 current_line_num = self.LineFromPosition(current_pos)
473 current_col = self.GetColumn(current_pos)
474 len_prompt = len(self.continuation_prompt())
475 if ( current_line.startswith(self.continuation_prompt())
476 and current_col == len_prompt):
477 new_lines = []
478 for line_num, line in enumerate(
479 self.input_buffer.split('\n')):
480 if (line_num + self.current_prompt_line ==
481 current_line_num):
482 new_lines.append(line[len_prompt:])
483 else:
484 new_lines.append('\n'+line)
485 # The first character is '\n', due to the above
486 # code:
487 self.input_buffer = ''.join(new_lines)[1:]
488 self.GotoPos(current_pos - 1 - len_prompt)
489 else:
490 ConsoleWidget._on_key_down(self, event, skip=skip)
443 else:
491 else:
444 ConsoleWidget._on_key_down(self, event, skip=skip)
492 ConsoleWidget._on_key_down(self, event, skip=skip)
493
445
494
446
495
447 def _on_key_up(self, event, skip=True):
496 def _on_key_up(self, event, skip=True):
448 """ Called when any key is released.
497 """ Called when any key is released.
449 """
498 """
450 if event.KeyCode in (59, ord('.')):
499 if event.KeyCode in (59, ord('.')):
451 # Intercepting '.'
500 # Intercepting '.'
452 event.Skip()
501 event.Skip()
453 wx.CallAfter(self._popup_completion, create=True)
502 wx.CallAfter(self._popup_completion, create=True)
454 else:
503 else:
455 ConsoleWidget._on_key_up(self, event, skip=skip)
504 ConsoleWidget._on_key_up(self, event, skip=skip)
505 # Make sure the continuation_prompts are always followed by a
506 # whitespace
507 new_lines = []
508 if self._input_state == 'readline':
509 position = self.GetCurrentPos()
510 continuation_prompt = self.continuation_prompt()[:-1]
511 for line in self.input_buffer.split('\n'):
512 if not line == continuation_prompt:
513 new_lines.append(line)
514 self.input_buffer = '\n'.join(new_lines)
515 self.GotoPos(position)
456
516
457
517
458 def _on_enter(self):
518 def _on_enter(self):
459 """ Called on return key down, in readline input_state.
519 """ Called on return key down, in readline input_state.
460 """
520 """
521 last_line_num = self.LineFromPosition(self.GetLength())
522 current_line_num = self.LineFromPosition(self.GetCurrentPos())
523 new_line_pos = (last_line_num - current_line_num)
461 if self.debug:
524 if self.debug:
462 print >>sys.__stdout__, repr(self.input_buffer)
525 print >>sys.__stdout__, repr(self.input_buffer)
463 PrefilterFrontEnd._on_enter(self)
526 self.write('\n', refresh=False)
527 # Under windows scintilla seems to be doing funny
528 # stuff to the line returns here, but the getter for
529 # input_buffer filters this out.
530 if sys.platform == 'win32':
531 self.input_buffer = self.input_buffer
532 old_prompt_num = self.current_prompt_pos
533 has_executed = PrefilterFrontEnd._on_enter(self,
534 new_line_pos=new_line_pos)
535 if old_prompt_num == self.current_prompt_pos:
536 # No execution has happened
537 self.GotoPos(self.GetLineEndPosition(current_line_num + 1))
538 return has_executed
464
539
465
540
466 #--------------------------------------------------------------------------
541 #--------------------------------------------------------------------------
467 # EditWindow API
542 # EditWindow API
468 #--------------------------------------------------------------------------
543 #--------------------------------------------------------------------------
469
544
470 def OnUpdateUI(self, event):
545 def OnUpdateUI(self, event):
471 """ Override the OnUpdateUI of the EditWindow class, to prevent
546 """ Override the OnUpdateUI of the EditWindow class, to prevent
472 syntax highlighting both for faster redraw, and for more
547 syntax highlighting both for faster redraw, and for more
473 consistent look and feel.
548 consistent look and feel.
474 """
549 """
475 if not self._input_state == 'readline':
550 if not self._input_state == 'readline':
476 ConsoleWidget.OnUpdateUI(self, event)
551 ConsoleWidget.OnUpdateUI(self, event)
477
552
478 #--------------------------------------------------------------------------
553 #--------------------------------------------------------------------------
479 # Private API
554 # Private API
480 #--------------------------------------------------------------------------
555 #--------------------------------------------------------------------------
481
556
482 def _buffer_flush(self, event):
557 def _buffer_flush(self, event):
483 """ Called by the timer to flush the write buffer.
558 """ Called by the timer to flush the write buffer.
484
559
485 This is always called in the mainloop, by the wx timer.
560 This is always called in the mainloop, by the wx timer.
486 """
561 """
487 self._out_buffer_lock.acquire()
562 self._out_buffer_lock.acquire()
488 _out_buffer = self._out_buffer
563 _out_buffer = self._out_buffer
489 self._out_buffer = []
564 self._out_buffer = []
490 self._out_buffer_lock.release()
565 self._out_buffer_lock.release()
491 self.write(''.join(_out_buffer), refresh=False)
566 self.write(''.join(_out_buffer), refresh=False)
492
567
493
568
494 def _colorize_input_buffer(self):
569 def _colorize_input_buffer(self):
495 """ Keep the input buffer lines at a bright color.
570 """ Keep the input buffer lines at a bright color.
496 """
571 """
497 if not self._input_state in ('readline', 'raw_input'):
572 if not self._input_state in ('readline', 'raw_input'):
498 return
573 return
499 end_line = self.GetCurrentLine()
574 end_line = self.GetCurrentLine()
500 if not sys.platform == 'win32':
575 if not sys.platform == 'win32':
501 end_line += 1
576 end_line += 1
502 for i in range(self.current_prompt_line, end_line):
577 for i in range(self.current_prompt_line, end_line):
503 if i in self._markers:
578 if i in self._markers:
504 self.MarkerDeleteHandle(self._markers[i])
579 self.MarkerDeleteHandle(self._markers[i])
505 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
580 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
506
581
507
582
508 if __name__ == '__main__':
583 if __name__ == '__main__':
509 class MainWindow(wx.Frame):
584 class MainWindow(wx.Frame):
510 def __init__(self, parent, id, title):
585 def __init__(self, parent, id, title):
511 wx.Frame.__init__(self, parent, id, title, size=(300,250))
586 wx.Frame.__init__(self, parent, id, title, size=(300,250))
512 self._sizer = wx.BoxSizer(wx.VERTICAL)
587 self._sizer = wx.BoxSizer(wx.VERTICAL)
513 self.shell = WxController(self)
588 self.shell = WxController(self)
514 self._sizer.Add(self.shell, 1, wx.EXPAND)
589 self._sizer.Add(self.shell, 1, wx.EXPAND)
515 self.SetSizer(self._sizer)
590 self.SetSizer(self._sizer)
516 self.SetAutoLayout(1)
591 self.SetAutoLayout(1)
517 self.Show(True)
592 self.Show(True)
518
593
519 app = wx.PySimpleApp()
594 app = wx.PySimpleApp()
520 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
595 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
521 frame.shell.SetFocus()
596 frame.shell.SetFocus()
522 frame.SetSize((680, 460))
597 frame.SetSize((680, 460))
523 self = frame.shell
598 self = frame.shell
524
599
525 app.MainLoop()
600 app.MainLoop()
526
601
@@ -1,758 +1,761 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 types import FunctionType
23 from types import FunctionType
24
24
25 import __builtin__
25 import __builtin__
26 import codeop
26 import codeop
27 import compiler
27 import compiler
28 import sys
28 import sys
29 import traceback
29 import traceback
30
30
31 # Local imports.
31 # Local imports.
32 from IPython.kernel.core import ultraTB
32 from IPython.kernel.core import ultraTB
33 from IPython.kernel.core.display_trap import DisplayTrap
33 from IPython.kernel.core.display_trap import DisplayTrap
34 from IPython.kernel.core.macro import Macro
34 from IPython.kernel.core.macro import Macro
35 from IPython.kernel.core.prompts import CachedOutput
35 from IPython.kernel.core.prompts import CachedOutput
36 from IPython.kernel.core.traceback_trap import TracebackTrap
36 from IPython.kernel.core.traceback_trap import TracebackTrap
37 from IPython.kernel.core.util import Bunch, system_shell
37 from IPython.kernel.core.util import Bunch, system_shell
38 from IPython.external.Itpl import ItplNS
38 from IPython.external.Itpl import ItplNS
39
39
40 # Global constants
40 # Global constants
41 COMPILER_ERROR = 'error'
41 COMPILER_ERROR = 'error'
42 INCOMPLETE_INPUT = 'incomplete'
42 INCOMPLETE_INPUT = 'incomplete'
43 COMPLETE_INPUT = 'complete'
43 COMPLETE_INPUT = 'complete'
44
44
45 ##############################################################################
45 ##############################################################################
46 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
46 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
47 # not
47 # not
48
48
49 rc = Bunch()
49 rc = Bunch()
50 rc.cache_size = 100
50 rc.cache_size = 100
51 rc.pprint = True
51 rc.pprint = True
52 rc.separate_in = '\n'
52 rc.separate_in = '\n'
53 rc.separate_out = '\n'
53 rc.separate_out = '\n'
54 rc.separate_out2 = ''
54 rc.separate_out2 = ''
55 rc.prompt_in1 = r'In [\#]: '
55 rc.prompt_in1 = r'In [\#]: '
56 rc.prompt_in2 = r' .\\D.: '
56 rc.prompt_in2 = r' .\\D.: '
57 rc.prompt_out = ''
57 rc.prompt_out = ''
58 rc.prompts_pad_left = False
58 rc.prompts_pad_left = False
59
59
60 ##############################################################################
60 ##############################################################################
61
61
62 # Top-level utilities
62 # Top-level utilities
63 def default_display_formatters():
63 def default_display_formatters():
64 """ Return a list of default display formatters.
64 """ Return a list of default display formatters.
65 """
65 """
66
66
67 from display_formatter import PPrintDisplayFormatter, ReprDisplayFormatter
67 from display_formatter import PPrintDisplayFormatter, ReprDisplayFormatter
68 return [PPrintDisplayFormatter(), ReprDisplayFormatter()]
68 return [PPrintDisplayFormatter(), ReprDisplayFormatter()]
69
69
70 def default_traceback_formatters():
70 def default_traceback_formatters():
71 """ Return a list of default traceback formatters.
71 """ Return a list of default traceback formatters.
72 """
72 """
73
73
74 from traceback_formatter import PlainTracebackFormatter
74 from traceback_formatter import PlainTracebackFormatter
75 return [PlainTracebackFormatter()]
75 return [PlainTracebackFormatter()]
76
76
77 # Top-level classes
77 # Top-level classes
78 class NotDefined(object): pass
78 class NotDefined(object): pass
79
79
80 class Interpreter(object):
80 class Interpreter(object):
81 """ An interpreter object.
81 """ An interpreter object.
82
82
83 fixme: needs to negotiate available formatters with frontends.
83 fixme: needs to negotiate available formatters with frontends.
84
84
85 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
86 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
87 replaced by a network adapter.
87 replaced by a network adapter.
88 """
88 """
89
89
90 def __init__(self, user_ns=None, global_ns=None,translator=None,
90 def __init__(self, user_ns=None, global_ns=None,translator=None,
91 magic=None, display_formatters=None,
91 magic=None, display_formatters=None,
92 traceback_formatters=None, output_trap=None, history=None,
92 traceback_formatters=None, output_trap=None, history=None,
93 message_cache=None, filename='<string>', config=None):
93 message_cache=None, filename='<string>', config=None):
94
94
95 # The local/global namespaces for code execution
95 # The local/global namespaces for code execution
96 local_ns = user_ns # compatibility name
96 local_ns = user_ns # compatibility name
97 if local_ns is None:
97 if local_ns is None:
98 local_ns = {}
98 local_ns = {}
99 self.user_ns = local_ns
99 self.user_ns = local_ns
100 # The local namespace
100 # The local namespace
101 if global_ns is None:
101 if global_ns is None:
102 global_ns = {}
102 global_ns = {}
103 self.user_global_ns = global_ns
103 self.user_global_ns = global_ns
104
104
105 # An object that will translate commands into executable Python.
105 # An object that will translate commands into executable Python.
106 # 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
107 # without!
107 # without!
108 # if translator is None:
108 # if translator is None:
109 # from IPython.kernel.core.translator import IPythonTranslator
109 # from IPython.kernel.core.translator import IPythonTranslator
110 # translator = IPythonTranslator()
110 # translator = IPythonTranslator()
111 self.translator = translator
111 self.translator = translator
112
112
113 # An object that maintains magic commands.
113 # An object that maintains magic commands.
114 if magic is None:
114 if magic is None:
115 from IPython.kernel.core.magic import Magic
115 from IPython.kernel.core.magic import Magic
116 magic = Magic(self)
116 magic = Magic(self)
117 self.magic = magic
117 self.magic = magic
118
118
119 # A list of formatters for the displayhook.
119 # A list of formatters for the displayhook.
120 if display_formatters is None:
120 if display_formatters is None:
121 display_formatters = default_display_formatters()
121 display_formatters = default_display_formatters()
122 self.display_formatters = display_formatters
122 self.display_formatters = display_formatters
123
123
124 # A list of formatters for tracebacks.
124 # A list of formatters for tracebacks.
125 if traceback_formatters is None:
125 if traceback_formatters is None:
126 traceback_formatters = default_traceback_formatters()
126 traceback_formatters = default_traceback_formatters()
127 self.traceback_formatters = traceback_formatters
127 self.traceback_formatters = traceback_formatters
128
128
129 # The object trapping stdout/stderr.
129 # The object trapping stdout/stderr.
130 if output_trap is None:
130 if output_trap is None:
131 from IPython.kernel.core.output_trap import OutputTrap
131 from IPython.kernel.core.output_trap import OutputTrap
132 output_trap = OutputTrap()
132 output_trap = OutputTrap()
133 self.output_trap = output_trap
133 self.output_trap = output_trap
134
134
135 # An object that manages the history.
135 # An object that manages the history.
136 if history is None:
136 if history is None:
137 from IPython.kernel.core.history import InterpreterHistory
137 from IPython.kernel.core.history import InterpreterHistory
138 history = InterpreterHistory()
138 history = InterpreterHistory()
139 self.history = history
139 self.history = history
140 self.get_history_item = history.get_history_item
140 self.get_history_item = history.get_history_item
141 self.get_history_input_cache = history.get_input_cache
141 self.get_history_input_cache = history.get_input_cache
142 self.get_history_input_after = history.get_input_after
142 self.get_history_input_after = history.get_input_after
143
143
144 # An object that caches all of the return messages.
144 # An object that caches all of the return messages.
145 if message_cache is None:
145 if message_cache is None:
146 from IPython.kernel.core.message_cache import SimpleMessageCache
146 from IPython.kernel.core.message_cache import SimpleMessageCache
147 message_cache = SimpleMessageCache()
147 message_cache = SimpleMessageCache()
148 self.message_cache = message_cache
148 self.message_cache = message_cache
149
149
150 # The "filename" of the code that is executed in this interpreter.
150 # The "filename" of the code that is executed in this interpreter.
151 self.filename = filename
151 self.filename = filename
152
152
153 # An object that contains much configuration information.
153 # An object that contains much configuration information.
154 if config is None:
154 if config is None:
155 # fixme: Move this constant elsewhere!
155 # fixme: Move this constant elsewhere!
156 config = Bunch(ESC_MAGIC='%')
156 config = Bunch(ESC_MAGIC='%')
157 self.config = config
157 self.config = config
158
158
159 # Hook managers.
159 # Hook managers.
160 # fixme: make the display callbacks configurable. In the meantime,
160 # fixme: make the display callbacks configurable. In the meantime,
161 # enable macros.
161 # enable macros.
162 self.display_trap = DisplayTrap(
162 self.display_trap = DisplayTrap(
163 formatters=self.display_formatters,
163 formatters=self.display_formatters,
164 callbacks=[self._possible_macro],
164 callbacks=[self._possible_macro],
165 )
165 )
166 self.traceback_trap = TracebackTrap(
166 self.traceback_trap = TracebackTrap(
167 formatters=self.traceback_formatters)
167 formatters=self.traceback_formatters)
168
168
169 # This is used temporarily for reformating exceptions in certain
169 # This is used temporarily for reformating exceptions in certain
170 # cases. It will go away once the ultraTB stuff is ported
170 # cases. It will go away once the ultraTB stuff is ported
171 # to ipython1
171 # to ipython1
172 self.tbHandler = ultraTB.FormattedTB(color_scheme='NoColor',
172 self.tbHandler = ultraTB.FormattedTB(color_scheme='NoColor',
173 mode='Context',
173 mode='Context',
174 tb_offset=2)
174 tb_offset=2)
175
175
176 # An object that can compile commands and remember __future__
176 # An object that can compile commands and remember __future__
177 # statements.
177 # statements.
178 self.command_compiler = codeop.CommandCompiler()
178 self.command_compiler = codeop.CommandCompiler()
179
179
180 # A replacement for the raw_input() and input() builtins. Change these
180 # A replacement for the raw_input() and input() builtins. Change these
181 # attributes later to configure them.
181 # attributes later to configure them.
182 self.raw_input_builtin = raw_input
182 self.raw_input_builtin = raw_input
183 self.input_builtin = input
183 self.input_builtin = input
184
184
185 # The number of the current cell.
185 # The number of the current cell.
186 self.current_cell_number = 1
186 self.current_cell_number = 1
187
187
188 # Initialize cache, set in/out prompts and printing system
188 # Initialize cache, set in/out prompts and printing system
189 self.outputcache = CachedOutput(self,
189 self.outputcache = CachedOutput(self,
190 rc.cache_size,
190 rc.cache_size,
191 rc.pprint,
191 rc.pprint,
192 input_sep = rc.separate_in,
192 input_sep = rc.separate_in,
193 output_sep = rc.separate_out,
193 output_sep = rc.separate_out,
194 output_sep2 = rc.separate_out2,
194 output_sep2 = rc.separate_out2,
195 ps1 = rc.prompt_in1,
195 ps1 = rc.prompt_in1,
196 ps2 = rc.prompt_in2,
196 ps2 = rc.prompt_in2,
197 ps_out = rc.prompt_out,
197 ps_out = rc.prompt_out,
198 pad_left = rc.prompts_pad_left)
198 pad_left = rc.prompts_pad_left)
199
199
200 # 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
201 # 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
202 sys.ps1 = self.outputcache.prompt1.p_str
202 sys.ps1 = self.outputcache.prompt1.p_str
203 sys.ps2 = self.outputcache.prompt2.p_str
203 sys.ps2 = self.outputcache.prompt2.p_str
204
204
205 # This is the message dictionary assigned temporarily when running the
205 # This is the message dictionary assigned temporarily when running the
206 # code.
206 # code.
207 self.message = None
207 self.message = None
208
208
209 self.setup_namespace()
209 self.setup_namespace()
210
210
211
211
212 #### Public 'Interpreter' interface ########################################
212 #### Public 'Interpreter' interface ########################################
213
213
214 def formatTraceback(self, et, ev, tb, message=''):
214 def formatTraceback(self, et, ev, tb, message=''):
215 """Put a formatted version of the traceback into value and reraise.
215 """Put a formatted version of the traceback into value and reraise.
216
216
217 When exceptions have to be sent over the network, the traceback
217 When exceptions have to be sent over the network, the traceback
218 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
219 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
220 exception and puts a string representation of the tb into the
220 exception and puts a string representation of the tb into the
221 value of the exception and reraises it.
221 value of the exception and reraises it.
222
222
223 Currently this method uses the ultraTb formatter from IPython trunk.
223 Currently this method uses the ultraTb formatter from IPython trunk.
224 Eventually it should simply use the traceback formatters in core
224 Eventually it should simply use the traceback formatters in core
225 that are loaded into self.tracback_trap.formatters.
225 that are loaded into self.tracback_trap.formatters.
226 """
226 """
227 tbinfo = self.tbHandler.text(et,ev,tb)
227 tbinfo = self.tbHandler.text(et,ev,tb)
228 ev._ipython_traceback_text = tbinfo
228 ev._ipython_traceback_text = tbinfo
229 return et, ev, tb
229 return et, ev, tb
230
230
231 def execute(self, commands, raiseException=True):
231 def execute(self, commands, raiseException=True):
232 """ Execute some IPython commands.
232 """ Execute some IPython commands.
233
233
234 1. Translate them into Python.
234 1. Translate them into Python.
235 2. Run them.
235 2. Run them.
236 3. Trap stdout/stderr.
236 3. Trap stdout/stderr.
237 4. Trap sys.displayhook().
237 4. Trap sys.displayhook().
238 5. Trap exceptions.
238 5. Trap exceptions.
239 6. Return a message object.
239 6. Return a message object.
240
240
241 Parameters
241 Parameters
242 ----------
242 ----------
243 commands : str
243 commands : str
244 The raw commands that the user typed into the prompt.
244 The raw commands that the user typed into the prompt.
245
245
246 Returns
246 Returns
247 -------
247 -------
248 message : dict
248 message : dict
249 The dictionary of responses. See the README.txt in this directory
249 The dictionary of responses. See the README.txt in this directory
250 for an explanation of the format.
250 for an explanation of the format.
251 """
251 """
252
252
253 # 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
254 # returning to the frontend and other listeners.
254 # returning to the frontend and other listeners.
255 message = self.setup_message()
255 message = self.setup_message()
256
256
257 # Massage the input and store the raw and translated commands into
257 # Massage the input and store the raw and translated commands into
258 # a dict.
258 # a dict.
259 user_input = dict(raw=commands)
259 user_input = dict(raw=commands)
260 if self.translator is not None:
260 if self.translator is not None:
261 python = self.translator(commands, message)
261 python = self.translator(commands, message)
262 if python is None:
262 if python is None:
263 # Something went wrong with the translation. The translator
263 # Something went wrong with the translation. The translator
264 # should have added an appropriate entry to the message object.
264 # should have added an appropriate entry to the message object.
265 return message
265 return message
266 else:
266 else:
267 python = commands
267 python = commands
268 user_input['translated'] = python
268 user_input['translated'] = python
269 message['input'] = user_input
269 message['input'] = user_input
270
270
271 # 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
272 # access.
272 # access.
273 self.message = message
273 self.message = message
274
274
275 # Set all of the output/exception traps.
275 # Set all of the output/exception traps.
276 self.set_traps()
276 self.set_traps()
277
277
278 # Actually execute the Python code.
278 # Actually execute the Python code.
279 status = self.execute_python(python)
279 status = self.execute_python(python)
280
280
281 # Unset all of the traps.
281 # Unset all of the traps.
282 self.unset_traps()
282 self.unset_traps()
283
283
284 # Unset the message object.
284 # Unset the message object.
285 self.message = None
285 self.message = None
286
286
287 # Update the history variables in the namespace.
287 # Update the history variables in the namespace.
288 # E.g. In, Out, _, __, ___
288 # E.g. In, Out, _, __, ___
289 if self.history is not None:
289 if self.history is not None:
290 self.history.update_history(self, python)
290 self.history.update_history(self, python)
291
291
292 # 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
293 # stored information.
293 # stored information.
294 self.output_trap.add_to_message(message)
294 self.output_trap.add_to_message(message)
295 self.output_trap.clear()
295 self.output_trap.clear()
296 self.display_trap.add_to_message(message)
296 self.display_trap.add_to_message(message)
297 self.display_trap.clear()
297 self.display_trap.clear()
298 self.traceback_trap.add_to_message(message)
298 self.traceback_trap.add_to_message(message)
299 # Pull out the type, value and tb of the current exception
299 # Pull out the type, value and tb of the current exception
300 # before clearing it.
300 # before clearing it.
301 einfo = self.traceback_trap.args
301 einfo = self.traceback_trap.args
302 self.traceback_trap.clear()
302 self.traceback_trap.clear()
303
303
304 # Cache the message.
304 # Cache the message.
305 self.message_cache.add_message(self.current_cell_number, message)
305 self.message_cache.add_message(self.current_cell_number, message)
306
306
307 # Bump the number.
307 # Bump the number.
308 self.current_cell_number += 1
308 self.current_cell_number += 1
309
309
310 # This conditional lets the execute method either raise any
310 # This conditional lets the execute method either raise any
311 # exception that has occured in user code OR return the message
311 # exception that has occured in user code OR return the message
312 # dict containing the traceback and other useful info.
312 # dict containing the traceback and other useful info.
313 if raiseException and einfo:
313 if raiseException and einfo:
314 raise einfo[0],einfo[1],einfo[2]
314 raise einfo[0],einfo[1],einfo[2]
315 else:
315 else:
316 return message
316 return message
317
317
318 def generate_prompt(self, is_continuation):
318 def generate_prompt(self, is_continuation):
319 """Calculate and return a string with the prompt to display.
319 """Calculate and return a string with the prompt to display.
320
320
321 :Parameters:
321 :Parameters:
322 is_continuation : bool
322 is_continuation : bool
323 Whether the input line is continuing multiline input or not, so
323 Whether the input line is continuing multiline input or not, so
324 that a proper continuation prompt can be computed."""
324 that a proper continuation prompt can be computed."""
325
325
326 if is_continuation:
326 if is_continuation:
327 return str(self.outputcache.prompt2)
327 return str(self.outputcache.prompt2)
328 else:
328 else:
329 return str(self.outputcache.prompt1)
329 return str(self.outputcache.prompt1)
330
330
331 def execute_python(self, python):
331 def execute_python(self, python):
332 """ Actually run the Python code in the namespace.
332 """ Actually run the Python code in the namespace.
333
333
334 :Parameters:
334 :Parameters:
335
335
336 python : str
336 python : str
337 Pure, exec'able Python code. Special IPython commands should have
337 Pure, exec'able Python code. Special IPython commands should have
338 already been translated into pure Python.
338 already been translated into pure Python.
339 """
339 """
340
340
341 # 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
342 # track of __future__ imports.
342 # track of __future__ imports.
343 try:
343 try:
344 commands = self.split_commands(python)
344 commands = self.split_commands(python)
345 except (SyntaxError, IndentationError), e:
345 except (SyntaxError, IndentationError), e:
346 # Save the exc_info so compilation related exceptions can be
346 # Save the exc_info so compilation related exceptions can be
347 # reraised
347 # reraised
348 self.traceback_trap.args = sys.exc_info()
348 self.traceback_trap.args = sys.exc_info()
349 self.pack_exception(self.message,e)
349 self.pack_exception(self.message,e)
350 return None
350 return None
351
351
352 for cmd in commands:
352 for cmd in commands:
353 try:
353 try:
354 code = self.command_compiler(cmd, self.filename, 'single')
354 code = self.command_compiler(cmd, self.filename, 'single')
355 except (SyntaxError, OverflowError, ValueError), e:
355 except (SyntaxError, OverflowError, ValueError), e:
356 self.traceback_trap.args = sys.exc_info()
356 self.traceback_trap.args = sys.exc_info()
357 self.pack_exception(self.message,e)
357 self.pack_exception(self.message,e)
358 # No point in continuing if one block raised
358 # No point in continuing if one block raised
359 return None
359 return None
360 else:
360 else:
361 self.execute_block(code)
361 self.execute_block(code)
362
362
363 def execute_block(self,code):
363 def execute_block(self,code):
364 """Execute a single block of code in the user namespace.
364 """Execute a single block of code in the user namespace.
365
365
366 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
367 successfully:
367 successfully:
368
368
369 - 0: successful execution.
369 - 0: successful execution.
370 - 1: an error occurred.
370 - 1: an error occurred.
371 """
371 """
372
372
373 outflag = 1 # start by assuming error, success will reset it
373 outflag = 1 # start by assuming error, success will reset it
374 try:
374 try:
375 exec code in self.user_ns
375 exec code in self.user_ns
376 outflag = 0
376 outflag = 0
377 except SystemExit:
377 except SystemExit:
378 self.resetbuffer()
378 self.resetbuffer()
379 self.traceback_trap.args = sys.exc_info()
379 self.traceback_trap.args = sys.exc_info()
380 except:
380 except:
381 self.traceback_trap.args = sys.exc_info()
381 self.traceback_trap.args = sys.exc_info()
382
382
383 return outflag
383 return outflag
384
384
385 def execute_macro(self, macro):
385 def execute_macro(self, macro):
386 """ Execute the value of a macro.
386 """ Execute the value of a macro.
387
387
388 Parameters
388 Parameters
389 ----------
389 ----------
390 macro : Macro
390 macro : Macro
391 """
391 """
392
392
393 python = macro.value
393 python = macro.value
394 if self.translator is not None:
394 if self.translator is not None:
395 python = self.translator(python)
395 python = self.translator(python)
396 self.execute_python(python)
396 self.execute_python(python)
397
397
398 def getCommand(self, i=None):
398 def getCommand(self, i=None):
399 """Gets the ith message in the message_cache.
399 """Gets the ith message in the message_cache.
400
400
401 This is implemented here for compatibility with the old ipython1 shell
401 This is implemented here for compatibility with the old ipython1 shell
402 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
403 were going to get rid of it.
403 were going to get rid of it.
404 """
404 """
405 return self.message_cache.get_message(i)
405 return self.message_cache.get_message(i)
406
406
407 def reset(self):
407 def reset(self):
408 """Reset the interpreter.
408 """Reset the interpreter.
409
409
410 Currently this only resets the users variables in the namespace.
410 Currently this only resets the users variables in the namespace.
411 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
412 things like that the Interpreter has, like In, Out, etc.
412 things like that the Interpreter has, like In, Out, etc.
413 """
413 """
414 self.user_ns.clear()
414 self.user_ns.clear()
415 self.setup_namespace()
415 self.setup_namespace()
416
416
417 def complete(self,line,text=None, pos=None):
417 def complete(self,line,text=None, pos=None):
418 """Complete the given text.
418 """Complete the given text.
419
419
420 :Parameters:
420 :Parameters:
421
421
422 text : str
422 text : str
423 Text fragment to be completed on. Typically this is
423 Text fragment to be completed on. Typically this is
424 """
424 """
425 # fixme: implement
425 # fixme: implement
426 raise NotImplementedError
426 raise NotImplementedError
427
427
428 def push(self, ns):
428 def push(self, ns):
429 """ Put value into the namespace with name key.
429 """ Put value into the namespace with name key.
430
430
431 Parameters
431 Parameters
432 ----------
432 ----------
433 **kwds
433 **kwds
434 """
434 """
435
435
436 self.user_ns.update(ns)
436 self.user_ns.update(ns)
437
437
438 def push_function(self, ns):
438 def push_function(self, ns):
439 # 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
440 new_kwds = {}
440 new_kwds = {}
441 for k, v in ns.iteritems():
441 for k, v in ns.iteritems():
442 if not isinstance(v, FunctionType):
442 if not isinstance(v, FunctionType):
443 raise TypeError("function object expected")
443 raise TypeError("function object expected")
444 new_kwds[k] = FunctionType(v.func_code, self.user_ns)
444 new_kwds[k] = FunctionType(v.func_code, self.user_ns)
445 self.user_ns.update(new_kwds)
445 self.user_ns.update(new_kwds)
446
446
447 def pack_exception(self,message,exc):
447 def pack_exception(self,message,exc):
448 message['exception'] = exc.__class__
448 message['exception'] = exc.__class__
449 message['exception_value'] = \
449 message['exception_value'] = \
450 traceback.format_exception_only(exc.__class__, exc)
450 traceback.format_exception_only(exc.__class__, exc)
451
451
452 def feed_block(self, source, filename='<input>', symbol='single'):
452 def feed_block(self, source, filename='<input>', symbol='single'):
453 """Compile some source in the interpreter.
453 """Compile some source in the interpreter.
454
454
455 One several things can happen:
455 One several things can happen:
456
456
457 1) The input is incorrect; compile_command() raised an
457 1) The input is incorrect; compile_command() raised an
458 exception (SyntaxError or OverflowError).
458 exception (SyntaxError or OverflowError).
459
459
460 2) The input is incomplete, and more input is required;
460 2) The input is incomplete, and more input is required;
461 compile_command() returned None. Nothing happens.
461 compile_command() returned None. Nothing happens.
462
462
463 3) The input is complete; compile_command() returned a code
463 3) The input is complete; compile_command() returned a code
464 object. The code is executed by calling self.runcode() (which
464 object. The code is executed by calling self.runcode() (which
465 also handles run-time exceptions, except for SystemExit).
465 also handles run-time exceptions, except for SystemExit).
466
466
467 The return value is:
467 The return value is:
468
468
469 - True in case 2
469 - True in case 2
470
470
471 - False in the other cases, unless an exception is raised, where
471 - False in the other cases, unless an exception is raised, where
472 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
473 know whether to continue feeding input or not.
473 know whether to continue feeding input or not.
474
474
475 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
476 sys.ps2 to prompt the next line."""
476 sys.ps2 to prompt the next line."""
477
477
478 self.message = self.setup_message()
478 self.message = self.setup_message()
479
479
480 try:
480 try:
481 code = self.command_compiler(source,filename,symbol)
481 code = self.command_compiler(source,filename,symbol)
482 except (OverflowError, SyntaxError, IndentationError, ValueError ), e:
482 except (OverflowError, SyntaxError, IndentationError, ValueError ), e:
483 # Case 1
483 # Case 1
484 self.traceback_trap.args = sys.exc_info()
484 self.traceback_trap.args = sys.exc_info()
485 self.pack_exception(self.message,e)
485 self.pack_exception(self.message,e)
486 return COMPILER_ERROR,False
486 return COMPILER_ERROR,False
487
487
488 if code is None:
488 if code is None:
489 # Case 2: incomplete input. This means that the input can span
489 # Case 2: incomplete input. This means that the input can span
490 # multiple lines. But we still need to decide when to actually
490 # multiple lines. But we still need to decide when to actually
491 # stop taking user input. Later we'll add auto-indentation support
491 # stop taking user input. Later we'll add auto-indentation support
492 # 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
493 # of pure whitespace at the end.
493 # of pure whitespace at the end.
494 last_two = source.rsplit('\n',2)[-2:]
494 last_two = source.rsplit('\n',2)[-2:]
495 print 'last two:',last_two # dbg
495 print 'last two:',last_two # dbg
496 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):
497 return COMPLETE_INPUT,False
497 return COMPLETE_INPUT,False
498 else:
498 else:
499 return INCOMPLETE_INPUT, True
499 return INCOMPLETE_INPUT, True
500 else:
500 else:
501 # Case 3
501 # Case 3
502 return COMPLETE_INPUT, False
502 return COMPLETE_INPUT, False
503
503
504 def pull(self, keys):
504 def pull(self, keys):
505 """ Get an item out of the namespace by key.
505 """ Get an item out of the namespace by key.
506
506
507 Parameters
507 Parameters
508 ----------
508 ----------
509 key : str
509 key : str
510
510
511 Returns
511 Returns
512 -------
512 -------
513 value : object
513 value : object
514
514
515 Raises
515 Raises
516 ------
516 ------
517 TypeError if the key is not a string.
517 TypeError if the key is not a string.
518 NameError if the object doesn't exist.
518 NameError if the object doesn't exist.
519 """
519 """
520
520
521 if isinstance(keys, str):
521 if isinstance(keys, str):
522 result = self.user_ns.get(keys, NotDefined())
522 result = self.user_ns.get(keys, NotDefined())
523 if isinstance(result, NotDefined):
523 if isinstance(result, NotDefined):
524 raise NameError('name %s is not defined' % keys)
524 raise NameError('name %s is not defined' % keys)
525 elif isinstance(keys, (list, tuple)):
525 elif isinstance(keys, (list, tuple)):
526 result = []
526 result = []
527 for key in keys:
527 for key in keys:
528 if not isinstance(key, str):
528 if not isinstance(key, str):
529 raise TypeError("objects must be keyed by strings.")
529 raise TypeError("objects must be keyed by strings.")
530 else:
530 else:
531 r = self.user_ns.get(key, NotDefined())
531 r = self.user_ns.get(key, NotDefined())
532 if isinstance(r, NotDefined):
532 if isinstance(r, NotDefined):
533 raise NameError('name %s is not defined' % key)
533 raise NameError('name %s is not defined' % key)
534 else:
534 else:
535 result.append(r)
535 result.append(r)
536 if len(keys)==1:
536 if len(keys)==1:
537 result = result[0]
537 result = result[0]
538 else:
538 else:
539 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")
540 return result
540 return result
541
541
542 def pull_function(self, keys):
542 def pull_function(self, keys):
543 return self.pull(keys)
543 return self.pull(keys)
544
544
545 #### Interactive user API ##################################################
545 #### Interactive user API ##################################################
546
546
547 def ipsystem(self, command):
547 def ipsystem(self, command):
548 """ 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
549 current namespace.
549 current namespace.
550
550
551 Parameters
551 Parameters
552 ----------
552 ----------
553 command : str
553 command : str
554 """
554 """
555
555
556 # Expand $variables.
556 # Expand $variables.
557 command = self.var_expand(command)
557 command = self.var_expand(command)
558
558
559 system_shell(command,
559 system_shell(command,
560 header='IPython system call: ',
560 header='IPython system call: ',
561 verbose=self.rc.system_verbose,
561 verbose=self.rc.system_verbose,
562 )
562 )
563
563
564 def ipmagic(self, arg_string):
564 def ipmagic(self, arg_string):
565 """ Call a magic function by name.
565 """ Call a magic function by name.
566
566
567 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
568 prompt:
568 prompt:
569
569
570 In[1]: %name -opt foo bar
570 In[1]: %name -opt foo bar
571
571
572 To call a magic without arguments, simply use ipmagic('name').
572 To call a magic without arguments, simply use ipmagic('name').
573
573
574 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
575 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
576 compound statements. It is added by IPython to the Python builtin
576 compound statements. It is added by IPython to the Python builtin
577 namespace upon initialization.
577 namespace upon initialization.
578
578
579 Parameters
579 Parameters
580 ----------
580 ----------
581 arg_string : str
581 arg_string : str
582 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
583 additional arguments to be passed to the magic.
583 additional arguments to be passed to the magic.
584
584
585 Returns
585 Returns
586 -------
586 -------
587 something : object
587 something : object
588 The return value of the actual object.
588 The return value of the actual object.
589 """
589 """
590
590
591 # Taken from IPython.
591 # Taken from IPython.
592 raise NotImplementedError('Not ported yet')
592 raise NotImplementedError('Not ported yet')
593
593
594 args = arg_string.split(' ', 1)
594 args = arg_string.split(' ', 1)
595 magic_name = args[0]
595 magic_name = args[0]
596 magic_name = magic_name.lstrip(self.config.ESC_MAGIC)
596 magic_name = magic_name.lstrip(self.config.ESC_MAGIC)
597
597
598 try:
598 try:
599 magic_args = args[1]
599 magic_args = args[1]
600 except IndexError:
600 except IndexError:
601 magic_args = ''
601 magic_args = ''
602 fn = getattr(self.magic, 'magic_'+magic_name, None)
602 fn = getattr(self.magic, 'magic_'+magic_name, None)
603 if fn is None:
603 if fn is None:
604 self.error("Magic function `%s` not found." % magic_name)
604 self.error("Magic function `%s` not found." % magic_name)
605 else:
605 else:
606 magic_args = self.var_expand(magic_args)
606 magic_args = self.var_expand(magic_args)
607 return fn(magic_args)
607 return fn(magic_args)
608
608
609
609
610 #### Private 'Interpreter' interface #######################################
610 #### Private 'Interpreter' interface #######################################
611
611
612 def setup_message(self):
612 def setup_message(self):
613 """Return a message object.
613 """Return a message object.
614
614
615 This method prepares and returns a message dictionary. This dict
615 This method prepares and returns a message dictionary. This dict
616 contains the various fields that are used to transfer information about
616 contains the various fields that are used to transfer information about
617 execution, results, tracebacks, etc, to clients (either in or out of
617 execution, results, tracebacks, etc, to clients (either in or out of
618 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
619 process clients, this dict MUST contain strictly pickle-safe values.
619 process clients, this dict MUST contain strictly pickle-safe values.
620 """
620 """
621
621
622 return dict(number=self.current_cell_number)
622 return dict(number=self.current_cell_number)
623
623
624 def setup_namespace(self):
624 def setup_namespace(self):
625 """ Add things to the namespace.
625 """ Add things to the namespace.
626 """
626 """
627
627
628 self.user_ns.setdefault('__name__', '__main__')
628 self.user_ns.setdefault('__name__', '__main__')
629 self.user_ns.setdefault('__builtins__', __builtin__)
629 self.user_ns.setdefault('__builtins__', __builtin__)
630 self.user_ns['__IP'] = self
630 self.user_ns['__IP'] = self
631 if self.raw_input_builtin is not None:
631 if self.raw_input_builtin is not None:
632 self.user_ns['raw_input'] = self.raw_input_builtin
632 self.user_ns['raw_input'] = self.raw_input_builtin
633 if self.input_builtin is not None:
633 if self.input_builtin is not None:
634 self.user_ns['input'] = self.input_builtin
634 self.user_ns['input'] = self.input_builtin
635
635
636 builtin_additions = dict(
636 builtin_additions = dict(
637 ipmagic=self.ipmagic,
637 ipmagic=self.ipmagic,
638 )
638 )
639 __builtin__.__dict__.update(builtin_additions)
639 __builtin__.__dict__.update(builtin_additions)
640
640
641 if self.history is not None:
641 if self.history is not None:
642 self.history.setup_namespace(self.user_ns)
642 self.history.setup_namespace(self.user_ns)
643
643
644 def set_traps(self):
644 def set_traps(self):
645 """ Set all of the output, display, and traceback traps.
645 """ Set all of the output, display, and traceback traps.
646 """
646 """
647
647
648 self.output_trap.set()
648 self.output_trap.set()
649 self.display_trap.set()
649 self.display_trap.set()
650 self.traceback_trap.set()
650 self.traceback_trap.set()
651
651
652 def unset_traps(self):
652 def unset_traps(self):
653 """ Unset all of the output, display, and traceback traps.
653 """ Unset all of the output, display, and traceback traps.
654 """
654 """
655
655
656 self.output_trap.unset()
656 self.output_trap.unset()
657 self.display_trap.unset()
657 self.display_trap.unset()
658 self.traceback_trap.unset()
658 self.traceback_trap.unset()
659
659
660 def split_commands(self, python):
660 def split_commands(self, python):
661 """ Split multiple lines of code into discrete commands that can be
661 """ Split multiple lines of code into discrete commands that can be
662 executed singly.
662 executed singly.
663
663
664 Parameters
664 Parameters
665 ----------
665 ----------
666 python : str
666 python : str
667 Pure, exec'able Python code.
667 Pure, exec'able Python code.
668
668
669 Returns
669 Returns
670 -------
670 -------
671 commands : list of str
671 commands : list of str
672 Separate commands that can be exec'ed independently.
672 Separate commands that can be exec'ed independently.
673 """
673 """
674
674
675 # compiler.parse treats trailing spaces after a newline as a
675 # compiler.parse treats trailing spaces after a newline as a
676 # SyntaxError. This is different than codeop.CommandCompiler, which
676 # SyntaxError. This is different than codeop.CommandCompiler, which
677 # will compile the trailng spaces just fine. We simply strip any
677 # will compile the trailng spaces just fine. We simply strip any
678 # trailing whitespace off. Passing a string with trailing whitespace
678 # trailing whitespace off. Passing a string with trailing whitespace
679 # 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
680 # how trailing whitespace is handled, but this seems to work.
680 # how trailing whitespace is handled, but this seems to work.
681 python = python.strip()
681 python = python.strip()
682
682
683 # The compiler module does not like unicode. We need to convert
683 # The compiler module does not like unicode. We need to convert
684 # it encode it:
684 # it encode it:
685 if isinstance(python, unicode):
685 if isinstance(python, unicode):
686 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
686 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
687 # encode string.
687 # encode string.
688 python = '\xef\xbb\xbf' + python.encode('utf-8')
688 python = '\xef\xbb\xbf' + python.encode('utf-8')
689
689
690 # The compiler module will parse the code into an abstract syntax tree.
690 # The compiler module will parse the code into an abstract syntax tree.
691 # This has a bug with str("a\nb"), but not str("""a\nb""")!!!
691 # This has a bug with str("a\nb"), but not str("""a\nb""")!!!
692 ast = compiler.parse(python)
692 ast = compiler.parse(python)
693
693
694 # Uncomment to help debug the ast tree
694 # Uncomment to help debug the ast tree
695 # for n in ast.node:
695 # for n in ast.node:
696 # print n.lineno,'->',n
696 # print n.lineno,'->',n
697
697
698 # Each separate command is available by iterating over ast.node. The
698 # Each separate command is available by iterating over ast.node. The
699 # lineno attribute is the line number (1-indexed) beginning the commands
699 # lineno attribute is the line number (1-indexed) beginning the commands
700 # suite.
700 # suite.
701 # lines ending with ";" yield a Discard Node that doesn't have a lineno
701 # lines ending with ";" yield a Discard Node that doesn't have a lineno
702 # attribute. These nodes can and should be discarded. But there are
702 # attribute. These nodes can and should be discarded. But there are
703 # other situations that cause Discard nodes that shouldn't be discarded.
703 # other situations that cause Discard nodes that shouldn't be discarded.
704 # We might eventually discover other cases where lineno is None and have
704 # We might eventually discover other cases where lineno is None and have
705 # to put in a more sophisticated test.
705 # to put in a more sophisticated test.
706 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
706 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
707
707
708 # When we finally get the slices, we will need to slice all the way to
708 # When we finally get the slices, we will need to slice all the way to
709 # the end even though we don't have a line number for it. Fortunately,
709 # the end even though we don't have a line number for it. Fortunately,
710 # None does the job nicely.
710 # None does the job nicely.
711 linenos.append(None)
711 linenos.append(None)
712
712
713 # This is causing problems with commands that have a \n embedded in
713 # Same problem at the other end: sometimes the ast tree has its
714 # a string, such as str("""a\nb""")
714 # first complete statement not starting on line 0. In this case
715 # we might miss part of it. This fixes ticket 266993. Thanks Gael!
716 linenos[0] = 0
717
715 lines = python.splitlines()
718 lines = python.splitlines()
716
719
717 # Create a list of atomic commands.
720 # Create a list of atomic commands.
718 cmds = []
721 cmds = []
719 for i, j in zip(linenos[:-1], linenos[1:]):
722 for i, j in zip(linenos[:-1], linenos[1:]):
720 cmd = lines[i:j]
723 cmd = lines[i:j]
721 if cmd:
724 if cmd:
722 cmds.append('\n'.join(cmd)+'\n')
725 cmds.append('\n'.join(cmd)+'\n')
723
726
724 return cmds
727 return cmds
725
728
726 def error(self, text):
729 def error(self, text):
727 """ Pass an error message back to the shell.
730 """ Pass an error message back to the shell.
728
731
729 Preconditions
732 Preconditions
730 -------------
733 -------------
731 This should only be called when self.message is set. In other words,
734 This should only be called when self.message is set. In other words,
732 when code is being executed.
735 when code is being executed.
733
736
734 Parameters
737 Parameters
735 ----------
738 ----------
736 text : str
739 text : str
737 """
740 """
738
741
739 errors = self.message.get('IPYTHON_ERROR', [])
742 errors = self.message.get('IPYTHON_ERROR', [])
740 errors.append(text)
743 errors.append(text)
741
744
742 def var_expand(self, template):
745 def var_expand(self, template):
743 """ Expand $variables in the current namespace using Itpl.
746 """ Expand $variables in the current namespace using Itpl.
744
747
745 Parameters
748 Parameters
746 ----------
749 ----------
747 template : str
750 template : str
748 """
751 """
749
752
750 return str(ItplNS(template, self.user_ns))
753 return str(ItplNS(template, self.user_ns))
751
754
752 def _possible_macro(self, obj):
755 def _possible_macro(self, obj):
753 """ If the object is a macro, execute it.
756 """ If the object is a macro, execute it.
754 """
757 """
755
758
756 if isinstance(obj, Macro):
759 if isinstance(obj, Macro):
757 self.execute_macro(obj)
760 self.execute_macro(obj)
758
761
@@ -1,36 +1,61 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the interpreter.py module."""
3 """This file contains unittests for the interpreter.py module."""
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 import unittest
18 import unittest
19 from IPython.kernel.core.interpreter import Interpreter
19 from IPython.kernel.core.interpreter import Interpreter
20
20
21 #-----------------------------------------------------------------------------
22 # Tests
23 #-----------------------------------------------------------------------------
24
21 class TestInterpreter(unittest.TestCase):
25 class TestInterpreter(unittest.TestCase):
22
26
23 def test_unicode(self):
27 def test_unicode(self):
24 """ Test unicode handling with the interpreter.
28 """ Test unicode handling with the interpreter."""
25 """
26 i = Interpreter()
29 i = Interpreter()
27 i.execute_python(u'print "ΓΉ"')
30 i.execute_python(u'print "ΓΉ"')
28 i.execute_python('print "ΓΉ"')
31 i.execute_python('print "ΓΉ"')
29
32
30 def test_ticket266993_1(self):
33 def test_ticket266993_1(self):
34 """ Test for ticket 266993."""
31 i = Interpreter()
35 i = Interpreter()
32 i.execute('str("""a\nb""")')
36 i.execute('str("""a\nb""")')
33
37
34 def test_ticket266993_2(self):
38 def test_ticket364347(self):
39 """Test for ticket 364347."""
35 i = Interpreter()
40 i = Interpreter()
36 i.execute('str("a\nb")') No newline at end of file
41 i.split_commands('str("a\nb")')
42
43 def test_split_commands(self):
44 """ Test that commands are indeed individually split."""
45 i = Interpreter()
46 test_atoms = [('(1\n + 1)', ),
47 ('1', '1', ),
48 ]
49 for atoms in test_atoms:
50 atoms = [atom.rstrip() + '\n' for atom in atoms]
51 self.assertEquals(i.split_commands(''.join(atoms)),atoms)
52
53 def test_long_lines(self):
54 """ Test for spurious syntax error created by the interpreter."""
55 test_strings = [u'( 1 +\n 1\n )\n\n',
56 u'(1 \n + 1\n )\n\n',
57 ]
58 i = Interpreter()
59 for s in test_strings:
60 i.execute(s)
61
@@ -1,279 +1,279 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """
3 """
4 This module defines the things that are used in setup.py for building IPython
4 This module defines the things that are used in setup.py for building IPython
5
5
6 This includes:
6 This includes:
7
7
8 * The basic arguments to setup
8 * The basic arguments to setup
9 * Functions for finding things like packages, package data, etc.
9 * Functions for finding things like packages, package data, etc.
10 * A function for checking dependencies.
10 * A function for checking dependencies.
11 """
11 """
12
12
13 __docformat__ = "restructuredtext en"
13 __docformat__ = "restructuredtext en"
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 The IPython Development Team
16 # Copyright (C) 2008 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-------------------------------------------------------------------------------
24 #-------------------------------------------------------------------------------
25
25
26 import os, sys
26 import os, sys
27
27
28 from glob import glob
28 from glob import glob
29
29
30 from setupext import install_data_ext
30 from setupext import install_data_ext
31
31
32 #-------------------------------------------------------------------------------
32 #-------------------------------------------------------------------------------
33 # Useful globals and utility functions
33 # Useful globals and utility functions
34 #-------------------------------------------------------------------------------
34 #-------------------------------------------------------------------------------
35
35
36 # A few handy globals
36 # A few handy globals
37 isfile = os.path.isfile
37 isfile = os.path.isfile
38 pjoin = os.path.join
38 pjoin = os.path.join
39
39
40 def oscmd(s):
40 def oscmd(s):
41 print ">", s
41 print ">", s
42 os.system(s)
42 os.system(s)
43
43
44 # A little utility we'll need below, since glob() does NOT allow you to do
44 # A little utility we'll need below, since glob() does NOT allow you to do
45 # exclusion on multiple endings!
45 # exclusion on multiple endings!
46 def file_doesnt_endwith(test,endings):
46 def file_doesnt_endwith(test,endings):
47 """Return true if test is a file and its name does NOT end with any
47 """Return true if test is a file and its name does NOT end with any
48 of the strings listed in endings."""
48 of the strings listed in endings."""
49 if not isfile(test):
49 if not isfile(test):
50 return False
50 return False
51 for e in endings:
51 for e in endings:
52 if test.endswith(e):
52 if test.endswith(e):
53 return False
53 return False
54 return True
54 return True
55
55
56 #---------------------------------------------------------------------------
56 #---------------------------------------------------------------------------
57 # Basic project information
57 # Basic project information
58 #---------------------------------------------------------------------------
58 #---------------------------------------------------------------------------
59
59
60 # Release.py contains version, authors, license, url, keywords, etc.
60 # Release.py contains version, authors, license, url, keywords, etc.
61 execfile(pjoin('IPython','Release.py'))
61 execfile(pjoin('IPython','Release.py'))
62
62
63 # Create a dict with the basic information
63 # Create a dict with the basic information
64 # This dict is eventually passed to setup after additional keys are added.
64 # This dict is eventually passed to setup after additional keys are added.
65 setup_args = dict(
65 setup_args = dict(
66 name = name,
66 name = name,
67 version = version,
67 version = version,
68 description = description,
68 description = description,
69 long_description = long_description,
69 long_description = long_description,
70 author = author,
70 author = author,
71 author_email = author_email,
71 author_email = author_email,
72 url = url,
72 url = url,
73 download_url = download_url,
73 download_url = download_url,
74 license = license,
74 license = license,
75 platforms = platforms,
75 platforms = platforms,
76 keywords = keywords,
76 keywords = keywords,
77 cmdclass = {'install_data': install_data_ext},
77 cmdclass = {'install_data': install_data_ext},
78 )
78 )
79
79
80
80
81 #---------------------------------------------------------------------------
81 #---------------------------------------------------------------------------
82 # Find packages
82 # Find packages
83 #---------------------------------------------------------------------------
83 #---------------------------------------------------------------------------
84
84
85 def add_package(packages,pname,config=False,tests=False,scripts=False,
85 def add_package(packages,pname,config=False,tests=False,scripts=False,
86 others=None):
86 others=None):
87 """
87 """
88 Add a package to the list of packages, including certain subpackages.
88 Add a package to the list of packages, including certain subpackages.
89 """
89 """
90 packages.append('.'.join(['IPython',pname]))
90 packages.append('.'.join(['IPython',pname]))
91 if config:
91 if config:
92 packages.append('.'.join(['IPython',pname,'config']))
92 packages.append('.'.join(['IPython',pname,'config']))
93 if tests:
93 if tests:
94 packages.append('.'.join(['IPython',pname,'tests']))
94 packages.append('.'.join(['IPython',pname,'tests']))
95 if scripts:
95 if scripts:
96 packages.append('.'.join(['IPython',pname,'scripts']))
96 packages.append('.'.join(['IPython',pname,'scripts']))
97 if others is not None:
97 if others is not None:
98 for o in others:
98 for o in others:
99 packages.append('.'.join(['IPython',pname,o]))
99 packages.append('.'.join(['IPython',pname,o]))
100
100
101 def find_packages():
101 def find_packages():
102 """
102 """
103 Find all of IPython's packages.
103 Find all of IPython's packages.
104 """
104 """
105 packages = ['IPython']
105 packages = ['IPython']
106 add_package(packages, 'config', tests=True)
106 add_package(packages, 'config', tests=True)
107 add_package(packages , 'Extensions')
107 add_package(packages , 'Extensions')
108 add_package(packages, 'external')
108 add_package(packages, 'external')
109 add_package(packages, 'gui')
109 add_package(packages, 'gui')
110 add_package(packages, 'gui.wx')
110 add_package(packages, 'gui.wx')
111 add_package(packages, 'frontend', tests=True)
111 add_package(packages, 'frontend', tests=True)
112 add_package(packages, 'frontend._process')
112 add_package(packages, 'frontend.process')
113 add_package(packages, 'frontend.wx')
113 add_package(packages, 'frontend.wx')
114 add_package(packages, 'frontend.cocoa', tests=True)
114 add_package(packages, 'frontend.cocoa', tests=True)
115 add_package(packages, 'kernel', config=True, tests=True, scripts=True)
115 add_package(packages, 'kernel', config=True, tests=True, scripts=True)
116 add_package(packages, 'kernel.core', config=True, tests=True)
116 add_package(packages, 'kernel.core', config=True, tests=True)
117 add_package(packages, 'testing', tests=True)
117 add_package(packages, 'testing', tests=True)
118 add_package(packages, 'tests')
118 add_package(packages, 'tests')
119 add_package(packages, 'testing.plugin', tests=False)
119 add_package(packages, 'testing.plugin', tests=False)
120 add_package(packages, 'tools', tests=True)
120 add_package(packages, 'tools', tests=True)
121 add_package(packages, 'UserConfig')
121 add_package(packages, 'UserConfig')
122 return packages
122 return packages
123
123
124 #---------------------------------------------------------------------------
124 #---------------------------------------------------------------------------
125 # Find package data
125 # Find package data
126 #---------------------------------------------------------------------------
126 #---------------------------------------------------------------------------
127
127
128 def find_package_data():
128 def find_package_data():
129 """
129 """
130 Find IPython's package_data.
130 Find IPython's package_data.
131 """
131 """
132 # This is not enough for these things to appear in an sdist.
132 # This is not enough for these things to appear in an sdist.
133 # We need to muck with the MANIFEST to get this to work
133 # We need to muck with the MANIFEST to get this to work
134 package_data = {
134 package_data = {
135 'IPython.UserConfig' : ['*'],
135 'IPython.UserConfig' : ['*'],
136 'IPython.tools.tests' : ['*.txt'],
136 'IPython.tools.tests' : ['*.txt'],
137 'IPython.testing' : ['*.txt']
137 'IPython.testing' : ['*.txt']
138 }
138 }
139 return package_data
139 return package_data
140
140
141
141
142 #---------------------------------------------------------------------------
142 #---------------------------------------------------------------------------
143 # Find data files
143 # Find data files
144 #---------------------------------------------------------------------------
144 #---------------------------------------------------------------------------
145
145
146 def make_dir_struct(tag,base,out_base):
146 def make_dir_struct(tag,base,out_base):
147 """Make the directory structure of all files below a starting dir.
147 """Make the directory structure of all files below a starting dir.
148
148
149 This is just a convenience routine to help build a nested directory
149 This is just a convenience routine to help build a nested directory
150 hierarchy because distutils is too stupid to do this by itself.
150 hierarchy because distutils is too stupid to do this by itself.
151
151
152 XXX - this needs a proper docstring!
152 XXX - this needs a proper docstring!
153 """
153 """
154
154
155 # we'll use these a lot below
155 # we'll use these a lot below
156 lbase = len(base)
156 lbase = len(base)
157 pathsep = os.path.sep
157 pathsep = os.path.sep
158 lpathsep = len(pathsep)
158 lpathsep = len(pathsep)
159
159
160 out = []
160 out = []
161 for (dirpath,dirnames,filenames) in os.walk(base):
161 for (dirpath,dirnames,filenames) in os.walk(base):
162 # we need to strip out the dirpath from the base to map it to the
162 # we need to strip out the dirpath from the base to map it to the
163 # output (installation) path. This requires possibly stripping the
163 # output (installation) path. This requires possibly stripping the
164 # path separator, because otherwise pjoin will not work correctly
164 # path separator, because otherwise pjoin will not work correctly
165 # (pjoin('foo/','/bar') returns '/bar').
165 # (pjoin('foo/','/bar') returns '/bar').
166
166
167 dp_eff = dirpath[lbase:]
167 dp_eff = dirpath[lbase:]
168 if dp_eff.startswith(pathsep):
168 if dp_eff.startswith(pathsep):
169 dp_eff = dp_eff[lpathsep:]
169 dp_eff = dp_eff[lpathsep:]
170 # The output path must be anchored at the out_base marker
170 # The output path must be anchored at the out_base marker
171 out_path = pjoin(out_base,dp_eff)
171 out_path = pjoin(out_base,dp_eff)
172 # Now we can generate the final filenames. Since os.walk only produces
172 # Now we can generate the final filenames. Since os.walk only produces
173 # filenames, we must join back with the dirpath to get full valid file
173 # filenames, we must join back with the dirpath to get full valid file
174 # paths:
174 # paths:
175 pfiles = [pjoin(dirpath,f) for f in filenames]
175 pfiles = [pjoin(dirpath,f) for f in filenames]
176 # Finally, generate the entry we need, which is a triple of (tag,output
176 # Finally, generate the entry we need, which is a triple of (tag,output
177 # path, files) for use as a data_files parameter in install_data.
177 # path, files) for use as a data_files parameter in install_data.
178 out.append((tag,out_path,pfiles))
178 out.append((tag,out_path,pfiles))
179
179
180 return out
180 return out
181
181
182
182
183 def find_data_files():
183 def find_data_files():
184 """
184 """
185 Find IPython's data_files.
185 Find IPython's data_files.
186
186
187 Most of these are docs.
187 Most of these are docs.
188 """
188 """
189
189
190 docdirbase = 'share/doc/ipython'
190 docdirbase = 'share/doc/ipython'
191 manpagebase = 'share/man/man1'
191 manpagebase = 'share/man/man1'
192
192
193 # Simple file lists can be made by hand
193 # Simple file lists can be made by hand
194 manpages = filter(isfile, glob('docs/man/*.1.gz'))
194 manpages = filter(isfile, glob('docs/man/*.1.gz'))
195 igridhelpfiles = filter(isfile, glob('IPython/Extensions/igrid_help.*'))
195 igridhelpfiles = filter(isfile, glob('IPython/Extensions/igrid_help.*'))
196
196
197 # For nested structures, use the utility above
197 # For nested structures, use the utility above
198 example_files = make_dir_struct('data','docs/examples',
198 example_files = make_dir_struct('data','docs/examples',
199 pjoin(docdirbase,'examples'))
199 pjoin(docdirbase,'examples'))
200 manual_files = make_dir_struct('data','docs/dist',pjoin(docdirbase,'manual'))
200 manual_files = make_dir_struct('data','docs/dist',pjoin(docdirbase,'manual'))
201
201
202 # And assemble the entire output list
202 # And assemble the entire output list
203 data_files = [ ('data',manpagebase, manpages),
203 data_files = [ ('data',manpagebase, manpages),
204 ('data',pjoin(docdirbase,'extensions'),igridhelpfiles),
204 ('data',pjoin(docdirbase,'extensions'),igridhelpfiles),
205 ] + manual_files + example_files
205 ] + manual_files + example_files
206
206
207 ## import pprint # dbg
207 ## import pprint # dbg
208 ## print '*'*80
208 ## print '*'*80
209 ## print 'data files'
209 ## print 'data files'
210 ## pprint.pprint(data_files)
210 ## pprint.pprint(data_files)
211 ## print '*'*80
211 ## print '*'*80
212
212
213 return data_files
213 return data_files
214
214
215 #---------------------------------------------------------------------------
215 #---------------------------------------------------------------------------
216 # Find scripts
216 # Find scripts
217 #---------------------------------------------------------------------------
217 #---------------------------------------------------------------------------
218
218
219 def find_scripts():
219 def find_scripts():
220 """
220 """
221 Find IPython's scripts.
221 Find IPython's scripts.
222 """
222 """
223 scripts = ['IPython/kernel/scripts/ipengine',
223 scripts = ['IPython/kernel/scripts/ipengine',
224 'IPython/kernel/scripts/ipcontroller',
224 'IPython/kernel/scripts/ipcontroller',
225 'IPython/kernel/scripts/ipcluster',
225 'IPython/kernel/scripts/ipcluster',
226 'scripts/ipython',
226 'scripts/ipython',
227 'scripts/ipythonx',
227 'scripts/ipythonx',
228 'scripts/ipython-wx',
228 'scripts/ipython-wx',
229 'scripts/pycolor',
229 'scripts/pycolor',
230 'scripts/irunner',
230 'scripts/irunner',
231 'scripts/iptest',
231 'scripts/iptest',
232 ]
232 ]
233
233
234 # Script to be run by the windows binary installer after the default setup
234 # Script to be run by the windows binary installer after the default setup
235 # routine, to add shortcuts and similar windows-only things. Windows
235 # routine, to add shortcuts and similar windows-only things. Windows
236 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
236 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
237 # doesn't find them.
237 # doesn't find them.
238 if 'bdist_wininst' in sys.argv:
238 if 'bdist_wininst' in sys.argv:
239 if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
239 if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
240 print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting."
240 print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting."
241 sys.exit(1)
241 sys.exit(1)
242 scripts.append('scripts/ipython_win_post_install.py')
242 scripts.append('scripts/ipython_win_post_install.py')
243
243
244 return scripts
244 return scripts
245
245
246 #---------------------------------------------------------------------------
246 #---------------------------------------------------------------------------
247 # Verify all dependencies
247 # Verify all dependencies
248 #---------------------------------------------------------------------------
248 #---------------------------------------------------------------------------
249
249
250 def check_for_dependencies():
250 def check_for_dependencies():
251 """Check for IPython's dependencies.
251 """Check for IPython's dependencies.
252
252
253 This function should NOT be called if running under setuptools!
253 This function should NOT be called if running under setuptools!
254 """
254 """
255 from setupext.setupext import (
255 from setupext.setupext import (
256 print_line, print_raw, print_status, print_message,
256 print_line, print_raw, print_status, print_message,
257 check_for_zopeinterface, check_for_twisted,
257 check_for_zopeinterface, check_for_twisted,
258 check_for_foolscap, check_for_pyopenssl,
258 check_for_foolscap, check_for_pyopenssl,
259 check_for_sphinx, check_for_pygments,
259 check_for_sphinx, check_for_pygments,
260 check_for_nose, check_for_pexpect
260 check_for_nose, check_for_pexpect
261 )
261 )
262 print_line()
262 print_line()
263 print_raw("BUILDING IPYTHON")
263 print_raw("BUILDING IPYTHON")
264 print_status('python', sys.version)
264 print_status('python', sys.version)
265 print_status('platform', sys.platform)
265 print_status('platform', sys.platform)
266 if sys.platform == 'win32':
266 if sys.platform == 'win32':
267 print_status('Windows version', sys.getwindowsversion())
267 print_status('Windows version', sys.getwindowsversion())
268
268
269 print_raw("")
269 print_raw("")
270 print_raw("OPTIONAL DEPENDENCIES")
270 print_raw("OPTIONAL DEPENDENCIES")
271
271
272 check_for_zopeinterface()
272 check_for_zopeinterface()
273 check_for_twisted()
273 check_for_twisted()
274 check_for_foolscap()
274 check_for_foolscap()
275 check_for_pyopenssl()
275 check_for_pyopenssl()
276 check_for_sphinx()
276 check_for_sphinx()
277 check_for_pygments()
277 check_for_pygments()
278 check_for_nose()
278 check_for_nose()
279 check_for_pexpect()
279 check_for_pexpect()
General Comments 0
You need to be logged in to leave comments. Login now