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