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