##// END OF EJS Templates
Take in account remarks by Fernando on code review
Gael Varoquaux -
Show More
@@ -1,367 +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 sys
21 import sys
22 import codeop
22 import codeop
23
23
24 from frontendbase import FrontEndBase
24 from frontendbase import FrontEndBase
25 from IPython.kernel.core.interpreter import Interpreter
25 from IPython.kernel.core.interpreter import Interpreter
26
26
27 def common_prefix(strings):
27 def common_prefix(strings):
28 """ Given a list of strings, return the common prefix between all
28 """ Given a list of strings, return the common prefix between all
29 these strings.
29 these strings.
30 """
30 """
31 ref = strings[0]
31 ref = strings[0]
32 prefix = ''
32 prefix = ''
33 for size in range(len(ref)):
33 for size in range(len(ref)):
34 test_prefix = ref[:size+1]
34 test_prefix = ref[:size+1]
35 for string in strings[1:]:
35 for string in strings[1:]:
36 if not string.startswith(test_prefix):
36 if not string.startswith(test_prefix):
37 return prefix
37 return prefix
38 prefix = test_prefix
38 prefix = test_prefix
39
39
40 return prefix
40 return prefix
41
41
42 #-------------------------------------------------------------------------------
42 #-------------------------------------------------------------------------------
43 # Base class for the line-oriented front ends
43 # Base class for the line-oriented front ends
44 #-------------------------------------------------------------------------------
44 #-------------------------------------------------------------------------------
45 class LineFrontEndBase(FrontEndBase):
45 class LineFrontEndBase(FrontEndBase):
46 """ Concrete implementation of the FrontEndBase class. This is meant
46 """ Concrete implementation of the FrontEndBase class. This is meant
47 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,
48 rather than block-oriented.
48 rather than block-oriented.
49 """
49 """
50
50
51 # 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
52 # it when there is an exception.
52 # it when there is an exception.
53 prompt_number = 1
53 prompt_number = 1
54
54
55 # 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
56 # programatic control of the frontend.
56 # programatic control of the frontend.
57 last_result = dict(number=0)
57 last_result = dict(number=0)
58
58
59 # The last prompt displayed. Useful for continuation prompts.
59 # The last prompt displayed. Useful for continuation prompts.
60 last_prompt = ''
60 last_prompt = ''
61
61
62 # The input buffer being edited
62 # The input buffer being edited
63 input_buffer = ''
63 input_buffer = ''
64
64
65 # Set to true for debug output
65 # Set to true for debug output
66 debug = False
66 debug = False
67
67
68 # A banner to print at startup
68 # A banner to print at startup
69 banner = None
69 banner = None
70
70
71 #--------------------------------------------------------------------------
71 #--------------------------------------------------------------------------
72 # FrontEndBase interface
72 # FrontEndBase interface
73 #--------------------------------------------------------------------------
73 #--------------------------------------------------------------------------
74
74
75 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
75 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
76 if shell is None:
76 if shell is None:
77 shell = Interpreter()
77 shell = Interpreter()
78 FrontEndBase.__init__(self, shell=shell, history=history)
78 FrontEndBase.__init__(self, shell=shell, history=history)
79
79
80 if banner is not None:
80 if banner is not None:
81 self.banner = banner
81 self.banner = banner
82
82
83 def start(self):
83 def start(self):
84 """ 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
85 interaction.
85 interaction.
86 """
86 """
87 if self.banner is not None:
87 if self.banner is not None:
88 self.write(self.banner, refresh=False)
88 self.write(self.banner, refresh=False)
89
89
90 self.new_prompt(self.input_prompt_template.substitute(number=1))
90 self.new_prompt(self.input_prompt_template.substitute(number=1))
91
91
92
92
93 def complete(self, line):
93 def complete(self, line):
94 """Complete line in engine's user_ns
94 """Complete line in engine's user_ns
95
95
96 Parameters
96 Parameters
97 ----------
97 ----------
98 line : string
98 line : string
99
99
100 Result
100 Result
101 ------
101 ------
102 The replacement for the line and the list of possible completions.
102 The replacement for the line and the list of possible completions.
103 """
103 """
104 completions = self.shell.complete(line)
104 completions = self.shell.complete(line)
105 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
105 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
106 if completions:
106 if completions:
107 prefix = common_prefix(completions)
107 prefix = common_prefix(completions)
108 residual = complete_sep.split(line)[:-1]
108 residual = complete_sep.split(line)[:-1]
109 line = line[:-len(residual)] + prefix
109 line = line[:-len(residual)] + prefix
110 return line, completions
110 return line, completions
111
111
112
112
113 def render_result(self, result):
113 def render_result(self, result):
114 """ Frontend-specific rendering of the result of a calculation
114 """ Frontend-specific rendering of the result of a calculation
115 that has been sent to an engine.
115 that has been sent to an engine.
116 """
116 """
117 if 'stdout' in result and result['stdout']:
117 if 'stdout' in result and result['stdout']:
118 self.write('\n' + result['stdout'])
118 self.write('\n' + result['stdout'])
119 if 'display' in result and result['display']:
119 if 'display' in result and result['display']:
120 self.write("%s%s\n" % (
120 self.write("%s%s\n" % (
121 self.output_prompt_template.substitute(
121 self.output_prompt_template.substitute(
122 number=result['number']),
122 number=result['number']),
123 result['display']['pprint']
123 result['display']['pprint']
124 ) )
124 ) )
125
125
126
126
127 def render_error(self, failure):
127 def render_error(self, failure):
128 """ Frontend-specific rendering of error.
128 """ Frontend-specific rendering of error.
129 """
129 """
130 self.write('\n\n'+str(failure)+'\n\n')
130 self.write('\n\n'+str(failure)+'\n\n')
131 return failure
131 return failure
132
132
133
133
134 def is_complete(self, string):
134 def is_complete(self, string):
135 """ Check if a string forms a complete, executable set of
135 """ Check if a string forms a complete, executable set of
136 commands.
136 commands.
137
137
138 For the line-oriented frontend, multi-line code is not executed
138 For the line-oriented frontend, multi-line code is not executed
139 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
140 returns.
140 returns.
141 """
141 """
142 if string in ('', '\n'):
142 if string in ('', '\n'):
143 # Prefiltering, eg through ipython0, may return an empty
143 # Prefiltering, eg through ipython0, may return an empty
144 # string although some operations have been accomplished. We
144 # string although some operations have been accomplished. We
145 # thus want to consider an empty string as a complete
145 # thus want to consider an empty string as a complete
146 # statement.
146 # statement.
147 return True
147 return True
148 elif ( len(self.input_buffer.split('\n'))>2
148 elif ( len(self.input_buffer.split('\n'))>2
149 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
149 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
150 return False
150 return False
151 else:
151 else:
152 self.capture_output()
152 self.capture_output()
153 try:
153 try:
154 # Add line returns here, to make sure that the statement is
154 # Add line returns here, to make sure that the statement is
155 # complete (except if '\' was used).
155 # complete (except if '\' was used).
156 # This should probably be done in a different place (like
156 # This should probably be done in a different place (like
157 # maybe 'prefilter_input' method? For now, this works.
157 # maybe 'prefilter_input' method? For now, this works.
158 clean_string = string.rstrip('\n')
158 clean_string = string.rstrip('\n')
159 if not clean_string.endswith('\\'): clean_string +='\n\n'
159 if not clean_string.endswith('\\'): clean_string +='\n\n'
160 is_complete = codeop.compile_command(clean_string,
160 is_complete = codeop.compile_command(clean_string,
161 "<string>", "exec")
161 "<string>", "exec")
162 self.release_output()
162 self.release_output()
163 except Exception, e:
163 except Exception, e:
164 # XXX: Hack: return True so that the
164 # XXX: Hack: return True so that the
165 # code gets executed and the error captured.
165 # code gets executed and the error captured.
166 is_complete = True
166 is_complete = True
167 return is_complete
167 return is_complete
168
168
169
169
170 def write(self, string, refresh=True):
170 def write(self, string, refresh=True):
171 """ Write some characters to the display.
171 """ Write some characters to the display.
172
172
173 Subclass should overide this method.
173 Subclass should overide this method.
174
174
175 The refresh keyword argument is used in frontends with an
175 The refresh keyword argument is used in frontends with an
176 event loop, to choose whether the write should trigget an UI
176 event loop, to choose whether the write should trigget an UI
177 refresh, and thus be syncrhonous, or not.
177 refresh, and thus be syncrhonous, or not.
178 """
178 """
179 print >>sys.__stderr__, string
179 print >>sys.__stderr__, string
180
180
181
181
182 def execute(self, python_string, raw_string=None):
182 def execute(self, python_string, raw_string=None):
183 """ Stores the raw_string in the history, and sends the
183 """ Stores the raw_string in the history, and sends the
184 python string to the interpreter.
184 python string to the interpreter.
185 """
185 """
186 if raw_string is None:
186 if raw_string is None:
187 raw_string = python_string
187 raw_string = python_string
188 # Create a false result, in case there is an exception
188 # Create a false result, in case there is an exception
189 self.last_result = dict(number=self.prompt_number)
189 self.last_result = dict(number=self.prompt_number)
190
190
191 try:
191 try:
192 try:
192 try:
193 self.history.input_cache[-1] = raw_string.rstrip()
193 self.history.input_cache[-1] = raw_string.rstrip()
194 result = self.shell.execute(python_string)
194 result = self.shell.execute(python_string)
195 self.last_result = result
195 self.last_result = result
196 self.render_result(result)
196 self.render_result(result)
197 except:
197 except:
198 self.show_traceback()
198 self.show_traceback()
199 finally:
199 finally:
200 self.after_execute()
200 self.after_execute()
201
201
202
202
203 #--------------------------------------------------------------------------
203 #--------------------------------------------------------------------------
204 # LineFrontEndBase interface
204 # LineFrontEndBase interface
205 #--------------------------------------------------------------------------
205 #--------------------------------------------------------------------------
206
206
207 def prefilter_input(self, string):
207 def prefilter_input(self, string):
208 """ Prefilter the input to turn it in valid python.
208 """ Prefilter the input to turn it in valid python.
209 """
209 """
210 string = string.replace('\r\n', '\n')
210 string = string.replace('\r\n', '\n')
211 string = string.replace('\t', 4*' ')
211 string = string.replace('\t', 4*' ')
212 # Clean the trailing whitespace
212 # Clean the trailing whitespace
213 string = '\n'.join(l.rstrip() for l in string.split('\n'))
213 string = '\n'.join(l.rstrip() for l in string.split('\n'))
214 return string
214 return string
215
215
216
216
217 def after_execute(self):
217 def after_execute(self):
218 """ All the operations required after an execution to put the
218 """ All the operations required after an execution to put the
219 terminal back in a shape where it is usable.
219 terminal back in a shape where it is usable.
220 """
220 """
221 self.prompt_number += 1
221 self.prompt_number += 1
222 self.new_prompt(self.input_prompt_template.substitute(
222 self.new_prompt(self.input_prompt_template.substitute(
223 number=(self.last_result['number'] + 1)))
223 number=(self.last_result['number'] + 1)))
224 # Start a new empty history entry
224 # Start a new empty history entry
225 self._add_history(None, '')
225 self._add_history(None, '')
226 self.history_cursor = len(self.history.input_cache) - 1
226 self.history_cursor = len(self.history.input_cache) - 1
227
227
228
228
229 def complete_current_input(self):
229 def complete_current_input(self):
230 """ Do code completion on current line.
230 """ Do code completion on current line.
231 """
231 """
232 if self.debug:
232 if self.debug:
233 print >>sys.__stdout__, "complete_current_input",
233 print >>sys.__stdout__, "complete_current_input",
234 line = self.input_buffer
234 line = self.input_buffer
235 new_line, completions = self.complete(line)
235 new_line, completions = self.complete(line)
236 if len(completions)>1:
236 if len(completions)>1:
237 self.write_completion(completions, new_line=new_line)
237 self.write_completion(completions, new_line=new_line)
238 elif not line == new_line:
238 elif not line == new_line:
239 self.input_buffer = new_line
239 self.input_buffer = new_line
240 if self.debug:
240 if self.debug:
241 print >>sys.__stdout__, 'line', line
241 print >>sys.__stdout__, 'line', line
242 print >>sys.__stdout__, 'new_line', new_line
242 print >>sys.__stdout__, 'new_line', new_line
243 print >>sys.__stdout__, completions
243 print >>sys.__stdout__, completions
244
244
245
245
246 def get_line_width(self):
246 def get_line_width(self):
247 """ Return the width of the line in characters.
247 """ Return the width of the line in characters.
248 """
248 """
249 return 80
249 return 80
250
250
251
251
252 def write_completion(self, possibilities, new_line=None):
252 def write_completion(self, possibilities, new_line=None):
253 """ Write the list of possible completions.
253 """ Write the list of possible completions.
254
254
255 new_line is the completed input line that should be displayed
255 new_line is the completed input line that should be displayed
256 after the completion are writen. If None, the input_buffer
256 after the completion are writen. If None, the input_buffer
257 before the completion is used.
257 before the completion is used.
258 """
258 """
259 if new_line is None:
259 if new_line is None:
260 new_line = self.input_buffer
260 new_line = self.input_buffer
261
261
262 self.write('\n')
262 self.write('\n')
263 max_len = len(max(possibilities, key=len)) + 1
263 max_len = len(max(possibilities, key=len)) + 1
264
264
265 # 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...
266 chars_per_line = self.get_line_width()
266 chars_per_line = self.get_line_width()
267 symbols_per_line = max(1, chars_per_line/max_len)
267 symbols_per_line = max(1, chars_per_line/max_len)
268
268
269 pos = 1
269 pos = 1
270 completion_string = []
270 completion_string = []
271 for symbol in possibilities:
271 for symbol in possibilities:
272 if pos < symbols_per_line:
272 if pos < symbols_per_line:
273 completion_string.append(symbol.ljust(max_len))
273 completion_string.append(symbol.ljust(max_len))
274 pos += 1
274 pos += 1
275 else:
275 else:
276 completion_string.append(symbol.rstrip() + '\n')
276 completion_string.append(symbol.rstrip() + '\n')
277 pos = 1
277 pos = 1
278 self.write(''.join(completion_string))
278 self.write(''.join(completion_string))
279 self.new_prompt(self.input_prompt_template.substitute(
279 self.new_prompt(self.input_prompt_template.substitute(
280 number=self.last_result['number'] + 1))
280 number=self.last_result['number'] + 1))
281 self.input_buffer = new_line
281 self.input_buffer = new_line
282
282
283
283
284 def new_prompt(self, prompt):
284 def new_prompt(self, prompt):
285 """ Prints a prompt and starts a new editing buffer.
285 """ Prints a prompt and starts a new editing buffer.
286
286
287 Subclasses should use this method to make sure that the
287 Subclasses should use this method to make sure that the
288 terminal is put in a state favorable for a new line
288 terminal is put in a state favorable for a new line
289 input.
289 input.
290 """
290 """
291 self.input_buffer = ''
291 self.input_buffer = ''
292 self.write(prompt)
292 self.write(prompt)
293
293
294
294
295 def continuation_prompt(self):
295 def continuation_prompt(self):
296 """Returns the current continuation prompt.
296 """Returns the current continuation prompt.
297 """
297 """
298 return ("."*(len(self.last_prompt)-2) + ': ')
298 return ("."*(len(self.last_prompt)-2) + ': ')
299
299
300
300
301 def execute_command(self, command, hidden=False):
301 def execute_command(self, command, hidden=False):
302 """ Execute a command, not only in the model, but also in the
302 """ Execute a command, not only in the model, but also in the
303 view, if any.
303 view, if any.
304 """
304 """
305 return self.shell.execute(command)
305 return self.shell.execute(command)
306
306
307 #--------------------------------------------------------------------------
307 #--------------------------------------------------------------------------
308 # Private API
308 # Private API
309 #--------------------------------------------------------------------------
309 #--------------------------------------------------------------------------
310
310
311 def _on_enter(self, new_line_pos=0):
311 def _on_enter(self, new_line_pos=0):
312 """ Called when the return key is pressed in a line editing
312 """ Called when the return key is pressed in a line editing
313 buffer.
313 buffer.
314
314
315 Parameters
315 Parameters
316 ----------
316 ----------
317 new_line_pos : integer, optional
317 new_line_pos : integer, optional
318 Position of the new line to add, starting from the
318 Position of the new line to add, starting from the
319 end (0 adds a new line after the last line, -1 before
319 end (0 adds a new line after the last line, -1 before
320 the last line...)
320 the last line...)
321
321
322 Returns
322 Returns
323 -------
323 -------
324 True if execution is triggered
324 True if execution is triggered
325 """
325 """
326 current_buffer = self.input_buffer
326 current_buffer = self.input_buffer
327 # XXX: This string replace is ugly, but there should be no way it
327 # XXX: This string replace is ugly, but there should be no way it
328 # fails.
328 # fails.
329 prompt_less_buffer = re.sub('^' + self.continuation_prompt(),
329 prompt_less_buffer = re.sub('^' + self.continuation_prompt(),
330 '', current_buffer).replace('\n' + self.continuation_prompt(),
330 '', current_buffer).replace('\n' + self.continuation_prompt(),
331 '\n')
331 '\n')
332 cleaned_buffer = self.prefilter_input(prompt_less_buffer)
332 cleaned_buffer = self.prefilter_input(prompt_less_buffer)
333 if self.is_complete(cleaned_buffer):
333 if self.is_complete(cleaned_buffer):
334 self.execute(cleaned_buffer, raw_string=current_buffer)
334 self.execute(cleaned_buffer, raw_string=current_buffer)
335 return True
335 return True
336 else:
336 else:
337 # Start a new line.
337 new_line_pos = -new_line_pos
338 new_line_pos = -new_line_pos
338 lines = current_buffer.split('\n')[:-1]
339 lines = current_buffer.split('\n')[:-1]
339 prompt_less_lines = prompt_less_buffer.split('\n')
340 prompt_less_lines = prompt_less_buffer.split('\n')
341 # Create the new line, with the continuation prompt, and the
342 # same amount of indent than the line above it.
340 new_line = self.continuation_prompt() + \
343 new_line = self.continuation_prompt() + \
341 self._get_indent_string('\n'.join(
344 self._get_indent_string('\n'.join(
342 prompt_less_lines[:new_line_pos-1]))
345 prompt_less_lines[:new_line_pos-1]))
343 if len(lines) == 1:
346 if len(lines) == 1:
347 # We are starting a first continuation line. Indent it.
344 new_line += '\t'
348 new_line += '\t'
345 elif current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
349 elif current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
350 # The last line ends with ":", autoindent the new line.
346 new_line += '\t'
351 new_line += '\t'
347
352
348 if new_line_pos == 0:
353 if new_line_pos == 0:
349 lines.append(new_line)
354 lines.append(new_line)
350 else:
355 else:
351 lines.insert(new_line_pos, new_line)
356 lines.insert(new_line_pos, new_line)
352 self.input_buffer = '\n'.join(lines)
357 self.input_buffer = '\n'.join(lines)
353
358
354
359
355 def _get_indent_string(self, string):
360 def _get_indent_string(self, string):
356 """ Return the string of whitespace that prefixes a line. Used to
361 """ Return the string of whitespace that prefixes a line. Used to
357 add the right amount of indendation when creating a new line.
362 add the right amount of indendation when creating a new line.
358 """
363 """
359 string = string.replace('\t', ' '*4)
364 string = string.replace('\t', ' '*4)
360 string = string.split('\n')[-1]
365 string = string.split('\n')[-1]
361 indent_chars = len(string) - len(string.lstrip())
366 indent_chars = len(string) - len(string.lstrip())
362 indent_string = '\t'*(indent_chars // 4) + \
367 indent_string = '\t'*(indent_chars // 4) + \
363 ' '*(indent_chars % 4)
368 ' '*(indent_chars % 4)
364
369
365 return indent_string
370 return indent_string
366
371
367
372
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 NO CONTENT: file renamed from IPython/frontend/_process/killableprocess.py to IPython/frontend/process/killableprocess.py
NO CONTENT: file renamed from IPython/frontend/_process/killableprocess.py to IPython/frontend/process/killableprocess.py
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,38 +1,37 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Test the LineFrontEnd
3 Test the LineFrontEnd
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 IPython.frontend.linefrontendbase import LineFrontEndBase
15 from IPython.frontend.linefrontendbase import LineFrontEndBase
16 from copy import deepcopy
16 from copy import deepcopy
17 import nose.tools as nt
17
18
18 class ConcreteLineFrontEnd(LineFrontEndBase):
19 class ConcreteLineFrontEnd(LineFrontEndBase):
19 """ A concrete class to test the LineFrontEndBase.
20 """ A concrete class to test the LineFrontEndBase.
20 """
21 """
21 def capture_output(self):
22 def capture_output(self):
22 pass
23 pass
23
24
24 def release_output(self):
25 def release_output(self):
25 pass
26 pass
26
27
27
28
28 def test_is_complete():
29 def test_is_complete():
29 """ Tests line completion heuristic.
30 """ Tests line completion heuristic.
30 """
31 """
31 frontend = ConcreteLineFrontEnd()
32 frontend = ConcreteLineFrontEnd()
32 assert not frontend.is_complete('for x in \\')
33 yield nt.assert_true, not frontend.is_complete('for x in \\')
33 assert not frontend.is_complete('for x in (1, ):')
34 yield nt.assert_true, not frontend.is_complete('for x in (1, ):')
34 assert frontend.is_complete('for x in (1, ):\n pass')
35 yield nt.assert_true, frontend.is_complete('for x in (1, ):\n pass')
35
36
36
37
37 if __name__ == '__main__':
38 test_is_complete()
@@ -1,244 +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
18 from nose.tools import assert_equal
19
19
20 from IPython.ipapi import get as get_ipython0
20 from IPython.ipapi import get as get_ipython0
21 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
21 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
22 from copy import copy, deepcopy
22 from copy import copy, deepcopy
23
23
24 def safe_deepcopy(d):
24 def safe_deepcopy(d):
25 """ Deep copy every key of the given dict, when possible. Elsewhere
25 """ Deep copy every key of the given dict, when possible. Elsewhere
26 do a copy.
26 do a copy.
27 """
27 """
28 copied_d = dict()
28 copied_d = dict()
29 for key, value in d.iteritems():
29 for key, value in d.iteritems():
30 try:
30 try:
31 copied_d[key] = deepcopy(value)
31 copied_d[key] = deepcopy(value)
32 except:
32 except:
33 try:
33 try:
34 copied_d[key] = copy(value)
34 copied_d[key] = copy(value)
35 except:
35 except:
36 copied_d[key] = value
36 copied_d[key] = value
37 return copied_d
37 return copied_d
38
38
39
39
40 class TestPrefilterFrontEnd(PrefilterFrontEnd):
40 class TestPrefilterFrontEnd(PrefilterFrontEnd):
41
41
42 input_prompt_template = string.Template('')
42 input_prompt_template = string.Template('')
43 output_prompt_template = string.Template('')
43 output_prompt_template = string.Template('')
44 banner = ''
44 banner = ''
45
45
46 def __init__(self):
46 def __init__(self):
47 self.out = StringIO()
47 self.out = StringIO()
48 PrefilterFrontEnd.__init__(self)
48 PrefilterFrontEnd.__init__(self)
49 # Some more code for isolation (yeah, crazy)
49 # Some more code for isolation (yeah, crazy)
50 self._on_enter()
50 self._on_enter()
51 self.out.flush()
51 self.out.flush()
52 self.out.reset()
52 self.out.reset()
53 self.out.truncate()
53 self.out.truncate()
54
54
55 def write(self, string, *args, **kwargs):
55 def write(self, string, *args, **kwargs):
56 self.out.write(string)
56 self.out.write(string)
57
57
58 def _on_enter(self):
58 def _on_enter(self):
59 self.input_buffer += '\n'
59 self.input_buffer += '\n'
60 PrefilterFrontEnd._on_enter(self)
60 PrefilterFrontEnd._on_enter(self)
61
61
62
62
63 def isolate_ipython0(func):
63 def isolate_ipython0(func):
64 """ Decorator to isolate execution that involves an iptyhon0.
64 """ Decorator to isolate execution that involves an iptyhon0.
65
65
66 Notes
66 Notes
67 ------
67 ------
68
68
69 Apply only to functions with no arguments. Nose skips functions
69 Apply only to functions with no arguments. Nose skips functions
70 with arguments.
70 with arguments.
71 """
71 """
72 def my_func():
72 def my_func():
73 iplib = get_ipython0()
73 iplib = get_ipython0()
74 if iplib is None:
74 if iplib is None:
75 return func()
75 return func()
76 ipython0 = iplib.IP
76 ipython0 = iplib.IP
77 global_ns = safe_deepcopy(ipython0.user_global_ns)
77 global_ns = safe_deepcopy(ipython0.user_global_ns)
78 user_ns = safe_deepcopy(ipython0.user_ns)
78 user_ns = safe_deepcopy(ipython0.user_ns)
79 try:
79 try:
80 out = func()
80 out = func()
81 finally:
81 finally:
82 ipython0.user_ns = user_ns
82 ipython0.user_ns = user_ns
83 ipython0.user_global_ns = global_ns
83 ipython0.user_global_ns = global_ns
84 # Undo the hack at creation of PrefilterFrontEnd
84 # Undo the hack at creation of PrefilterFrontEnd
85 from IPython import iplib
85 from IPython import iplib
86 iplib.InteractiveShell.isthreaded = False
86 iplib.InteractiveShell.isthreaded = False
87 return out
87 return out
88
88
89 my_func.__name__ = func.__name__
89 my_func.__name__ = func.__name__
90 return my_func
90 return my_func
91
91
92
92
93 @isolate_ipython0
93 @isolate_ipython0
94 def test_execution():
94 def test_execution():
95 """ Test execution of a command.
95 """ Test execution of a command.
96 """
96 """
97 f = TestPrefilterFrontEnd()
97 f = TestPrefilterFrontEnd()
98 f.input_buffer = 'print 1'
98 f.input_buffer = 'print 1'
99 f._on_enter()
99 f._on_enter()
100 out_value = f.out.getvalue()
100 out_value = f.out.getvalue()
101 assert_equal(out_value, '1\n')
101 assert_equal(out_value, '1\n')
102
102
103
103
104 @isolate_ipython0
104 @isolate_ipython0
105 def test_multiline():
105 def test_multiline():
106 """ Test execution of a multiline command.
106 """ Test execution of a multiline command.
107 """
107 """
108 f = TestPrefilterFrontEnd()
108 f = TestPrefilterFrontEnd()
109 f.input_buffer = 'if True:'
109 f.input_buffer = 'if True:'
110 f._on_enter()
110 f._on_enter()
111 f.input_buffer += 'print 1'
111 f.input_buffer += 'print 1'
112 f._on_enter()
112 f._on_enter()
113 out_value = f.out.getvalue()
113 out_value = f.out.getvalue()
114 assert_equal(out_value, '')
114 yield assert_equal, out_value, ''
115 f._on_enter()
115 f._on_enter()
116 out_value = f.out.getvalue()
116 out_value = f.out.getvalue()
117 assert_equal(out_value, '1\n')
117 yield assert_equal, out_value, '1\n'
118 f = TestPrefilterFrontEnd()
118 f = TestPrefilterFrontEnd()
119 f.input_buffer='(1 +'
119 f.input_buffer='(1 +'
120 f._on_enter()
120 f._on_enter()
121 f.input_buffer += '0)'
121 f.input_buffer += '0)'
122 f._on_enter()
122 f._on_enter()
123 out_value = f.out.getvalue()
123 out_value = f.out.getvalue()
124 assert_equal(out_value, '')
124 yield assert_equal, out_value, ''
125 f._on_enter()
125 f._on_enter()
126 out_value = f.out.getvalue()
126 out_value = f.out.getvalue()
127 assert_equal(out_value, '1\n')
127 yield assert_equal, out_value, '1\n'
128
128
129
129
130 @isolate_ipython0
130 @isolate_ipython0
131 def test_capture():
131 def test_capture():
132 """ Test the capture of output in different channels.
132 """ Test the capture of output in different channels.
133 """
133 """
134 # Test on the OS-level stdout, stderr.
134 # Test on the OS-level stdout, stderr.
135 f = TestPrefilterFrontEnd()
135 f = TestPrefilterFrontEnd()
136 f.input_buffer = \
136 f.input_buffer = \
137 '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()'
138 f._on_enter()
138 f._on_enter()
139 out_value = f.out.getvalue()
139 out_value = f.out.getvalue()
140 assert_equal(out_value, '1')
140 yield assert_equal, out_value, '1'
141 f = TestPrefilterFrontEnd()
141 f = TestPrefilterFrontEnd()
142 f.input_buffer = \
142 f.input_buffer = \
143 '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()'
144 f._on_enter()
144 f._on_enter()
145 out_value = f.out.getvalue()
145 out_value = f.out.getvalue()
146 assert_equal(out_value, '1')
146 yield assert_equal, out_value, '1'
147
147
148
148
149 @isolate_ipython0
149 @isolate_ipython0
150 def test_magic():
150 def test_magic():
151 """ Test the magic expansion and history.
151 """ Test the magic expansion and history.
152
152
153 This test is fairly fragile and will break when magics change.
153 This test is fairly fragile and will break when magics change.
154 """
154 """
155 f = TestPrefilterFrontEnd()
155 f = TestPrefilterFrontEnd()
156 f.input_buffer += '%who'
156 f.input_buffer += '%who'
157 f._on_enter()
157 f._on_enter()
158 out_value = f.out.getvalue()
158 out_value = f.out.getvalue()
159 assert_equal(out_value, 'Interactive namespace is empty.\n')
159 assert_equal(out_value, 'Interactive namespace is empty.\n')
160
160
161
161
162 @isolate_ipython0
162 @isolate_ipython0
163 def test_help():
163 def test_help():
164 """ Test object inspection.
164 """ Test object inspection.
165 """
165 """
166 f = TestPrefilterFrontEnd()
166 f = TestPrefilterFrontEnd()
167 f.input_buffer += "def f():"
167 f.input_buffer += "def f():"
168 f._on_enter()
168 f._on_enter()
169 f.input_buffer += "'foobar'"
169 f.input_buffer += "'foobar'"
170 f._on_enter()
170 f._on_enter()
171 f.input_buffer += "pass"
171 f.input_buffer += "pass"
172 f._on_enter()
172 f._on_enter()
173 f._on_enter()
173 f._on_enter()
174 f.input_buffer += "f?"
174 f.input_buffer += "f?"
175 f._on_enter()
175 f._on_enter()
176 assert 'traceback' not in f.last_result
176 assert 'traceback' not in f.last_result
177 ## XXX: ipython doctest magic breaks this. I have no clue why
177 ## XXX: ipython doctest magic breaks this. I have no clue why
178 #out_value = f.out.getvalue()
178 #out_value = f.out.getvalue()
179 #assert out_value.split()[-1] == 'foobar'
179 #assert out_value.split()[-1] == 'foobar'
180
180
181
181
182 @isolate_ipython0
182 @isolate_ipython0
183 def test_completion_simple():
183 def test_completion_simple():
184 """ Test command-line completion on trivial examples.
184 """ Test command-line completion on trivial examples.
185 """
185 """
186 f = TestPrefilterFrontEnd()
186 f = TestPrefilterFrontEnd()
187 f.input_buffer = 'zzza = 1'
187 f.input_buffer = 'zzza = 1'
188 f._on_enter()
188 f._on_enter()
189 f.input_buffer = 'zzzb = 2'
189 f.input_buffer = 'zzzb = 2'
190 f._on_enter()
190 f._on_enter()
191 f.input_buffer = 'zz'
191 f.input_buffer = 'zz'
192 f.complete_current_input()
192 f.complete_current_input()
193 out_value = f.out.getvalue()
193 out_value = f.out.getvalue()
194 assert_equal(out_value, '\nzzza zzzb ')
194 yield assert_equal, out_value, '\nzzza zzzb '
195 assert_equal(f.input_buffer, 'zzz')
195 yield assert_equal, f.input_buffer, 'zzz'
196
196
197
197
198 @isolate_ipython0
198 @isolate_ipython0
199 def test_completion_parenthesis():
199 def test_completion_parenthesis():
200 """ Test command-line completion when a parenthesis is open.
200 """ Test command-line completion when a parenthesis is open.
201 """
201 """
202 f = TestPrefilterFrontEnd()
202 f = TestPrefilterFrontEnd()
203 f.input_buffer = 'zzza = 1'
203 f.input_buffer = 'zzza = 1'
204 f._on_enter()
204 f._on_enter()
205 f.input_buffer = 'zzzb = 2'
205 f.input_buffer = 'zzzb = 2'
206 f._on_enter()
206 f._on_enter()
207 f.input_buffer = 'map(zz'
207 f.input_buffer = 'map(zz'
208 f.complete_current_input()
208 f.complete_current_input()
209 out_value = f.out.getvalue()
209 out_value = f.out.getvalue()
210 assert_equal(out_value, '\nzzza zzzb ')
210 yield assert_equal, out_value, '\nzzza zzzb '
211 assert_equal(f.input_buffer, 'map(zzz')
211 yield assert_equal, f.input_buffer, 'map(zzz'
212
212
213
213
214 @isolate_ipython0
214 @isolate_ipython0
215 def test_completion_indexing():
215 def test_completion_indexing():
216 """ Test command-line completion when indexing on objects.
216 """ Test command-line completion when indexing on objects.
217 """
217 """
218 f = TestPrefilterFrontEnd()
218 f = TestPrefilterFrontEnd()
219 f.input_buffer = 'a = [0]'
219 f.input_buffer = 'a = [0]'
220 f._on_enter()
220 f._on_enter()
221 f.input_buffer = 'a[0].'
221 f.input_buffer = 'a[0].'
222 f.complete_current_input()
222 f.complete_current_input()
223 assert_equal(f.input_buffer, 'a[0].__')
223 assert_equal(f.input_buffer, 'a[0].__')
224
224
225
225
226 @isolate_ipython0
226 @isolate_ipython0
227 def test_completion_equal():
227 def test_completion_equal():
228 """ Test command-line completion when the delimiter is "=", not " ".
228 """ Test command-line completion when the delimiter is "=", not " ".
229 """
229 """
230 f = TestPrefilterFrontEnd()
230 f = TestPrefilterFrontEnd()
231 f.input_buffer = 'a=1.'
231 f.input_buffer = 'a=1.'
232 f.complete_current_input()
232 f.complete_current_input()
233 assert_equal(f.input_buffer, 'a=1.__')
233 assert_equal(f.input_buffer, 'a=1.__')
234
234
235
235
236
236
237 if __name__ == '__main__':
237 if __name__ == '__main__':
238 test_magic()
238 test_magic()
239 test_help()
239 test_help()
240 test_execution()
240 test_execution()
241 test_multiline()
241 test_multiline()
242 test_capture()
242 test_capture()
243 test_completion_simple()
243 test_completion_simple()
244 test_completion_complex()
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,656 +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
28 import string
29
29
30 LINESEP = '\n'
30 LINESEP = '\n'
31 if sys.platform == 'win32':
31 if sys.platform == 'win32':
32 LINESEP = '\n\r'
32 LINESEP = '\n\r'
33
33
34 import re
34 import re
35
35
36 # 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
37 # screen: this should not be editable by the user.
37 # screen: this should not be editable by the user.
38 #-------------------------------------------------------------------------------
38 #-------------------------------------------------------------------------------
39 # Constants
39 # Constants
40 #-------------------------------------------------------------------------------
40 #-------------------------------------------------------------------------------
41 _COMPLETE_BUFFER_MARKER = 31
41 _COMPLETE_BUFFER_MARKER = 31
42 _ERROR_MARKER = 30
42 _ERROR_MARKER = 30
43 _INPUT_MARKER = 29
43 _INPUT_MARKER = 29
44
44
45 _DEFAULT_SIZE = 10
45 _DEFAULT_SIZE = 10
46 if sys.platform == 'darwin':
46 if sys.platform == 'darwin':
47 _DEFAULT_SIZE = 12
47 _DEFAULT_SIZE = 12
48
48
49 _DEFAULT_STYLE = {
49 _DEFAULT_STYLE = {
50 #background definition
50 #background definition
51 'default' : 'size:%d' % _DEFAULT_SIZE,
51 'default' : 'size:%d' % _DEFAULT_SIZE,
52 'bracegood' : 'fore:#00AA00,back:#000000,bold',
52 'bracegood' : 'fore:#00AA00,back:#000000,bold',
53 'bracebad' : 'fore:#FF0000,back:#000000,bold',
53 'bracebad' : 'fore:#FF0000,back:#000000,bold',
54
54
55 # Edge column: a number of None
55 # Edge column: a number of None
56 'edge_column' : -1,
56 'edge_column' : -1,
57
57
58 # properties for the various Python lexer styles
58 # properties for the various Python lexer styles
59 'comment' : 'fore:#007F00',
59 'comment' : 'fore:#007F00',
60 'number' : 'fore:#007F7F',
60 'number' : 'fore:#007F7F',
61 'string' : 'fore:#7F007F,italic',
61 'string' : 'fore:#7F007F,italic',
62 'char' : 'fore:#7F007F,italic',
62 'char' : 'fore:#7F007F,italic',
63 'keyword' : 'fore:#00007F,bold',
63 'keyword' : 'fore:#00007F,bold',
64 'triple' : 'fore:#7F0000',
64 'triple' : 'fore:#7F0000',
65 'tripledouble' : 'fore:#7F0000',
65 'tripledouble' : 'fore:#7F0000',
66 'class' : 'fore:#0000FF,bold,underline',
66 'class' : 'fore:#0000FF,bold,underline',
67 'def' : 'fore:#007F7F,bold',
67 'def' : 'fore:#007F7F,bold',
68 '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',
69 }
86 }
70
87
71 # new style numbers
88 # new style numbers
72 _STDOUT_STYLE = 15
89 _STDOUT_STYLE = 15
73 _STDERR_STYLE = 16
90 _STDERR_STYLE = 16
74 _TRACE_STYLE = 17
91 _TRACE_STYLE = 17
75
92
76
93
77 # system colors
94 # system colors
78 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
95 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
79
96
80 # Translation table from ANSI escape sequences to color.
97 # Translation table from ANSI escape sequences to color.
81 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
98 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
82 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
99 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
83 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
100 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
84 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
101 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
85 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
102 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
86 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
103 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
87 '1;34': [12, 'LIGHT BLUE'], '1;35':
104 '1;34': [12, 'LIGHT BLUE'], '1;35':
88 [13, 'MEDIUM VIOLET RED'],
105 [13, 'MEDIUM VIOLET RED'],
89 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
106 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
90
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
91 #we define platform specific fonts
111 #we define platform specific fonts
92 if wx.Platform == '__WXMSW__':
112 if wx.Platform == '__WXMSW__':
93 FACES = { 'times': 'Times New Roman',
113 FACES = { 'times': 'Times New Roman',
94 'mono' : 'Courier New',
114 'mono' : 'Courier New',
95 'helv' : 'Arial',
115 'helv' : 'Arial',
96 'other': 'Comic Sans MS',
116 'other': 'Comic Sans MS',
97 'size' : 10,
117 'size' : 10,
98 'size2': 8,
118 'size2': 8,
99 }
119 }
100 elif wx.Platform == '__WXMAC__':
120 elif wx.Platform == '__WXMAC__':
101 FACES = { 'times': 'Times New Roman',
121 FACES = { 'times': 'Times New Roman',
102 'mono' : 'Monaco',
122 'mono' : 'Monaco',
103 'helv' : 'Arial',
123 'helv' : 'Arial',
104 'other': 'Comic Sans MS',
124 'other': 'Comic Sans MS',
105 'size' : 10,
125 'size' : 10,
106 'size2': 8,
126 'size2': 8,
107 }
127 }
108 else:
128 else:
109 FACES = { 'times': 'Times',
129 FACES = { 'times': 'Times',
110 'mono' : 'Courier',
130 'mono' : 'Courier',
111 'helv' : 'Helvetica',
131 'helv' : 'Helvetica',
112 'other': 'new century schoolbook',
132 'other': 'new century schoolbook',
113 'size' : 10,
133 'size' : 10,
114 'size2': 8,
134 'size2': 8,
115 }
135 }
116
136
117
137
118 #-------------------------------------------------------------------------------
138 #-------------------------------------------------------------------------------
119 # The console widget class
139 # The console widget class
120 #-------------------------------------------------------------------------------
140 #-------------------------------------------------------------------------------
121 class ConsoleWidget(editwindow.EditWindow):
141 class ConsoleWidget(editwindow.EditWindow):
122 """ Specialized styled text control view for console-like workflow.
142 """ Specialized styled text control view for console-like workflow.
123
143
124 This widget is mainly interested in dealing with the prompt and
144 This widget is mainly interested in dealing with the prompt and
125 keeping the cursor inside the editing line.
145 keeping the cursor inside the editing line.
126 """
146 """
127
147
128 # 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
129 # stored.
149 # stored.
130 title = 'Console'
150 title = 'Console'
131
151
132 # Last prompt printed
152 # Last prompt printed
133 last_prompt = ''
153 last_prompt = ''
134
154
135 # The buffer being edited.
155 # The buffer being edited.
136 def _set_input_buffer(self, string):
156 def _set_input_buffer(self, string):
137 self.SetSelection(self.current_prompt_pos, self.GetLength())
157 self.SetSelection(self.current_prompt_pos, self.GetLength())
138 self.ReplaceSelection(string)
158 self.ReplaceSelection(string)
139 self.GotoPos(self.GetLength())
159 self.GotoPos(self.GetLength())
140
160
141 def _get_input_buffer(self):
161 def _get_input_buffer(self):
142 """ Returns the text in current edit buffer.
162 """ Returns the text in current edit buffer.
143 """
163 """
144 input_buffer = self.GetTextRange(self.current_prompt_pos,
164 input_buffer = self.GetTextRange(self.current_prompt_pos,
145 self.GetLength())
165 self.GetLength())
146 input_buffer = input_buffer.replace(LINESEP, '\n')
166 input_buffer = input_buffer.replace(LINESEP, '\n')
147 return input_buffer
167 return input_buffer
148
168
149 input_buffer = property(_get_input_buffer, _set_input_buffer)
169 input_buffer = property(_get_input_buffer, _set_input_buffer)
150
170
151 style = _DEFAULT_STYLE.copy()
171 style = _DEFAULT_STYLE.copy()
152
172
153 # Translation table from ANSI escape sequences to color. Override
173 # Translation table from ANSI escape sequences to color. Override
154 # this to specify your colors.
174 # this to specify your colors.
155 ANSI_STYLES = ANSI_STYLES.copy()
175 ANSI_STYLES = ANSI_STYLES.copy()
156
176
157 # Font faces
177 # Font faces
158 faces = FACES.copy()
178 faces = FACES.copy()
159
179
160 # Store the last time a refresh was done
180 # Store the last time a refresh was done
161 _last_refresh_time = 0
181 _last_refresh_time = 0
162
182
163 #--------------------------------------------------------------------------
183 #--------------------------------------------------------------------------
164 # Public API
184 # Public API
165 #--------------------------------------------------------------------------
185 #--------------------------------------------------------------------------
166
186
167 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
187 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
168 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
188 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
169 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
189 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
170 self.configure_scintilla()
190 self.configure_scintilla()
171 # Track if 'enter' key as ever been processed
191 # Track if 'enter' key as ever been processed
172 # This variable will only be reallowed until key goes up
192 # This variable will only be reallowed until key goes up
173 self.enter_catched = False
193 self.enter_catched = False
174 self.current_prompt_pos = 0
194 self.current_prompt_pos = 0
175
195
176 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
196 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
177 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
197 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
178
198
179
199
180 def write(self, text, refresh=True):
200 def write(self, text, refresh=True):
181 """ Write given text to buffer, while translating the ansi escape
201 """ Write given text to buffer, while translating the ansi escape
182 sequences.
202 sequences.
183 """
203 """
184 # 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
185 # this method, the print statements will call this method, as
205 # this method, the print statements will call this method, as
186 # you will end up with an infinit loop
206 # you will end up with an infinit loop
187 title = self.title_pat.split(text)
207 title = self.title_pat.split(text)
188 if len(title)>1:
208 if len(title)>1:
189 self.title = title[-2]
209 self.title = title[-2]
190
210
191 text = self.title_pat.sub('', text)
211 text = self.title_pat.sub('', text)
192 segments = self.color_pat.split(text)
212 segments = self.color_pat.split(text)
193 segment = segments.pop(0)
213 segment = segments.pop(0)
194 self.GotoPos(self.GetLength())
214 self.GotoPos(self.GetLength())
195 self.StartStyling(self.GetLength(), 0xFF)
215 self.StartStyling(self.GetLength(), 0xFF)
196 try:
216 try:
197 self.AppendText(segment)
217 self.AppendText(segment)
198 except UnicodeDecodeError:
218 except UnicodeDecodeError:
199 # XXX: Do I really want to skip the exception?
219 # XXX: Do I really want to skip the exception?
200 pass
220 pass
201
221
202 if segments:
222 if segments:
203 for ansi_tag, text in zip(segments[::2], segments[1::2]):
223 for ansi_tag, text in zip(segments[::2], segments[1::2]):
204 self.StartStyling(self.GetLength(), 0xFF)
224 self.StartStyling(self.GetLength(), 0xFF)
205 try:
225 try:
206 self.AppendText(text)
226 self.AppendText(text)
207 except UnicodeDecodeError:
227 except UnicodeDecodeError:
208 # XXX: Do I really want to skip the exception?
228 # XXX: Do I really want to skip the exception?
209 pass
229 pass
210
230
211 if ansi_tag not in self.ANSI_STYLES:
231 if ansi_tag not in self.ANSI_STYLES:
212 style = 0
232 style = 0
213 else:
233 else:
214 style = self.ANSI_STYLES[ansi_tag][0]
234 style = self.ANSI_STYLES[ansi_tag][0]
215
235
216 self.SetStyling(len(text), style)
236 self.SetStyling(len(text), style)
217
237
218 self.GotoPos(self.GetLength())
238 self.GotoPos(self.GetLength())
219 if refresh:
239 if refresh:
220 current_time = time.time()
240 current_time = time.time()
221 if current_time - self._last_refresh_time > 0.03:
241 if current_time - self._last_refresh_time > 0.03:
222 if sys.platform == 'win32':
242 if sys.platform == 'win32':
223 wx.SafeYield()
243 wx.SafeYield()
224 else:
244 else:
225 wx.Yield()
245 wx.Yield()
226 # self.ProcessEvent(wx.PaintEvent())
246 # self.ProcessEvent(wx.PaintEvent())
227 self._last_refresh_time = current_time
247 self._last_refresh_time = current_time
228
248
229
249
230 def new_prompt(self, prompt):
250 def new_prompt(self, prompt):
231 """ 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
232 current block there.
252 current block there.
233
253
234 The prompt can be given with ascii escape sequences.
254 The prompt can be given with ascii escape sequences.
235 """
255 """
236 self.write(prompt, refresh=False)
256 self.write(prompt, refresh=False)
237 # now we update our cursor giving end of prompt
257 # now we update our cursor giving end of prompt
238 self.current_prompt_pos = self.GetLength()
258 self.current_prompt_pos = self.GetLength()
239 self.current_prompt_line = self.GetCurrentLine()
259 self.current_prompt_line = self.GetCurrentLine()
240 self.EnsureCaretVisible()
260 self.EnsureCaretVisible()
241 self.last_prompt = prompt
261 self.last_prompt = prompt
242
262
243
263
244 def continuation_prompt(self):
264 def continuation_prompt(self):
245 """ Returns the current continuation prompt.
265 """ Returns the current continuation prompt.
246 We need to implement this method here to deal with the
266 We need to implement this method here to deal with the
247 ascii escape sequences cleaning up.
267 ascii escape sequences cleaning up.
248 """
268 """
249 # ASCII-less prompt
269 # ASCII-less prompt
250 ascii_less = ''.join(self.color_pat.split(self.last_prompt)[2::2])
270 ascii_less = ''.join(self.color_pat.split(self.last_prompt)[2::2])
251 return "."*(len(ascii_less)-2) + ': '
271 return "."*(len(ascii_less)-2) + ': '
252
272
253
273
254 def scroll_to_bottom(self):
274 def scroll_to_bottom(self):
255 maxrange = self.GetScrollRange(wx.VERTICAL)
275 maxrange = self.GetScrollRange(wx.VERTICAL)
256 self.ScrollLines(maxrange)
276 self.ScrollLines(maxrange)
257
277
258
278
259 def pop_completion(self, possibilities, offset=0):
279 def pop_completion(self, possibilities, offset=0):
260 """ Pops up an autocompletion menu. Offset is the offset
280 """ Pops up an autocompletion menu. Offset is the offset
261 in characters of the position at which the menu should
281 in characters of the position at which the menu should
262 appear, relativ to the cursor.
282 appear, relativ to the cursor.
263 """
283 """
264 self.AutoCompSetIgnoreCase(False)
284 self.AutoCompSetIgnoreCase(False)
265 self.AutoCompSetAutoHide(False)
285 self.AutoCompSetAutoHide(False)
266 self.AutoCompSetMaxHeight(len(possibilities))
286 self.AutoCompSetMaxHeight(len(possibilities))
267 self.AutoCompShow(offset, " ".join(possibilities))
287 self.AutoCompShow(offset, " ".join(possibilities))
268
288
269
289
270 def get_line_width(self):
290 def get_line_width(self):
271 """ Return the width of the line in characters.
291 """ Return the width of the line in characters.
272 """
292 """
273 return self.GetSize()[0]/self.GetCharWidth()
293 return self.GetSize()[0]/self.GetCharWidth()
274
294
275
295
276 def configure_scintilla(self):
296 def configure_scintilla(self):
277
297 """ Set up all the styling option of the embedded scintilla
278 p = self.style
298 widget.
299 """
300 p = self.style.copy()
279
301
280 #First we define the special background colors
281 if 'trace' in p:
282 _COMPLETE_BUFFER_BG = p['trace']
283 else:
284 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
285
286 if 'stdout' in p:
287 _INPUT_BUFFER_BG = p['stdout']
288 else:
289 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
290
291 if 'stderr' in p:
292 _ERROR_BG = p['stderr']
293 else:
294 _ERROR_BG = '#FFF1F1' # Nice red
295
296 # Marker for complete buffer.
302 # Marker for complete buffer.
297 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
303 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
298 background = _COMPLETE_BUFFER_BG)
304 background=p['trace'])
299
305
300 # Marker for current input buffer.
306 # Marker for current input buffer.
301 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
307 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
302 background = _INPUT_BUFFER_BG)
308 background=p['stdout'])
303 # Marker for tracebacks.
309 # Marker for tracebacks.
304 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
310 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
305 background = _ERROR_BG)
311 background=p['stderr'])
306
312
307 self.SetEOLMode(stc.STC_EOL_LF)
313 self.SetEOLMode(stc.STC_EOL_LF)
308
314
309 # 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
310 # the widget
316 # the widget
311 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
317 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
312 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
318 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
313 # Also allow Ctrl Shift "=" for poor non US keyboard users.
319 # Also allow Ctrl Shift "=" for poor non US keyboard users.
314 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
320 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
315 stc.STC_CMD_ZOOMIN)
321 stc.STC_CMD_ZOOMIN)
316
322
317 # 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
318 # well with a console.
324 # well with a console.
319 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
325 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
320 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
326 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
321 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
327 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
322 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
328 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
323
329
324 self.SetEOLMode(stc.STC_EOL_CRLF)
330 self.SetEOLMode(stc.STC_EOL_CRLF)
325 self.SetWrapMode(stc.STC_WRAP_CHAR)
331 self.SetWrapMode(stc.STC_WRAP_CHAR)
326 self.SetWrapMode(stc.STC_WRAP_WORD)
332 self.SetWrapMode(stc.STC_WRAP_WORD)
327 self.SetBufferedDraw(True)
333 self.SetBufferedDraw(True)
328
334
329 if 'antialiasing' in p:
335 self.SetUseAntiAliasing(p['antialiasing'])
330 self.SetUseAntiAliasing(p['antialiasing'])
331 else:
332 self.SetUseAntiAliasing(True)
333
336
334 self.SetLayoutCache(stc.STC_CACHE_PAGE)
337 self.SetLayoutCache(stc.STC_CACHE_PAGE)
335 self.SetUndoCollection(False)
338 self.SetUndoCollection(False)
336 self.SetUseTabs(True)
339 self.SetUseTabs(True)
337 self.SetIndent(4)
340 self.SetIndent(4)
338 self.SetTabWidth(4)
341 self.SetTabWidth(4)
339
342
340 # we don't want scintilla's autocompletion to choose
343 # we don't want scintilla's autocompletion to choose
341 # 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
342 # automaticaly
345 # automaticaly
343 self.AutoCompSetChooseSingle(False)
346 self.AutoCompSetChooseSingle(False)
344 self.AutoCompSetMaxHeight(10)
347 self.AutoCompSetMaxHeight(10)
345 # XXX: this doesn't seem to have an effect.
348 # XXX: this doesn't seem to have an effect.
346 self.AutoCompSetFillUps('\n')
349 self.AutoCompSetFillUps('\n')
347
350
348 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
349 # Suppressing Scintilla margins
352 # Suppressing Scintilla margins
350 self.SetMarginWidth(0, 0)
353 self.SetMarginWidth(0, 0)
351 self.SetMarginWidth(1, 0)
354 self.SetMarginWidth(1, 0)
352 self.SetMarginWidth(2, 0)
355 self.SetMarginWidth(2, 0)
353
356
354 # Xterm escape sequences
357 # Xterm escape sequences
355 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
358 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
356 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
359 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
357
360
358 # styles
361 # styles
359
362
360 if 'carret_color' in p:
363 self.SetCaretForeground(p['carret_color'])
361 self.SetCaretForeground(p['carret_color'])
362 else:
363 self.SetCaretForeground('BLACK')
364
364
365 if 'background_color' in p:
365 background_color = p['background_color']
366 background_color = p['background_color']
367 else:
368 background_color = 'WHITE'
369
366
370 if 'default' in p:
367 if 'default' in p:
371 if 'back' not in p['default']:
368 if 'back' not in p['default']:
372 p['default'] += ',back:%s' % background_color
369 p['default'] += ',back:%s' % background_color
373 if 'size' not in p['default']:
370 if 'size' not in p['default']:
374 p['default'] += ',size:%s' % self.faces['size']
371 p['default'] += ',size:%s' % self.faces['size']
375 if 'face' not in p['default']:
372 if 'face' not in p['default']:
376 p['default'] += ',face:%s' % self.faces['mono']
373 p['default'] += ',face:%s' % self.faces['mono']
377
374
378 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
375 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
379 else:
376 else:
380 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
377 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
381 "fore:%s,back:%s,size:%d,face:%s"
378 "fore:%s,back:%s,size:%d,face:%s"
382 % (self.ANSI_STYLES['0;30'][1],
379 % (self.ANSI_STYLES['0;30'][1],
383 background_color,
380 background_color,
384 self.faces['size'], self.faces['mono']))
381 self.faces['size'], self.faces['mono']))
385
382
386 #all styles = default one
387 self.StyleClearAll()
383 self.StyleClearAll()
388
384
389 # XXX: two lines below are usefull if not using the lexer
385 # XXX: two lines below are usefull if not using the lexer
390 #for style in self.ANSI_STYLES.values():
386 #for style in self.ANSI_STYLES.values():
391 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
387 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
392
388
393 #prompt definition
389 # prompt definition
394 if 'prompt_in1' in p:
390 self.prompt_in1 = p['prompt_in1']
395 self.prompt_in1 = p['prompt_in1']
391 self.prompt_out = p['prompt_out']
396 else:
397 self.prompt_in1 = \
398 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
399
400 if 'prompt_out' in p:
401 self.prompt_out = p['prompt_out']
402 else:
403 self.prompt_out = \
404 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
405
392
406 self.output_prompt_template = string.Template(self.prompt_out)
393 self.output_prompt_template = string.Template(self.prompt_out)
407 self.input_prompt_template = string.Template(self.prompt_in1)
394 self.input_prompt_template = string.Template(self.prompt_in1)
408
395
409 if 'stdout' in p:
396 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
410 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
397 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
411 if 'stderr' in p:
398 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
412 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
399 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
413 if 'trace' in p:
400 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
414 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
401 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
415 if 'bracegood' in p:
402 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
416 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
403 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
417 if 'bracebad' in p:
404 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
418 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
405 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
419 if 'comment' in p:
406 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
420 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
407 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
421 if 'number' in p:
408 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
422 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
409 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
423 if 'string' in p:
410 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
424 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
411 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
425 if 'char' in p:
412 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
426 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
413
427 if 'keyword' in p:
414 edge_column = p['edge_column']
428 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
415 if edge_column is not None and edge_column > 0:
429 if 'keyword' in p:
416 #we add a vertical line to console widget
430 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
417 self.SetEdgeMode(stc.STC_EDGE_LINE)
431 if 'triple' in p:
418 self.SetEdgeColumn(edge_column)
432 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
433 if 'tripledouble' in p:
434 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
435 if 'class' in p:
436 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
437 if 'def' in p:
438 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
439 if 'operator' in p:
440 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
441 if 'comment' in p:
442 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
443
444 if 'edge_column' in p:
445 edge_column = p['edge_column']
446 if edge_column is not None and edge_column > 0:
447 #we add a vertical line to console widget
448 self.SetEdgeMode(stc.STC_EDGE_LINE)
449 self.SetEdgeColumn(88)
450
419
451
420
452 #--------------------------------------------------------------------------
421 #--------------------------------------------------------------------------
453 # EditWindow API
422 # EditWindow API
454 #--------------------------------------------------------------------------
423 #--------------------------------------------------------------------------
455
424
456 def OnUpdateUI(self, event):
425 def OnUpdateUI(self, event):
457 """ Override the OnUpdateUI of the EditWindow class, to prevent
426 """ Override the OnUpdateUI of the EditWindow class, to prevent
458 syntax highlighting both for faster redraw, and for more
427 syntax highlighting both for faster redraw, and for more
459 consistent look and feel.
428 consistent look and feel.
460 """
429 """
461
430
462
431
463 #--------------------------------------------------------------------------
432 #--------------------------------------------------------------------------
464 # Private API
433 # Private API
465 #--------------------------------------------------------------------------
434 #--------------------------------------------------------------------------
466
435
467 def _on_key_down(self, event, skip=True):
436 def _on_key_down(self, event, skip=True):
468 """ Key press callback used for correcting behavior for
437 """ Key press callback used for correcting behavior for
469 console-like interfaces: the cursor is constraint to be after
438 console-like interfaces: the cursor is constraint to be after
470 the last prompt.
439 the last prompt.
471
440
472 Return True if event as been catched.
441 Return True if event as been catched.
473 """
442 """
474 catched = True
443 catched = True
475 # XXX: Would the right way to do this be to have a
444 # XXX: Would the right way to do this be to have a
476 # dictionary at the instance level associating keys with
445 # dictionary at the instance level associating keys with
477 # callbacks? How would we deal with inheritance? And Do the
446 # callbacks? How would we deal with inheritance? And Do the
478 # different callbacks share local variables?
447 # different callbacks share local variables?
479
448
480 # Intercept some specific keys.
449 # Intercept some specific keys.
481 if event.KeyCode == ord('L') and event.ControlDown() :
450 if event.KeyCode == ord('L') and event.ControlDown() :
482 self.scroll_to_bottom()
451 self.scroll_to_bottom()
483 elif event.KeyCode == ord('K') and event.ControlDown() :
452 elif event.KeyCode == ord('K') and event.ControlDown() :
484 self.input_buffer = ''
453 self.input_buffer = ''
485 elif event.KeyCode == ord('A') and event.ControlDown() :
454 elif event.KeyCode == ord('A') and event.ControlDown() :
486 self.GotoPos(self.GetLength())
455 self.GotoPos(self.GetLength())
487 self.SetSelectionStart(self.current_prompt_pos)
456 self.SetSelectionStart(self.current_prompt_pos)
488 self.SetSelectionEnd(self.GetCurrentPos())
457 self.SetSelectionEnd(self.GetCurrentPos())
489 catched = True
458 catched = True
490 elif event.KeyCode == ord('E') and event.ControlDown() :
459 elif event.KeyCode == ord('E') and event.ControlDown() :
491 self.GotoPos(self.GetLength())
460 self.GotoPos(self.GetLength())
492 catched = True
461 catched = True
493 elif event.KeyCode == wx.WXK_PAGEUP:
462 elif event.KeyCode == wx.WXK_PAGEUP:
494 self.ScrollPages(-1)
463 self.ScrollPages(-1)
495 elif event.KeyCode == wx.WXK_PAGEDOWN:
464 elif event.KeyCode == wx.WXK_PAGEDOWN:
496 self.ScrollPages(1)
465 self.ScrollPages(1)
497 elif event.KeyCode == wx.WXK_HOME:
466 elif event.KeyCode == wx.WXK_HOME:
498 self.GotoPos(self.GetLength())
467 self.GotoPos(self.GetLength())
499 elif event.KeyCode == wx.WXK_END:
468 elif event.KeyCode == wx.WXK_END:
500 self.GotoPos(self.GetLength())
469 self.GotoPos(self.GetLength())
501 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
470 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
502 self.ScrollLines(-1)
471 self.ScrollLines(-1)
503 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
472 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
504 self.ScrollLines(1)
473 self.ScrollLines(1)
505 else:
474 else:
506 catched = False
475 catched = False
507
476
508 if self.AutoCompActive():
477 if self.AutoCompActive():
509 event.Skip()
478 event.Skip()
510 else:
479 else:
511 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
480 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
512 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN,
481 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN,
513 wx.MOD_SHIFT):
482 wx.MOD_SHIFT):
514 catched = True
483 catched = True
515 if not self.enter_catched:
484 if not self.enter_catched:
516 self.CallTipCancel()
485 self.CallTipCancel()
517 if event.Modifiers == wx.MOD_SHIFT:
486 if event.Modifiers == wx.MOD_SHIFT:
518 # Try to force execution
487 # Try to force execution
519 self.GotoPos(self.GetLength())
488 self.GotoPos(self.GetLength())
520 self.write('\n' + self.continuation_prompt(),
489 self.write('\n' + self.continuation_prompt(),
521 refresh=False)
490 refresh=False)
522 self._on_enter()
491 self._on_enter()
523 else:
492 else:
524 self._on_enter()
493 self._on_enter()
525 self.enter_catched = True
494 self.enter_catched = True
526
495
527 elif event.KeyCode == wx.WXK_HOME:
496 elif event.KeyCode == wx.WXK_HOME:
528 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
497 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
529 self.GotoPos(self.current_prompt_pos)
498 self.GotoPos(self.current_prompt_pos)
530 catched = True
499 catched = True
531
500
532 elif event.Modifiers == wx.MOD_SHIFT:
501 elif event.Modifiers == wx.MOD_SHIFT:
533 # FIXME: This behavior is not ideal: if the selection
502 # FIXME: This behavior is not ideal: if the selection
534 # is already started, it will jump.
503 # is already started, it will jump.
535 self.SetSelectionStart(self.current_prompt_pos)
504 self.SetSelectionStart(self.current_prompt_pos)
536 self.SetSelectionEnd(self.GetCurrentPos())
505 self.SetSelectionEnd(self.GetCurrentPos())
537 catched = True
506 catched = True
538
507
539 elif event.KeyCode == wx.WXK_UP:
508 elif event.KeyCode == wx.WXK_UP:
540 if self.GetCurrentLine() > self.current_prompt_line:
509 if self.GetCurrentLine() > self.current_prompt_line:
541 if self.GetCurrentLine() == self.current_prompt_line + 1 \
510 if self.GetCurrentLine() == self.current_prompt_line + 1 \
542 and self.GetColumn(self.GetCurrentPos()) < \
511 and self.GetColumn(self.GetCurrentPos()) < \
543 self.GetColumn(self.current_prompt_pos):
512 self.GetColumn(self.current_prompt_pos):
544 self.GotoPos(self.current_prompt_pos)
513 self.GotoPos(self.current_prompt_pos)
545 else:
514 else:
546 event.Skip()
515 event.Skip()
547 catched = True
516 catched = True
548
517
549 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
518 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
550 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
519 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
551 event.Skip()
520 event.Skip()
552 catched = True
521 catched = True
553
522
554 elif event.KeyCode == wx.WXK_RIGHT:
523 elif event.KeyCode == wx.WXK_RIGHT:
555 if not self._keep_cursor_in_buffer(self.GetCurrentPos() + 1):
524 if not self._keep_cursor_in_buffer(self.GetCurrentPos() + 1):
556 event.Skip()
525 event.Skip()
557 catched = True
526 catched = True
558
527
559
528
560 elif event.KeyCode == wx.WXK_DELETE:
529 elif event.KeyCode == wx.WXK_DELETE:
561 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
530 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
562 event.Skip()
531 event.Skip()
563 catched = True
532 catched = True
564
533
565 if skip and not catched:
534 if skip and not catched:
566 # Put the cursor back in the edit region
535 # Put the cursor back in the edit region
567 if not self._keep_cursor_in_buffer():
536 if not self._keep_cursor_in_buffer():
568 if not (self.GetCurrentPos() == self.GetLength()
537 if not (self.GetCurrentPos() == self.GetLength()
569 and event.KeyCode == wx.WXK_DELETE):
538 and event.KeyCode == wx.WXK_DELETE):
570 event.Skip()
539 event.Skip()
571 catched = True
540 catched = True
572
541
573 return catched
542 return catched
574
543
575
544
576 def _on_key_up(self, event, skip=True):
545 def _on_key_up(self, event, skip=True):
577 """ If cursor is outside the editing region, put it back.
546 """ If cursor is outside the editing region, put it back.
578 """
547 """
579 if skip:
548 if skip:
580 event.Skip()
549 event.Skip()
581 self._keep_cursor_in_buffer()
550 self._keep_cursor_in_buffer()
582
551
583
552
584 # XXX: I need to avoid the problem of having an empty glass;
553 # XXX: I need to avoid the problem of having an empty glass;
585 def _keep_cursor_in_buffer(self, pos=None):
554 def _keep_cursor_in_buffer(self, pos=None):
586 """ Checks if the cursor is where it is allowed to be. If not,
555 """ Checks if the cursor is where it is allowed to be. If not,
587 put it back.
556 put it back.
588
557
589 Returns
558 Returns
590 -------
559 -------
591 cursor_moved: Boolean
560 cursor_moved: Boolean
592 whether or not the cursor was moved by this routine.
561 whether or not the cursor was moved by this routine.
593
562
594 Notes
563 Notes
595 ------
564 ------
596 WARNING: This does proper checks only for horizontal
565 WARNING: This does proper checks only for horizontal
597 movements.
566 movements.
598 """
567 """
599 if pos is None:
568 if pos is None:
600 current_pos = self.GetCurrentPos()
569 current_pos = self.GetCurrentPos()
601 else:
570 else:
602 current_pos = pos
571 current_pos = pos
603 if current_pos < self.current_prompt_pos:
572 if current_pos < self.current_prompt_pos:
604 self.GotoPos(self.current_prompt_pos)
573 self.GotoPos(self.current_prompt_pos)
605 return True
574 return True
606 line_num = self.LineFromPosition(current_pos)
575 line_num = self.LineFromPosition(current_pos)
607 if not current_pos > self.GetLength():
576 if not current_pos > self.GetLength():
608 line_pos = self.GetColumn(current_pos)
577 line_pos = self.GetColumn(current_pos)
609 else:
578 else:
610 line_pos = self.GetColumn(self.GetLength())
579 line_pos = self.GetColumn(self.GetLength())
611 line = self.GetLine(line_num)
580 line = self.GetLine(line_num)
612 # Jump the continuation prompt
581 # Jump the continuation prompt
613 continuation_prompt = self.continuation_prompt()
582 continuation_prompt = self.continuation_prompt()
614 if ( line.startswith(continuation_prompt)
583 if ( line.startswith(continuation_prompt)
615 and line_pos < len(continuation_prompt)):
584 and line_pos < len(continuation_prompt)):
616 if line_pos < 2:
585 if line_pos < 2:
617 # We are at the beginning of the line, trying to move
586 # We are at the beginning of the line, trying to move
618 # forward: jump forward.
587 # forward: jump forward.
619 self.GotoPos(current_pos + 1 +
588 self.GotoPos(current_pos + 1 +
620 len(continuation_prompt) - line_pos)
589 len(continuation_prompt) - line_pos)
621 else:
590 else:
622 # Jump back up
591 # Jump back up
623 self.GotoPos(self.GetLineEndPosition(line_num-1))
592 self.GotoPos(self.GetLineEndPosition(line_num-1))
624 return True
593 return True
625 elif ( current_pos > self.GetLineEndPosition(line_num)
594 elif ( current_pos > self.GetLineEndPosition(line_num)
626 and not current_pos == self.GetLength()):
595 and not current_pos == self.GetLength()):
627 # Jump to next line
596 # Jump to next line
628 self.GotoPos(current_pos + 1 +
597 self.GotoPos(current_pos + 1 +
629 len(continuation_prompt))
598 len(continuation_prompt))
630 return True
599 return True
631
600
632 # We re-allow enter event processing
601 # We re-allow enter event processing
633 self.enter_catched = False
602 self.enter_catched = False
634 return False
603 return False
635
604
636
605
637 if __name__ == '__main__':
606 if __name__ == '__main__':
638 # Some simple code to test the console widget.
607 # Some simple code to test the console widget.
639 class MainWindow(wx.Frame):
608 class MainWindow(wx.Frame):
640 def __init__(self, parent, id, title):
609 def __init__(self, parent, id, title):
641 wx.Frame.__init__(self, parent, id, title, size=(300, 250))
610 wx.Frame.__init__(self, parent, id, title, size=(300, 250))
642 self._sizer = wx.BoxSizer(wx.VERTICAL)
611 self._sizer = wx.BoxSizer(wx.VERTICAL)
643 self.console_widget = ConsoleWidget(self)
612 self.console_widget = ConsoleWidget(self)
644 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
613 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
645 self.SetSizer(self._sizer)
614 self.SetSizer(self._sizer)
646 self.SetAutoLayout(1)
615 self.SetAutoLayout(1)
647 self.Show(True)
616 self.Show(True)
648
617
649 app = wx.PySimpleApp()
618 app = wx.PySimpleApp()
650 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
619 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
651 w.SetSize((780, 460))
620 w.SetSize((780, 460))
652 w.Show()
621 w.Show()
653
622
654 app.MainLoop()
623 app.MainLoop()
655
624
656
625
@@ -1,598 +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 import sys
28 import sys
29 from threading import Lock
29 from threading import Lock
30
30
31 import wx
31 import wx
32 from wx import stc
32 from wx import stc
33
33
34 # Ipython-specific imports.
34 # Ipython-specific imports.
35 from IPython.frontend._process import PipedProcess
35 from IPython.frontend.process import PipedProcess
36 from console_widget import ConsoleWidget, _COMPLETE_BUFFER_MARKER, \
36 from console_widget import ConsoleWidget, _COMPLETE_BUFFER_MARKER, \
37 _ERROR_MARKER, _INPUT_MARKER
37 _ERROR_MARKER, _INPUT_MARKER
38 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
38 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
39
39
40 #-------------------------------------------------------------------------------
40 #-------------------------------------------------------------------------------
41 # Classes to implement the Wx frontend
41 # Classes to implement the Wx frontend
42 #-------------------------------------------------------------------------------
42 #-------------------------------------------------------------------------------
43 class WxController(ConsoleWidget, PrefilterFrontEnd):
43 class WxController(ConsoleWidget, PrefilterFrontEnd):
44 """Classes to provide a Wx frontend to the
44 """Classes to provide a Wx frontend to the
45 IPython.kernel.core.interpreter.
45 IPython.kernel.core.interpreter.
46
46
47 This class inherits from ConsoleWidget, that provides a console-like
47 This class inherits from ConsoleWidget, that provides a console-like
48 widget to provide a text-rendering widget suitable for a terminal.
48 widget to provide a text-rendering widget suitable for a terminal.
49 """
49 """
50
50
51 # Print debug info on what is happening to the console.
51 # Print debug info on what is happening to the console.
52 debug = False
52 debug = False
53
53
54 # The title of the terminal, as captured through the ANSI escape
54 # The title of the terminal, as captured through the ANSI escape
55 # sequences.
55 # sequences.
56 def _set_title(self, title):
56 def _set_title(self, title):
57 return self.Parent.SetTitle(title)
57 return self.Parent.SetTitle(title)
58
58
59 def _get_title(self):
59 def _get_title(self):
60 return self.Parent.GetTitle()
60 return self.Parent.GetTitle()
61
61
62 title = property(_get_title, _set_title)
62 title = property(_get_title, _set_title)
63
63
64
64
65 # The buffer being edited.
65 # The buffer being edited.
66 # We are duplicating the definition here because of multiple
66 # We are duplicating the definition here because of multiple
67 # inheritence
67 # inheritence
68 def _set_input_buffer(self, string):
68 def _set_input_buffer(self, string):
69 ConsoleWidget._set_input_buffer(self, string)
69 ConsoleWidget._set_input_buffer(self, string)
70 self._colorize_input_buffer()
70 self._colorize_input_buffer()
71
71
72 def _get_input_buffer(self):
72 def _get_input_buffer(self):
73 """ Returns the text in current edit buffer.
73 """ Returns the text in current edit buffer.
74 """
74 """
75 return ConsoleWidget._get_input_buffer(self)
75 return ConsoleWidget._get_input_buffer(self)
76
76
77 input_buffer = property(_get_input_buffer, _set_input_buffer)
77 input_buffer = property(_get_input_buffer, _set_input_buffer)
78
78
79
79
80 #--------------------------------------------------------------------------
80 #--------------------------------------------------------------------------
81 # Private Attributes
81 # Private Attributes
82 #--------------------------------------------------------------------------
82 #--------------------------------------------------------------------------
83
83
84 # A flag governing the behavior of the input. Can be:
84 # A flag governing the behavior of the input. Can be:
85 #
85 #
86 # 'readline' for readline-like behavior with a prompt
86 # 'readline' for readline-like behavior with a prompt
87 # and an edit buffer.
87 # and an edit buffer.
88 # 'raw_input' similar to readline, but triggered by a raw-input
88 # 'raw_input' similar to readline, but triggered by a raw-input
89 # call. Can be used by subclasses to act differently.
89 # call. Can be used by subclasses to act differently.
90 # 'subprocess' for sending the raw input directly to a
90 # 'subprocess' for sending the raw input directly to a
91 # subprocess.
91 # subprocess.
92 # 'buffering' for buffering of the input, that will be used
92 # 'buffering' for buffering of the input, that will be used
93 # when the input state switches back to another state.
93 # when the input state switches back to another state.
94 _input_state = 'readline'
94 _input_state = 'readline'
95
95
96 # 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
97 # are running any.
97 # are running any.
98 _running_process = False
98 _running_process = False
99
99
100 # 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
101 # event loop
101 # event loop
102 _out_buffer = []
102 _out_buffer = []
103
103
104 # 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
105 # while it is being swapped
105 # while it is being swapped
106 _out_buffer_lock = Lock()
106 _out_buffer_lock = Lock()
107
107
108 # The different line markers used to higlight the prompts.
108 # The different line markers used to higlight the prompts.
109 _markers = dict()
109 _markers = dict()
110
110
111 #--------------------------------------------------------------------------
111 #--------------------------------------------------------------------------
112 # Public API
112 # Public API
113 #--------------------------------------------------------------------------
113 #--------------------------------------------------------------------------
114
114
115 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
115 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
116 size=wx.DefaultSize,
116 size=wx.DefaultSize,
117 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
117 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
118 styledef=None,
118 styledef=None,
119 *args, **kwds):
119 *args, **kwds):
120 """ Create Shell instance.
120 """ Create Shell instance.
121
121
122 Parameters
122 Parameters
123 -----------
123 -----------
124 styledef : dict, optional
124 styledef : dict, optional
125 styledef is the dictionary of options used to define the
125 styledef is the dictionary of options used to define the
126 style.
126 style.
127 """
127 """
128 if styledef is not None:
128 if styledef is not None:
129 self.style = styledef
129 self.style = styledef
130 ConsoleWidget.__init__(self, parent, id, pos, size, style)
130 ConsoleWidget.__init__(self, parent, id, pos, size, style)
131 PrefilterFrontEnd.__init__(self, **kwds)
131 PrefilterFrontEnd.__init__(self, **kwds)
132
132
133 # Stick in our own raw_input:
133 # Stick in our own raw_input:
134 self.ipython0.raw_input = self.raw_input
134 self.ipython0.raw_input = self.raw_input
135
135
136 # A time for flushing the write buffer
136 # A time for flushing the write buffer
137 BUFFER_FLUSH_TIMER_ID = 100
137 BUFFER_FLUSH_TIMER_ID = 100
138 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
138 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
139 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
139 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
140
140
141 if 'debug' in kwds:
141 if 'debug' in kwds:
142 self.debug = kwds['debug']
142 self.debug = kwds['debug']
143 kwds.pop('debug')
143 kwds.pop('debug')
144
144
145 # Inject self in namespace, for debug
145 # Inject self in namespace, for debug
146 if self.debug:
146 if self.debug:
147 self.shell.user_ns['self'] = self
147 self.shell.user_ns['self'] = self
148 # Inject our own raw_input in namespace
148 # Inject our own raw_input in namespace
149 self.shell.user_ns['raw_input'] = self.raw_input
149 self.shell.user_ns['raw_input'] = self.raw_input
150
150
151 def raw_input(self, prompt=''):
151 def raw_input(self, prompt=''):
152 """ A replacement from python's raw_input.
152 """ A replacement from python's raw_input.
153 """
153 """
154 self.new_prompt(prompt)
154 self.new_prompt(prompt)
155 self._input_state = 'raw_input'
155 self._input_state = 'raw_input'
156 if hasattr(self, '_cursor'):
156 if hasattr(self, '_cursor'):
157 del self._cursor
157 del self._cursor
158 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
158 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
159 self.__old_on_enter = self._on_enter
159 self.__old_on_enter = self._on_enter
160 event_loop = wx.EventLoop()
160 event_loop = wx.EventLoop()
161 def my_on_enter():
161 def my_on_enter():
162 event_loop.Exit()
162 event_loop.Exit()
163 self._on_enter = my_on_enter
163 self._on_enter = my_on_enter
164 # XXX: Running a separate event_loop. Ugly.
164 # XXX: Running a separate event_loop. Ugly.
165 event_loop.Run()
165 event_loop.Run()
166 self._on_enter = self.__old_on_enter
166 self._on_enter = self.__old_on_enter
167 self._input_state = 'buffering'
167 self._input_state = 'buffering'
168 self._cursor = wx.BusyCursor()
168 self._cursor = wx.BusyCursor()
169 return self.input_buffer.rstrip('\n')
169 return self.input_buffer.rstrip('\n')
170
170
171
171
172 def system_call(self, command_string):
172 def system_call(self, command_string):
173 self._input_state = 'subprocess'
173 self._input_state = 'subprocess'
174 event_loop = wx.EventLoop()
174 event_loop = wx.EventLoop()
175 def _end_system_call():
175 def _end_system_call():
176 self._input_state = 'buffering'
176 self._input_state = 'buffering'
177 self._running_process = False
177 self._running_process = False
178 event_loop.Exit()
178 event_loop.Exit()
179
179
180 self._running_process = PipedProcess(command_string,
180 self._running_process = PipedProcess(command_string,
181 out_callback=self.buffered_write,
181 out_callback=self.buffered_write,
182 end_callback = _end_system_call)
182 end_callback = _end_system_call)
183 self._running_process.start()
183 self._running_process.start()
184 # XXX: Running a separate event_loop. Ugly.
184 # XXX: Running a separate event_loop. Ugly.
185 event_loop.Run()
185 event_loop.Run()
186 # Be sure to flush the buffer.
186 # Be sure to flush the buffer.
187 self._buffer_flush(event=None)
187 self._buffer_flush(event=None)
188
188
189
189
190 def do_calltip(self):
190 def do_calltip(self):
191 """ Analyse current and displays useful calltip for it.
191 """ Analyse current and displays useful calltip for it.
192 """
192 """
193 if self.debug:
193 if self.debug:
194 print >>sys.__stdout__, "do_calltip"
194 print >>sys.__stdout__, "do_calltip"
195 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
195 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
196 symbol = self.input_buffer
196 symbol = self.input_buffer
197 symbol_string = separators.split(symbol)[-1]
197 symbol_string = separators.split(symbol)[-1]
198 base_symbol_string = symbol_string.split('.')[0]
198 base_symbol_string = symbol_string.split('.')[0]
199 if base_symbol_string in self.shell.user_ns:
199 if base_symbol_string in self.shell.user_ns:
200 symbol = self.shell.user_ns[base_symbol_string]
200 symbol = self.shell.user_ns[base_symbol_string]
201 elif base_symbol_string in self.shell.user_global_ns:
201 elif base_symbol_string in self.shell.user_global_ns:
202 symbol = self.shell.user_global_ns[base_symbol_string]
202 symbol = self.shell.user_global_ns[base_symbol_string]
203 elif base_symbol_string in __builtin__.__dict__:
203 elif base_symbol_string in __builtin__.__dict__:
204 symbol = __builtin__.__dict__[base_symbol_string]
204 symbol = __builtin__.__dict__[base_symbol_string]
205 else:
205 else:
206 return False
206 return False
207 try:
207 try:
208 for name in symbol_string.split('.')[1:] + ['__doc__']:
208 for name in symbol_string.split('.')[1:] + ['__doc__']:
209 symbol = getattr(symbol, name)
209 symbol = getattr(symbol, name)
210 self.AutoCompCancel()
210 self.AutoCompCancel()
211 # Check that the symbol can indeed be converted to a string:
211 # Check that the symbol can indeed be converted to a string:
212 symbol += ''
212 symbol += ''
213 wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol)
213 wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol)
214 except:
214 except:
215 # The retrieve symbol couldn't be converted to a string
215 # The retrieve symbol couldn't be converted to a string
216 pass
216 pass
217
217
218
218
219 def _popup_completion(self, create=False):
219 def _popup_completion(self, create=False):
220 """ Updates the popup completion menu if it exists. If create is
220 """ Updates the popup completion menu if it exists. If create is
221 true, open the menu.
221 true, open the menu.
222 """
222 """
223 if self.debug:
223 if self.debug:
224 print >>sys.__stdout__, "_popup_completion"
224 print >>sys.__stdout__, "_popup_completion"
225 line = self.input_buffer
225 line = self.input_buffer
226 if (self.AutoCompActive() and line and not line[-1] == '.') \
226 if (self.AutoCompActive() and line and not line[-1] == '.') \
227 or create==True:
227 or create==True:
228 suggestion, completions = self.complete(line)
228 suggestion, completions = self.complete(line)
229 if completions:
229 if completions:
230 offset = len(self._get_completion_text(line))
230 offset = len(self._get_completion_text(line))
231 self.pop_completion(completions, offset=offset)
231 self.pop_completion(completions, offset=offset)
232 if self.debug:
232 if self.debug:
233 print >>sys.__stdout__, completions
233 print >>sys.__stdout__, completions
234
234
235
235
236 def buffered_write(self, text):
236 def buffered_write(self, text):
237 """ A write method for streams, that caches the stream in order
237 """ A write method for streams, that caches the stream in order
238 to avoid flooding the event loop.
238 to avoid flooding the event loop.
239
239
240 This can be called outside of the main loop, in separate
240 This can be called outside of the main loop, in separate
241 threads.
241 threads.
242 """
242 """
243 self._out_buffer_lock.acquire()
243 self._out_buffer_lock.acquire()
244 self._out_buffer.append(text)
244 self._out_buffer.append(text)
245 self._out_buffer_lock.release()
245 self._out_buffer_lock.release()
246 if not self._buffer_flush_timer.IsRunning():
246 if not self._buffer_flush_timer.IsRunning():
247 wx.CallAfter(self._buffer_flush_timer.Start,
247 wx.CallAfter(self._buffer_flush_timer.Start,
248 milliseconds=100, oneShot=True)
248 milliseconds=100, oneShot=True)
249
249
250
250
251 def clear_screen(self):
251 def clear_screen(self):
252 """ Empty completely the widget.
252 """ Empty completely the widget.
253 """
253 """
254 self.ClearAll()
254 self.ClearAll()
255 self.new_prompt(self.input_prompt_template.substitute(
255 self.new_prompt(self.input_prompt_template.substitute(
256 number=(self.last_result['number'] + 1)))
256 number=(self.last_result['number'] + 1)))
257
257
258
258
259 #--------------------------------------------------------------------------
259 #--------------------------------------------------------------------------
260 # LineFrontEnd interface
260 # LineFrontEnd interface
261 #--------------------------------------------------------------------------
261 #--------------------------------------------------------------------------
262
262
263 def execute(self, python_string, raw_string=None):
263 def execute(self, python_string, raw_string=None):
264 self._input_state = 'buffering'
264 self._input_state = 'buffering'
265 self.CallTipCancel()
265 self.CallTipCancel()
266 self._cursor = wx.BusyCursor()
266 self._cursor = wx.BusyCursor()
267 if raw_string is None:
267 if raw_string is None:
268 raw_string = python_string
268 raw_string = python_string
269 end_line = self.current_prompt_line \
269 end_line = self.current_prompt_line \
270 + max(1, len(raw_string.split('\n'))-1)
270 + max(1, len(raw_string.split('\n'))-1)
271 for i in range(self.current_prompt_line, end_line):
271 for i in range(self.current_prompt_line, end_line):
272 if i in self._markers:
272 if i in self._markers:
273 self.MarkerDeleteHandle(self._markers[i])
273 self.MarkerDeleteHandle(self._markers[i])
274 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
274 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
275 # Use a callafter to update the display robustly under windows
275 # Use a callafter to update the display robustly under windows
276 def callback():
276 def callback():
277 self.GotoPos(self.GetLength())
277 self.GotoPos(self.GetLength())
278 PrefilterFrontEnd.execute(self, python_string,
278 PrefilterFrontEnd.execute(self, python_string,
279 raw_string=raw_string)
279 raw_string=raw_string)
280 wx.CallAfter(callback)
280 wx.CallAfter(callback)
281
281
282
282
283 def execute_command(self, command, hidden=False):
283 def execute_command(self, command, hidden=False):
284 """ Execute a command, not only in the model, but also in the
284 """ Execute a command, not only in the model, but also in the
285 view.
285 view.
286 """
286 """
287 # XXX: This method needs to be integrated in the base fronted
287 # XXX: This method needs to be integrated in the base fronted
288 # interface
288 # interface
289 if hidden:
289 if hidden:
290 return self.shell.execute(command)
290 return self.shell.execute(command)
291 else:
291 else:
292 # XXX: we are not storing the input buffer previous to the
292 # XXX: we are not storing the input buffer previous to the
293 # execution, as this forces us to run the execution
293 # execution, as this forces us to run the execution
294 # input_buffer a yield, which is not good.
294 # input_buffer a yield, which is not good.
295 ##current_buffer = self.shell.control.input_buffer
295 ##current_buffer = self.shell.control.input_buffer
296 command = command.rstrip()
296 command = command.rstrip()
297 if len(command.split('\n')) > 1:
297 if len(command.split('\n')) > 1:
298 # The input command is several lines long, we need to
298 # The input command is several lines long, we need to
299 # force the execution to happen
299 # force the execution to happen
300 command += '\n'
300 command += '\n'
301 cleaned_command = self.prefilter_input(command)
301 cleaned_command = self.prefilter_input(command)
302 self.input_buffer = command
302 self.input_buffer = command
303 # Do not use wx.Yield() (aka GUI.process_events()) to avoid
303 # Do not use wx.Yield() (aka GUI.process_events()) to avoid
304 # recursive yields.
304 # recursive yields.
305 self.ProcessEvent(wx.PaintEvent())
305 self.ProcessEvent(wx.PaintEvent())
306 self.write('\n')
306 self.write('\n')
307 if not self.is_complete(cleaned_command + '\n'):
307 if not self.is_complete(cleaned_command + '\n'):
308 self._colorize_input_buffer()
308 self._colorize_input_buffer()
309 self.render_error('Incomplete or invalid input')
309 self.render_error('Incomplete or invalid input')
310 self.new_prompt(self.input_prompt_template.substitute(
310 self.new_prompt(self.input_prompt_template.substitute(
311 number=(self.last_result['number'] + 1)))
311 number=(self.last_result['number'] + 1)))
312 return False
312 return False
313 self._on_enter()
313 self._on_enter()
314 return True
314 return True
315
315
316
316
317 def save_output_hooks(self):
317 def save_output_hooks(self):
318 self.__old_raw_input = __builtin__.raw_input
318 self.__old_raw_input = __builtin__.raw_input
319 PrefilterFrontEnd.save_output_hooks(self)
319 PrefilterFrontEnd.save_output_hooks(self)
320
320
321 def capture_output(self):
321 def capture_output(self):
322 self.SetLexer(stc.STC_LEX_NULL)
322 self.SetLexer(stc.STC_LEX_NULL)
323 PrefilterFrontEnd.capture_output(self)
323 PrefilterFrontEnd.capture_output(self)
324 __builtin__.raw_input = self.raw_input
324 __builtin__.raw_input = self.raw_input
325
325
326
326
327 def release_output(self):
327 def release_output(self):
328 __builtin__.raw_input = self.__old_raw_input
328 __builtin__.raw_input = self.__old_raw_input
329 PrefilterFrontEnd.release_output(self)
329 PrefilterFrontEnd.release_output(self)
330 self.SetLexer(stc.STC_LEX_PYTHON)
330 self.SetLexer(stc.STC_LEX_PYTHON)
331
331
332
332
333 def after_execute(self):
333 def after_execute(self):
334 PrefilterFrontEnd.after_execute(self)
334 PrefilterFrontEnd.after_execute(self)
335 # Clear the wait cursor
335 # Clear the wait cursor
336 if hasattr(self, '_cursor'):
336 if hasattr(self, '_cursor'):
337 del self._cursor
337 del self._cursor
338 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
338 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
339
339
340
340
341 def show_traceback(self):
341 def show_traceback(self):
342 start_line = self.GetCurrentLine()
342 start_line = self.GetCurrentLine()
343 PrefilterFrontEnd.show_traceback(self)
343 PrefilterFrontEnd.show_traceback(self)
344 self.ProcessEvent(wx.PaintEvent())
344 self.ProcessEvent(wx.PaintEvent())
345 #wx.Yield()
345 #wx.Yield()
346 for i in range(start_line, self.GetCurrentLine()):
346 for i in range(start_line, self.GetCurrentLine()):
347 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
347 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
348
348
349
349
350 #--------------------------------------------------------------------------
350 #--------------------------------------------------------------------------
351 # FrontEndBase interface
351 # FrontEndBase interface
352 #--------------------------------------------------------------------------
352 #--------------------------------------------------------------------------
353
353
354 def render_error(self, e):
354 def render_error(self, e):
355 start_line = self.GetCurrentLine()
355 start_line = self.GetCurrentLine()
356 self.write('\n' + e + '\n')
356 self.write('\n' + e + '\n')
357 for i in range(start_line, self.GetCurrentLine()):
357 for i in range(start_line, self.GetCurrentLine()):
358 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
358 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
359
359
360
360
361 #--------------------------------------------------------------------------
361 #--------------------------------------------------------------------------
362 # ConsoleWidget interface
362 # ConsoleWidget interface
363 #--------------------------------------------------------------------------
363 #--------------------------------------------------------------------------
364
364
365 def new_prompt(self, prompt):
365 def new_prompt(self, prompt):
366 """ Display a new prompt, and start a new input buffer.
366 """ Display a new prompt, and start a new input buffer.
367 """
367 """
368 self._input_state = 'readline'
368 self._input_state = 'readline'
369 ConsoleWidget.new_prompt(self, prompt)
369 ConsoleWidget.new_prompt(self, prompt)
370 i = self.current_prompt_line
370 i = self.current_prompt_line
371 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
371 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
372
372
373
373
374 def continuation_prompt(self, *args, **kwargs):
374 def continuation_prompt(self, *args, **kwargs):
375 # Avoid multiple inheritence, be explicit about which
375 # Avoid multiple inheritence, be explicit about which
376 # parent method class gets called
376 # parent method class gets called
377 return ConsoleWidget.continuation_prompt(self, *args, **kwargs)
377 return ConsoleWidget.continuation_prompt(self, *args, **kwargs)
378
378
379
379
380 def write(self, *args, **kwargs):
380 def write(self, *args, **kwargs):
381 # Avoid multiple inheritence, be explicit about which
381 # Avoid multiple inheritence, be explicit about which
382 # parent method class gets called
382 # parent method class gets called
383 return ConsoleWidget.write(self, *args, **kwargs)
383 return ConsoleWidget.write(self, *args, **kwargs)
384
384
385
385
386 def _on_key_down(self, event, skip=True):
386 def _on_key_down(self, event, skip=True):
387 """ Capture the character events, let the parent
387 """ Capture the character events, let the parent
388 widget handle them, and put our logic afterward.
388 widget handle them, and put our logic afterward.
389 """
389 """
390 # FIXME: This method needs to be broken down in smaller ones.
390 # FIXME: This method needs to be broken down in smaller ones.
391 current_line_num = self.GetCurrentLine()
391 current_line_num = self.GetCurrentLine()
392 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
392 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
393 # Capture Control-C
393 # Capture Control-C
394 if self._input_state == 'subprocess':
394 if self._input_state == 'subprocess':
395 if self.debug:
395 if self.debug:
396 print >>sys.__stderr__, 'Killing running process'
396 print >>sys.__stderr__, 'Killing running process'
397 if hasattr(self._running_process, 'process'):
397 if hasattr(self._running_process, 'process'):
398 self._running_process.process.kill()
398 self._running_process.process.kill()
399 elif self._input_state == 'buffering':
399 elif self._input_state == 'buffering':
400 if self.debug:
400 if self.debug:
401 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
401 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
402 raise KeyboardInterrupt
402 raise KeyboardInterrupt
403 # XXX: We need to make really sure we
403 # XXX: We need to make really sure we
404 # get back to a prompt.
404 # get back to a prompt.
405 elif self._input_state == 'subprocess' and (
405 elif self._input_state == 'subprocess' and (
406 ( event.KeyCode<256 and
406 ( event.KeyCode<256 and
407 not event.ControlDown() )
407 not event.ControlDown() )
408 or
408 or
409 ( event.KeyCode in (ord('d'), ord('D')) and
409 ( event.KeyCode in (ord('d'), ord('D')) and
410 event.ControlDown())):
410 event.ControlDown())):
411 # We are running a process, we redirect keys.
411 # We are running a process, we redirect keys.
412 ConsoleWidget._on_key_down(self, event, skip=skip)
412 ConsoleWidget._on_key_down(self, event, skip=skip)
413 char = chr(event.KeyCode)
413 char = chr(event.KeyCode)
414 # Deal with some inconsistency in wx keycodes:
414 # Deal with some inconsistency in wx keycodes:
415 if char == '\r':
415 if char == '\r':
416 char = '\n'
416 char = '\n'
417 elif not event.ShiftDown():
417 elif not event.ShiftDown():
418 char = char.lower()
418 char = char.lower()
419 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
419 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
420 char = '\04'
420 char = '\04'
421 self._running_process.process.stdin.write(char)
421 self._running_process.process.stdin.write(char)
422 self._running_process.process.stdin.flush()
422 self._running_process.process.stdin.flush()
423 elif event.KeyCode in (ord('('), 57, 53):
423 elif event.KeyCode in (ord('('), 57, 53):
424 # Calltips
424 # Calltips
425 event.Skip()
425 event.Skip()
426 self.do_calltip()
426 self.do_calltip()
427 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
427 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
428 event.Skip()
428 event.Skip()
429 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
429 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
430 wx.CallAfter(self._popup_completion, create=True)
430 wx.CallAfter(self._popup_completion, create=True)
431 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,
432 wx.WXK_RIGHT, wx.WXK_ESCAPE):
432 wx.WXK_RIGHT, wx.WXK_ESCAPE):
433 wx.CallAfter(self._popup_completion)
433 wx.CallAfter(self._popup_completion)
434 else:
434 else:
435 # Up history
435 # Up history
436 if event.KeyCode == wx.WXK_UP and (
436 if event.KeyCode == wx.WXK_UP and (
437 ( current_line_num == self.current_prompt_line and
437 ( current_line_num == self.current_prompt_line and
438 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
438 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
439 or event.ControlDown() ):
439 or event.ControlDown() ):
440 new_buffer = self.get_history_previous(
440 new_buffer = self.get_history_previous(
441 self.input_buffer)
441 self.input_buffer)
442 if new_buffer is not None:
442 if new_buffer is not None:
443 self.input_buffer = new_buffer
443 self.input_buffer = new_buffer
444 if self.GetCurrentLine() > self.current_prompt_line:
444 if self.GetCurrentLine() > self.current_prompt_line:
445 # Go to first line, for seemless history up.
445 # Go to first line, for seemless history up.
446 self.GotoPos(self.current_prompt_pos)
446 self.GotoPos(self.current_prompt_pos)
447 # Down history
447 # Down history
448 elif event.KeyCode == wx.WXK_DOWN and (
448 elif event.KeyCode == wx.WXK_DOWN and (
449 ( current_line_num == self.LineCount -1 and
449 ( current_line_num == self.LineCount -1 and
450 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
450 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
451 or event.ControlDown() ):
451 or event.ControlDown() ):
452 new_buffer = self.get_history_next()
452 new_buffer = self.get_history_next()
453 if new_buffer is not None:
453 if new_buffer is not None:
454 self.input_buffer = new_buffer
454 self.input_buffer = new_buffer
455 # Tab-completion
455 # Tab-completion
456 elif event.KeyCode == ord('\t'):
456 elif event.KeyCode == ord('\t'):
457 current_line, current_line_num = self.CurLine
457 current_line, current_line_num = self.CurLine
458 if not re.match(r'^\s*$', current_line):
458 if not re.match(r'^\s*$', current_line):
459 self.complete_current_input()
459 self.complete_current_input()
460 if self.AutoCompActive():
460 if self.AutoCompActive():
461 wx.CallAfter(self._popup_completion, create=True)
461 wx.CallAfter(self._popup_completion, create=True)
462 else:
462 else:
463 event.Skip()
463 event.Skip()
464 elif event.KeyCode == wx.WXK_BACK:
464 elif event.KeyCode == wx.WXK_BACK:
465 # If characters where erased, check if we have to
465 # If characters where erased, check if we have to
466 # remove a line.
466 # remove a line.
467 # XXX: What about DEL?
467 # XXX: What about DEL?
468 # FIXME: This logics should be in ConsoleWidget, as it is
468 # FIXME: This logics should be in ConsoleWidget, as it is
469 # independant of IPython
469 # independant of IPython
470 current_line, _ = self.CurLine
470 current_line, _ = self.CurLine
471 current_pos = self.GetCurrentPos()
471 current_pos = self.GetCurrentPos()
472 current_line_num = self.LineFromPosition(current_pos)
472 current_line_num = self.LineFromPosition(current_pos)
473 current_col = self.GetColumn(current_pos)
473 current_col = self.GetColumn(current_pos)
474 len_prompt = len(self.continuation_prompt())
474 len_prompt = len(self.continuation_prompt())
475 if ( current_line.startswith(self.continuation_prompt())
475 if ( current_line.startswith(self.continuation_prompt())
476 and current_col == len_prompt):
476 and current_col == len_prompt):
477 new_lines = []
477 new_lines = []
478 for line_num, line in enumerate(
478 for line_num, line in enumerate(
479 self.input_buffer.split('\n')):
479 self.input_buffer.split('\n')):
480 if (line_num + self.current_prompt_line ==
480 if (line_num + self.current_prompt_line ==
481 current_line_num):
481 current_line_num):
482 new_lines.append(line[len_prompt:])
482 new_lines.append(line[len_prompt:])
483 else:
483 else:
484 new_lines.append('\n'+line)
484 new_lines.append('\n'+line)
485 # The first character is '\n', due to the above
485 # The first character is '\n', due to the above
486 # code:
486 # code:
487 self.input_buffer = ''.join(new_lines)[1:]
487 self.input_buffer = ''.join(new_lines)[1:]
488 self.GotoPos(current_pos - 1 - len_prompt)
488 self.GotoPos(current_pos - 1 - len_prompt)
489 else:
489 else:
490 ConsoleWidget._on_key_down(self, event, skip=skip)
490 ConsoleWidget._on_key_down(self, event, skip=skip)
491 else:
491 else:
492 ConsoleWidget._on_key_down(self, event, skip=skip)
492 ConsoleWidget._on_key_down(self, event, skip=skip)
493
493
494
494
495
495
496 def _on_key_up(self, event, skip=True):
496 def _on_key_up(self, event, skip=True):
497 """ Called when any key is released.
497 """ Called when any key is released.
498 """
498 """
499 if event.KeyCode in (59, ord('.')):
499 if event.KeyCode in (59, ord('.')):
500 # Intercepting '.'
500 # Intercepting '.'
501 event.Skip()
501 event.Skip()
502 wx.CallAfter(self._popup_completion, create=True)
502 wx.CallAfter(self._popup_completion, create=True)
503 else:
503 else:
504 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
505 # Make sure the continuation_prompts are always followed by a
506 # whitespace
506 # whitespace
507 new_lines = []
507 new_lines = []
508 if self._input_state == 'readline':
508 if self._input_state == 'readline':
509 position = self.GetCurrentPos()
509 position = self.GetCurrentPos()
510 continuation_prompt = self.continuation_prompt()[:-1]
510 for line in self.input_buffer.split('\n'):
511 for line in self.input_buffer.split('\n'):
511 if not line == self.continuation_prompt()[:-1]:
512 if not line == continuation_prompt:
512 new_lines.append(line)
513 new_lines.append(line)
513 self.input_buffer = '\n'.join(new_lines)
514 self.input_buffer = '\n'.join(new_lines)
514 self.GotoPos(position)
515 self.GotoPos(position)
515
516
516
517
517 def _on_enter(self):
518 def _on_enter(self):
518 """ Called on return key down, in readline input_state.
519 """ Called on return key down, in readline input_state.
519 """
520 """
520 last_line_num = self.LineFromPosition(self.GetLength())
521 last_line_num = self.LineFromPosition(self.GetLength())
521 current_line_num = self.LineFromPosition(self.GetCurrentPos())
522 current_line_num = self.LineFromPosition(self.GetCurrentPos())
522 new_line_pos = (last_line_num - current_line_num)
523 new_line_pos = (last_line_num - current_line_num)
523 if self.debug:
524 if self.debug:
524 print >>sys.__stdout__, repr(self.input_buffer)
525 print >>sys.__stdout__, repr(self.input_buffer)
525 self.write('\n', refresh=False)
526 self.write('\n', refresh=False)
526 # Under windows scintilla seems to be doing funny
527 # Under windows scintilla seems to be doing funny
527 # stuff to the line returns here, but the getter for
528 # stuff to the line returns here, but the getter for
528 # input_buffer filters this out.
529 # input_buffer filters this out.
529 if sys.platform == 'win32':
530 if sys.platform == 'win32':
530 self.input_buffer = self.input_buffer
531 self.input_buffer = self.input_buffer
532 old_prompt_num = self.current_prompt_pos
531 has_executed = PrefilterFrontEnd._on_enter(self,
533 has_executed = PrefilterFrontEnd._on_enter(self,
532 new_line_pos=new_line_pos)
534 new_line_pos=new_line_pos)
533 if not has_executed:
535 if old_prompt_num == self.current_prompt_pos:
536 # No execution has happened
534 self.GotoPos(self.GetLineEndPosition(current_line_num + 1))
537 self.GotoPos(self.GetLineEndPosition(current_line_num + 1))
535 return has_executed
538 return has_executed
536
539
537
540
538 #--------------------------------------------------------------------------
541 #--------------------------------------------------------------------------
539 # EditWindow API
542 # EditWindow API
540 #--------------------------------------------------------------------------
543 #--------------------------------------------------------------------------
541
544
542 def OnUpdateUI(self, event):
545 def OnUpdateUI(self, event):
543 """ Override the OnUpdateUI of the EditWindow class, to prevent
546 """ Override the OnUpdateUI of the EditWindow class, to prevent
544 syntax highlighting both for faster redraw, and for more
547 syntax highlighting both for faster redraw, and for more
545 consistent look and feel.
548 consistent look and feel.
546 """
549 """
547 if not self._input_state == 'readline':
550 if not self._input_state == 'readline':
548 ConsoleWidget.OnUpdateUI(self, event)
551 ConsoleWidget.OnUpdateUI(self, event)
549
552
550 #--------------------------------------------------------------------------
553 #--------------------------------------------------------------------------
551 # Private API
554 # Private API
552 #--------------------------------------------------------------------------
555 #--------------------------------------------------------------------------
553
556
554 def _buffer_flush(self, event):
557 def _buffer_flush(self, event):
555 """ Called by the timer to flush the write buffer.
558 """ Called by the timer to flush the write buffer.
556
559
557 This is always called in the mainloop, by the wx timer.
560 This is always called in the mainloop, by the wx timer.
558 """
561 """
559 self._out_buffer_lock.acquire()
562 self._out_buffer_lock.acquire()
560 _out_buffer = self._out_buffer
563 _out_buffer = self._out_buffer
561 self._out_buffer = []
564 self._out_buffer = []
562 self._out_buffer_lock.release()
565 self._out_buffer_lock.release()
563 self.write(''.join(_out_buffer), refresh=False)
566 self.write(''.join(_out_buffer), refresh=False)
564
567
565
568
566 def _colorize_input_buffer(self):
569 def _colorize_input_buffer(self):
567 """ Keep the input buffer lines at a bright color.
570 """ Keep the input buffer lines at a bright color.
568 """
571 """
569 if not self._input_state in ('readline', 'raw_input'):
572 if not self._input_state in ('readline', 'raw_input'):
570 return
573 return
571 end_line = self.GetCurrentLine()
574 end_line = self.GetCurrentLine()
572 if not sys.platform == 'win32':
575 if not sys.platform == 'win32':
573 end_line += 1
576 end_line += 1
574 for i in range(self.current_prompt_line, end_line):
577 for i in range(self.current_prompt_line, end_line):
575 if i in self._markers:
578 if i in self._markers:
576 self.MarkerDeleteHandle(self._markers[i])
579 self.MarkerDeleteHandle(self._markers[i])
577 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
580 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
578
581
579
582
580 if __name__ == '__main__':
583 if __name__ == '__main__':
581 class MainWindow(wx.Frame):
584 class MainWindow(wx.Frame):
582 def __init__(self, parent, id, title):
585 def __init__(self, parent, id, title):
583 wx.Frame.__init__(self, parent, id, title, size=(300,250))
586 wx.Frame.__init__(self, parent, id, title, size=(300,250))
584 self._sizer = wx.BoxSizer(wx.VERTICAL)
587 self._sizer = wx.BoxSizer(wx.VERTICAL)
585 self.shell = WxController(self)
588 self.shell = WxController(self)
586 self._sizer.Add(self.shell, 1, wx.EXPAND)
589 self._sizer.Add(self.shell, 1, wx.EXPAND)
587 self.SetSizer(self._sizer)
590 self.SetSizer(self._sizer)
588 self.SetAutoLayout(1)
591 self.SetAutoLayout(1)
589 self.Show(True)
592 self.Show(True)
590
593
591 app = wx.PySimpleApp()
594 app = wx.PySimpleApp()
592 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
595 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
593 frame.shell.SetFocus()
596 frame.shell.SetFocus()
594 frame.SetSize((680, 460))
597 frame.SetSize((680, 460))
595 self = frame.shell
598 self = frame.shell
596
599
597 app.MainLoop()
600 app.MainLoop()
598
601
@@ -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