Show More
@@ -0,0 +1,48 b'' | |||
|
1 | # encoding: utf-8 | |
|
2 | """ | |
|
3 | Object for encapsulating process execution by using callbacks for stdout, | |
|
4 | stderr and stdin. | |
|
5 | """ | |
|
6 | __docformat__ = "restructuredtext en" | |
|
7 | ||
|
8 | #------------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #------------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #------------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #------------------------------------------------------------------------------- | |
|
18 | from subprocess import Popen, PIPE | |
|
19 | from threading import Thread | |
|
20 | ||
|
21 | ||
|
22 | class PipedProcess(Thread): | |
|
23 | ||
|
24 | def __init__(self, command_string, out_callback, | |
|
25 | end_callback=None,): | |
|
26 | self.command_string = command_string | |
|
27 | self.out_callback = out_callback | |
|
28 | self.end_callback = end_callback | |
|
29 | Thread.__init__(self) | |
|
30 | ||
|
31 | ||
|
32 | def run(self): | |
|
33 | """ Start the process and hook up the callbacks. | |
|
34 | """ | |
|
35 | process = Popen((self.command_string + ' 2>&1', ), shell=True, | |
|
36 | universal_newlines=True, | |
|
37 | stdout=PIPE, stdin=PIPE) | |
|
38 | self.process = process | |
|
39 | while True: | |
|
40 | out_char = process.stdout.read(1) | |
|
41 | if out_char == '' and process.poll() is not None: | |
|
42 | break | |
|
43 | self.out_callback(out_char) | |
|
44 | ||
|
45 | if self.end_callback is not None: | |
|
46 | self.end_callback() | |
|
47 | ||
|
48 |
@@ -23,26 +23,19 b' from IPython.ipmaker import make_IPython' | |||
|
23 | 23 | from IPython.ipapi import IPApi |
|
24 | 24 | from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap |
|
25 | 25 | |
|
26 | from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap | |
|
27 | ||
|
28 | from IPython.ultraTB import ColorTB | |
|
26 | 29 | from IPython.genutils import Term |
|
27 | 30 | import pydoc |
|
28 | 31 | |
|
29 | #------------------------------------------------------------------------------- | |
|
30 | # Utility functions (temporary, should be moved out of here) | |
|
31 | #------------------------------------------------------------------------------- | |
|
32 | import os | |
|
33 | def xterm_system(command): | |
|
34 | """ Run a command in a separate console window. | |
|
35 | """ | |
|
36 | os.system(("""xterm -title "%s" -e \'/bin/sh -c "%s ; """ | |
|
37 | """echo; echo press enter to close ; """ | |
|
38 | # """echo \\"\x1b]0;%s (finished -- press enter to close)\x07\\" ; | |
|
39 | """read foo;"\' """) % (command, command) ) | |
|
40 | ||
|
41 | def system_call(command): | |
|
42 | """ Temporary hack for aliases | |
|
32 | def mk_system_call(system_call_function, command): | |
|
33 | """ given a os.system replacement, and a leading string command, | |
|
34 | returns a function that will execute the command with the given | |
|
35 | argument string. | |
|
43 | 36 | """ |
|
44 | 37 | def my_system_call(args): |
|
45 |
|
|
|
38 | system_call_function("%s %s" % (command, args)) | |
|
46 | 39 | return my_system_call |
|
47 | 40 | |
|
48 | 41 | #------------------------------------------------------------------------------- |
@@ -65,14 +58,18 b' class PrefilterFrontEnd(LineFrontEndBase):' | |||
|
65 | 58 | self.shell.user_global_ns = self.ipython0.user_global_ns |
|
66 | 59 | # Make sure the raw system call doesn't get called, as we don't |
|
67 | 60 | # have a stdin accessible. |
|
68 |
self._ip.system = |
|
|
61 | self._ip.system = self.system_call | |
|
69 | 62 | # XXX: Muck around with magics so that they work better |
|
70 | 63 | # in our environment |
|
71 |
self.ipython0.magic_ls = system_call |
|
|
64 | self.ipython0.magic_ls = mk_system_call(self.system_call, | |
|
65 | 'ls -CF') | |
|
72 | 66 | self.shell.output_trap = RedirectorOutputTrap( |
|
73 | 67 | out_callback=self.write, |
|
74 | 68 | err_callback=self.write, |
|
75 | 69 | ) |
|
70 | self.shell.traceback_trap = SyncTracebackTrap( | |
|
71 | formatters=[ColorTB(color_scheme='LightBG'), ] | |
|
72 | ) | |
|
76 | 73 | # Capture and release the outputs, to make sure all the |
|
77 | 74 | # shadow variables are set |
|
78 | 75 | self.capture_output() |
@@ -115,6 +112,13 b' class PrefilterFrontEnd(LineFrontEndBase):' | |||
|
115 | 112 | self.release_output() |
|
116 | 113 | |
|
117 | 114 | |
|
115 | def system_call(self, command): | |
|
116 | """ Allows for frontend to define their own system call, to be | |
|
117 | able capture output and redirect input. | |
|
118 | """ | |
|
119 | return os.system(command, args) | |
|
120 | ||
|
121 | ||
|
118 | 122 | def capture_output(self): |
|
119 | 123 | """ Capture all the output mechanisms we can think of. |
|
120 | 124 | """ |
@@ -198,9 +198,11 b' class ConsoleWidget(editwindow.EditWindow):' | |||
|
198 | 198 | """ Write given text to buffer, while translating the ansi escape |
|
199 | 199 | sequences. |
|
200 | 200 | """ |
|
201 |
# XXX: do not put print statements |
|
|
202 |
# statements will call this method, a |
|
|
203 | # an infinit loop | |
|
201 | # XXX: do not put print statements to sys.stdout/sys.stderr in | |
|
202 | # this method, the print statements will call this method, as | |
|
203 | # you will end up with an infinit loop | |
|
204 | if self.debug: | |
|
205 | print >>sys.__stderr__, text | |
|
204 | 206 | title = self.title_pat.split(text) |
|
205 | 207 | if len(title)>1: |
|
206 | 208 | self.title = title[-2] |
@@ -5,6 +5,7 b' ipython.' | |||
|
5 | 5 | |
|
6 | 6 | import wx |
|
7 | 7 | from wx_frontend import WxController |
|
8 | import __builtin__ | |
|
8 | 9 | |
|
9 | 10 | class WIPythonController(WxController): |
|
10 | 11 | """ Sub class of WxController that adds some application-specific |
@@ -19,7 +20,8 b' class WIPythonController(WxController):' | |||
|
19 | 20 | def _on_key_down(self, event, skip=True): |
|
20 | 21 | # Intercept Ctrl-D to quit |
|
21 | 22 | if event.KeyCode == ord('D') and event.ControlDown() and \ |
|
22 |
self.get_current_edit_buffer()=='' |
|
|
23 | self.get_current_edit_buffer()=='' and \ | |
|
24 | not self.raw_input == __builtin__.raw_input: | |
|
23 | 25 | wx.CallAfter(self.ask_exit) |
|
24 | 26 | else: |
|
25 | 27 | WxController._on_key_down(self, event, skip=skip) |
@@ -27,7 +27,11 b' from wx import stc' | |||
|
27 | 27 | from console_widget import ConsoleWidget |
|
28 | 28 | import __builtin__ |
|
29 | 29 | from time import sleep |
|
30 | import sys | |
|
30 | 31 | |
|
32 | from threading import Lock | |
|
33 | ||
|
34 | from IPython.frontend.piped_process import PipedProcess | |
|
31 | 35 | from IPython.frontend.prefilterfrontend import PrefilterFrontEnd |
|
32 | 36 | |
|
33 | 37 | #_COMMAND_BG = '#FAFAF1' # Nice green |
@@ -46,7 +50,19 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||
|
46 | 50 | '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02' |
|
47 | 51 | |
|
48 | 52 | debug = True |
|
49 | ||
|
53 | ||
|
54 | # Attribute to store reference to the pipes of a subprocess, if we | |
|
55 | # are running any. | |
|
56 | running_process = False | |
|
57 | ||
|
58 | # A queue for writing fast streams to the screen without flooding the | |
|
59 | # event loop | |
|
60 | write_buffer = [] | |
|
61 | ||
|
62 | # A lock to lock the write_buffer to make sure we don't empty it | |
|
63 | # while it is being swapped | |
|
64 | write_buffer_lock = Lock() | |
|
65 | ||
|
50 | 66 | #-------------------------------------------------------------------------- |
|
51 | 67 | # Public API |
|
52 | 68 | #-------------------------------------------------------------------------- |
@@ -59,9 +75,6 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||
|
59 | 75 | ConsoleWidget.__init__(self, parent, id, pos, size, style) |
|
60 | 76 | PrefilterFrontEnd.__init__(self) |
|
61 | 77 | |
|
62 | # Capture Character keys | |
|
63 | self.Bind(wx.EVT_KEY_DOWN, self._on_key_down) | |
|
64 | ||
|
65 | 78 | # Marker for running buffer. |
|
66 | 79 | self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND, |
|
67 | 80 | background=_RUNNING_BUFFER_BG) |
@@ -69,18 +82,28 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||
|
69 | 82 | self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND, |
|
70 | 83 | background=_ERROR_BG) |
|
71 | 84 | |
|
85 | # A time for flushing the write buffer | |
|
86 | BUFFER_FLUSH_TIMER_ID = 100 | |
|
87 | self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID) | |
|
88 | wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush) | |
|
72 | 89 | |
|
73 | 90 | def do_completion(self): |
|
74 | 91 | """ Do code completion. |
|
75 | 92 | """ |
|
93 | if self.debug: | |
|
94 | print >>sys.__stdout__, "do_completion", | |
|
76 | 95 | line = self.get_current_edit_buffer() |
|
77 | 96 | new_line, completions = self.complete(line) |
|
78 | 97 | if len(completions)>1: |
|
79 | 98 | self.write_completion(completions) |
|
80 | 99 | self.replace_current_edit_buffer(new_line) |
|
100 | if self.debug: | |
|
101 | print >>sys.__stdout__, completions | |
|
81 | 102 | |
|
82 | 103 | |
|
83 | 104 | def do_calltip(self): |
|
105 | if self.debug: | |
|
106 | print >>sys.__stdout__, "do_calltip" | |
|
84 | 107 | separators = re.compile('[\s\{\}\[\]\(\)\= ,:]') |
|
85 | 108 | symbol = self.get_current_edit_buffer() |
|
86 | 109 | symbol_string = separators.split(symbol)[-1] |
@@ -108,6 +131,8 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||
|
108 | 131 | """ Updates the popup completion menu if it exists. If create is |
|
109 | 132 | true, open the menu. |
|
110 | 133 | """ |
|
134 | if self.debug: | |
|
135 | print >>sys.__stdout__, "popup_completion", | |
|
111 | 136 | line = self.get_current_edit_buffer() |
|
112 | 137 | if (self.AutoCompActive() and not line[-1] == '.') \ |
|
113 | 138 | or create==True: |
@@ -118,6 +143,8 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||
|
118 | 143 | residual = complete_sep.split(line)[-1] |
|
119 | 144 | offset = len(residual) |
|
120 | 145 | self.pop_completion(completions, offset=offset) |
|
146 | if self.debug: | |
|
147 | print >>sys.__stdout__, completions | |
|
121 | 148 | |
|
122 | 149 | |
|
123 | 150 | def raw_input(self, prompt): |
@@ -148,9 +175,6 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||
|
148 | 175 | self.MarkerAdd(i, _RUNNING_BUFFER_MARKER) |
|
149 | 176 | # Update the display: |
|
150 | 177 | wx.Yield() |
|
151 | ## Remove the trailing "\n" for cleaner display | |
|
152 | #self.SetSelection(self.GetLength()-1, self.GetLength()) | |
|
153 | #self.ReplaceSelection('') | |
|
154 | 178 | self.GotoPos(self.GetLength()) |
|
155 | 179 | PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string) |
|
156 | 180 | |
@@ -171,6 +195,51 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||
|
171 | 195 | if hasattr(self, '_cursor'): |
|
172 | 196 | del self._cursor |
|
173 | 197 | |
|
198 | ||
|
199 | def system_call(self, command_string): | |
|
200 | self.running_process = PipedProcess(command_string, | |
|
201 | out_callback=self.buffered_write, | |
|
202 | end_callback = self._end_system_call) | |
|
203 | self.running_process.start() | |
|
204 | # XXX: another one of these polling loops to have a blocking | |
|
205 | # call | |
|
206 | while self.running_process: | |
|
207 | wx.Yield() | |
|
208 | sleep(0.1) | |
|
209 | ||
|
210 | ||
|
211 | def buffered_write(self, text): | |
|
212 | """ A write method for streams, that caches the stream in order | |
|
213 | to avoid flooding the event loop. | |
|
214 | ||
|
215 | This can be called outside of the main loop, in separate | |
|
216 | threads. | |
|
217 | """ | |
|
218 | self.write_buffer_lock.acquire() | |
|
219 | self.write_buffer.append(text) | |
|
220 | self.write_buffer_lock.release() | |
|
221 | if not self._buffer_flush_timer.IsRunning(): | |
|
222 | self._buffer_flush_timer.Start(100) # milliseconds | |
|
223 | ||
|
224 | ||
|
225 | def _end_system_call(self): | |
|
226 | """ Called at the end of a system call. | |
|
227 | """ | |
|
228 | self.running_process = False | |
|
229 | ||
|
230 | ||
|
231 | def _buffer_flush(self, event): | |
|
232 | """ Called by the timer to flush the write buffer. | |
|
233 | ||
|
234 | This is always called in the mainloop, by the wx timer. | |
|
235 | """ | |
|
236 | self.write_buffer_lock.acquire() | |
|
237 | write_buffer = self.write_buffer | |
|
238 | self.write_buffer = [] | |
|
239 | self.write_buffer_lock.release() | |
|
240 | self.write(''.join(write_buffer)) | |
|
241 | self._buffer_flush_timer.Stop() | |
|
242 | ||
|
174 | 243 | |
|
175 | 244 | def show_traceback(self): |
|
176 | 245 | start_line = self.GetCurrentLine() |
@@ -184,14 +253,20 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||
|
184 | 253 | # Private API |
|
185 | 254 | #-------------------------------------------------------------------------- |
|
186 | 255 | |
|
187 | ||
|
188 | 256 | def _on_key_down(self, event, skip=True): |
|
189 | 257 | """ Capture the character events, let the parent |
|
190 | 258 | widget handle them, and put our logic afterward. |
|
191 | 259 | """ |
|
192 | 260 | current_line_number = self.GetCurrentLine() |
|
193 | # Calltips | |
|
194 | if event.KeyCode == ord('('): | |
|
261 | if self.running_process and event.KeyCode<256 \ | |
|
262 | and event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN): | |
|
263 | # We are running a process, let us not be too clever. | |
|
264 | ConsoleWidget._on_key_down(self, event, skip=skip) | |
|
265 | if self.debug: | |
|
266 | print >>sys.__stderr__, chr(event.KeyCode) | |
|
267 | self.running_process.process.stdin.write(chr(event.KeyCode)) | |
|
268 | elif event.KeyCode in (ord('('), 57): | |
|
269 | # Calltips | |
|
195 | 270 | event.Skip() |
|
196 | 271 | self.do_calltip() |
|
197 | 272 | elif self.AutoCompActive(): |
@@ -234,7 +309,7 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||
|
234 | 309 | |
|
235 | 310 | |
|
236 | 311 | def _on_key_up(self, event, skip=True): |
|
237 |
if event.KeyCode |
|
|
312 | if event.KeyCode in (59, ord('.')): | |
|
238 | 313 | # Intercepting '.' |
|
239 | 314 | event.Skip() |
|
240 | 315 | self.popup_completion(create=True) |
@@ -243,7 +318,6 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||
|
243 | 318 | |
|
244 | 319 | def _on_enter(self): |
|
245 | 320 | if self.debug: |
|
246 | import sys | |
|
247 | 321 | print >>sys.__stdout__, repr(self.get_current_edit_buffer()) |
|
248 | 322 | PrefilterFrontEnd._on_enter(self) |
|
249 | 323 |
@@ -14,10 +14,8 b' __docformat__ = "restructuredtext en"' | |||
|
14 | 14 | #------------------------------------------------------------------------------- |
|
15 | 15 | # Imports |
|
16 | 16 | #------------------------------------------------------------------------------- |
|
17 | ||
|
18 | 17 | import sys |
|
19 | 18 | |
|
20 | ||
|
21 | 19 | class TracebackTrap(object): |
|
22 | 20 | """ Object to trap and format tracebacks. |
|
23 | 21 | """ |
@@ -38,9 +36,6 b' class TracebackTrap(object):' | |||
|
38 | 36 | def hook(self, *args): |
|
39 | 37 | """ This method actually implements the hook. |
|
40 | 38 | """ |
|
41 | import sys | |
|
42 | print >>sys.stderr, "I have been raised" | |
|
43 | ||
|
44 | 39 | self.args = args |
|
45 | 40 | |
|
46 | 41 | def set(self): |
General Comments 0
You need to be logged in to leave comments.
Login now