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 | from IPython.ipapi import IPApi |
|
23 | from IPython.ipapi import IPApi | |
24 | from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap |
|
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 | from IPython.genutils import Term |
|
29 | from IPython.genutils import Term | |
27 | import pydoc |
|
30 | import pydoc | |
28 |
|
31 | |||
29 | #------------------------------------------------------------------------------- |
|
32 | def mk_system_call(system_call_function, command): | |
30 | # Utility functions (temporary, should be moved out of here) |
|
33 | """ given a os.system replacement, and a leading string command, | |
31 | #------------------------------------------------------------------------------- |
|
34 | returns a function that will execute the command with the given | |
32 | import os |
|
35 | argument string. | |
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 |
|
|||
43 | """ |
|
36 | """ | |
44 | def my_system_call(args): |
|
37 | def my_system_call(args): | |
45 |
|
|
38 | system_call_function("%s %s" % (command, args)) | |
46 | return my_system_call |
|
39 | return my_system_call | |
47 |
|
40 | |||
48 | #------------------------------------------------------------------------------- |
|
41 | #------------------------------------------------------------------------------- | |
@@ -65,14 +58,18 b' class PrefilterFrontEnd(LineFrontEndBase):' | |||||
65 | self.shell.user_global_ns = self.ipython0.user_global_ns |
|
58 | self.shell.user_global_ns = self.ipython0.user_global_ns | |
66 | # Make sure the raw system call doesn't get called, as we don't |
|
59 | # Make sure the raw system call doesn't get called, as we don't | |
67 | # have a stdin accessible. |
|
60 | # have a stdin accessible. | |
68 |
self._ip.system = |
|
61 | self._ip.system = self.system_call | |
69 | # XXX: Muck around with magics so that they work better |
|
62 | # XXX: Muck around with magics so that they work better | |
70 | # in our environment |
|
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 | self.shell.output_trap = RedirectorOutputTrap( |
|
66 | self.shell.output_trap = RedirectorOutputTrap( | |
73 | out_callback=self.write, |
|
67 | out_callback=self.write, | |
74 | err_callback=self.write, |
|
68 | err_callback=self.write, | |
75 | ) |
|
69 | ) | |
|
70 | self.shell.traceback_trap = SyncTracebackTrap( | |||
|
71 | formatters=[ColorTB(color_scheme='LightBG'), ] | |||
|
72 | ) | |||
76 | # Capture and release the outputs, to make sure all the |
|
73 | # Capture and release the outputs, to make sure all the | |
77 | # shadow variables are set |
|
74 | # shadow variables are set | |
78 | self.capture_output() |
|
75 | self.capture_output() | |
@@ -115,6 +112,13 b' class PrefilterFrontEnd(LineFrontEndBase):' | |||||
115 | self.release_output() |
|
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 | def capture_output(self): |
|
122 | def capture_output(self): | |
119 | """ Capture all the output mechanisms we can think of. |
|
123 | """ Capture all the output mechanisms we can think of. | |
120 | """ |
|
124 | """ |
@@ -198,9 +198,11 b' class ConsoleWidget(editwindow.EditWindow):' | |||||
198 | """ Write given text to buffer, while translating the ansi escape |
|
198 | """ Write given text to buffer, while translating the ansi escape | |
199 | sequences. |
|
199 | sequences. | |
200 | """ |
|
200 | """ | |
201 |
# XXX: do not put print statements |
|
201 | # XXX: do not put print statements to sys.stdout/sys.stderr in | |
202 |
# statements will call this method, a |
|
202 | # this method, the print statements will call this method, as | |
203 | # an infinit loop |
|
203 | # you will end up with an infinit loop | |
|
204 | if self.debug: | |||
|
205 | print >>sys.__stderr__, text | |||
204 | title = self.title_pat.split(text) |
|
206 | title = self.title_pat.split(text) | |
205 | if len(title)>1: |
|
207 | if len(title)>1: | |
206 | self.title = title[-2] |
|
208 | self.title = title[-2] |
@@ -5,6 +5,7 b' ipython.' | |||||
5 |
|
5 | |||
6 | import wx |
|
6 | import wx | |
7 | from wx_frontend import WxController |
|
7 | from wx_frontend import WxController | |
|
8 | import __builtin__ | |||
8 |
|
9 | |||
9 | class WIPythonController(WxController): |
|
10 | class WIPythonController(WxController): | |
10 | """ Sub class of WxController that adds some application-specific |
|
11 | """ Sub class of WxController that adds some application-specific | |
@@ -19,7 +20,8 b' class WIPythonController(WxController):' | |||||
19 | def _on_key_down(self, event, skip=True): |
|
20 | def _on_key_down(self, event, skip=True): | |
20 | # Intercept Ctrl-D to quit |
|
21 | # Intercept Ctrl-D to quit | |
21 | if event.KeyCode == ord('D') and event.ControlDown() and \ |
|
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 | wx.CallAfter(self.ask_exit) |
|
25 | wx.CallAfter(self.ask_exit) | |
24 | else: |
|
26 | else: | |
25 | WxController._on_key_down(self, event, skip=skip) |
|
27 | WxController._on_key_down(self, event, skip=skip) |
@@ -27,7 +27,11 b' from wx import stc' | |||||
27 | from console_widget import ConsoleWidget |
|
27 | from console_widget import ConsoleWidget | |
28 | import __builtin__ |
|
28 | import __builtin__ | |
29 | from time import sleep |
|
29 | from time import sleep | |
|
30 | import sys | |||
|
31 | ||||
|
32 | from threading import Lock | |||
30 |
|
33 | |||
|
34 | from IPython.frontend.piped_process import PipedProcess | |||
31 | from IPython.frontend.prefilterfrontend import PrefilterFrontEnd |
|
35 | from IPython.frontend.prefilterfrontend import PrefilterFrontEnd | |
32 |
|
36 | |||
33 | #_COMMAND_BG = '#FAFAF1' # Nice green |
|
37 | #_COMMAND_BG = '#FAFAF1' # Nice green | |
@@ -47,6 +51,18 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||||
47 |
|
51 | |||
48 | debug = True |
|
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 | # Public API |
|
67 | # Public API | |
52 | #-------------------------------------------------------------------------- |
|
68 | #-------------------------------------------------------------------------- | |
@@ -59,9 +75,6 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||||
59 | ConsoleWidget.__init__(self, parent, id, pos, size, style) |
|
75 | ConsoleWidget.__init__(self, parent, id, pos, size, style) | |
60 | PrefilterFrontEnd.__init__(self) |
|
76 | PrefilterFrontEnd.__init__(self) | |
61 |
|
77 | |||
62 | # Capture Character keys |
|
|||
63 | self.Bind(wx.EVT_KEY_DOWN, self._on_key_down) |
|
|||
64 |
|
||||
65 | # Marker for running buffer. |
|
78 | # Marker for running buffer. | |
66 | self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND, |
|
79 | self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND, | |
67 | background=_RUNNING_BUFFER_BG) |
|
80 | background=_RUNNING_BUFFER_BG) | |
@@ -69,18 +82,28 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||||
69 | self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND, |
|
82 | self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND, | |
70 | background=_ERROR_BG) |
|
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 | def do_completion(self): |
|
90 | def do_completion(self): | |
74 | """ Do code completion. |
|
91 | """ Do code completion. | |
75 | """ |
|
92 | """ | |
|
93 | if self.debug: | |||
|
94 | print >>sys.__stdout__, "do_completion", | |||
76 | line = self.get_current_edit_buffer() |
|
95 | line = self.get_current_edit_buffer() | |
77 | new_line, completions = self.complete(line) |
|
96 | new_line, completions = self.complete(line) | |
78 | if len(completions)>1: |
|
97 | if len(completions)>1: | |
79 | self.write_completion(completions) |
|
98 | self.write_completion(completions) | |
80 | self.replace_current_edit_buffer(new_line) |
|
99 | self.replace_current_edit_buffer(new_line) | |
|
100 | if self.debug: | |||
|
101 | print >>sys.__stdout__, completions | |||
81 |
|
102 | |||
82 |
|
103 | |||
83 | def do_calltip(self): |
|
104 | def do_calltip(self): | |
|
105 | if self.debug: | |||
|
106 | print >>sys.__stdout__, "do_calltip" | |||
84 | separators = re.compile('[\s\{\}\[\]\(\)\= ,:]') |
|
107 | separators = re.compile('[\s\{\}\[\]\(\)\= ,:]') | |
85 | symbol = self.get_current_edit_buffer() |
|
108 | symbol = self.get_current_edit_buffer() | |
86 | symbol_string = separators.split(symbol)[-1] |
|
109 | symbol_string = separators.split(symbol)[-1] | |
@@ -108,6 +131,8 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||||
108 | """ Updates the popup completion menu if it exists. If create is |
|
131 | """ Updates the popup completion menu if it exists. If create is | |
109 | true, open the menu. |
|
132 | true, open the menu. | |
110 | """ |
|
133 | """ | |
|
134 | if self.debug: | |||
|
135 | print >>sys.__stdout__, "popup_completion", | |||
111 | line = self.get_current_edit_buffer() |
|
136 | line = self.get_current_edit_buffer() | |
112 | if (self.AutoCompActive() and not line[-1] == '.') \ |
|
137 | if (self.AutoCompActive() and not line[-1] == '.') \ | |
113 | or create==True: |
|
138 | or create==True: | |
@@ -118,6 +143,8 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||||
118 | residual = complete_sep.split(line)[-1] |
|
143 | residual = complete_sep.split(line)[-1] | |
119 | offset = len(residual) |
|
144 | offset = len(residual) | |
120 | self.pop_completion(completions, offset=offset) |
|
145 | self.pop_completion(completions, offset=offset) | |
|
146 | if self.debug: | |||
|
147 | print >>sys.__stdout__, completions | |||
121 |
|
148 | |||
122 |
|
149 | |||
123 | def raw_input(self, prompt): |
|
150 | def raw_input(self, prompt): | |
@@ -148,9 +175,6 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||||
148 | self.MarkerAdd(i, _RUNNING_BUFFER_MARKER) |
|
175 | self.MarkerAdd(i, _RUNNING_BUFFER_MARKER) | |
149 | # Update the display: |
|
176 | # Update the display: | |
150 | wx.Yield() |
|
177 | wx.Yield() | |
151 | ## Remove the trailing "\n" for cleaner display |
|
|||
152 | #self.SetSelection(self.GetLength()-1, self.GetLength()) |
|
|||
153 | #self.ReplaceSelection('') |
|
|||
154 | self.GotoPos(self.GetLength()) |
|
178 | self.GotoPos(self.GetLength()) | |
155 | PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string) |
|
179 | PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string) | |
156 |
|
180 | |||
@@ -172,6 +196,51 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||||
172 | del self._cursor |
|
196 | del self._cursor | |
173 |
|
197 | |||
174 |
|
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 | ||||
|
243 | ||||
175 | def show_traceback(self): |
|
244 | def show_traceback(self): | |
176 | start_line = self.GetCurrentLine() |
|
245 | start_line = self.GetCurrentLine() | |
177 | PrefilterFrontEnd.show_traceback(self) |
|
246 | PrefilterFrontEnd.show_traceback(self) | |
@@ -184,14 +253,20 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||||
184 | # Private API |
|
253 | # Private API | |
185 | #-------------------------------------------------------------------------- |
|
254 | #-------------------------------------------------------------------------- | |
186 |
|
255 | |||
187 |
|
||||
188 | def _on_key_down(self, event, skip=True): |
|
256 | def _on_key_down(self, event, skip=True): | |
189 | """ Capture the character events, let the parent |
|
257 | """ Capture the character events, let the parent | |
190 | widget handle them, and put our logic afterward. |
|
258 | widget handle them, and put our logic afterward. | |
191 | """ |
|
259 | """ | |
192 | current_line_number = self.GetCurrentLine() |
|
260 | current_line_number = self.GetCurrentLine() | |
|
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): | |||
193 | # Calltips |
|
269 | # Calltips | |
194 | if event.KeyCode == ord('('): |
|
|||
195 | event.Skip() |
|
270 | event.Skip() | |
196 | self.do_calltip() |
|
271 | self.do_calltip() | |
197 | elif self.AutoCompActive(): |
|
272 | elif self.AutoCompActive(): | |
@@ -234,7 +309,7 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||||
234 |
|
309 | |||
235 |
|
310 | |||
236 | def _on_key_up(self, event, skip=True): |
|
311 | def _on_key_up(self, event, skip=True): | |
237 |
if event.KeyCode |
|
312 | if event.KeyCode in (59, ord('.')): | |
238 | # Intercepting '.' |
|
313 | # Intercepting '.' | |
239 | event.Skip() |
|
314 | event.Skip() | |
240 | self.popup_completion(create=True) |
|
315 | self.popup_completion(create=True) | |
@@ -243,7 +318,6 b' class WxController(PrefilterFrontEnd, ConsoleWidget):' | |||||
243 |
|
318 | |||
244 | def _on_enter(self): |
|
319 | def _on_enter(self): | |
245 | if self.debug: |
|
320 | if self.debug: | |
246 | import sys |
|
|||
247 | print >>sys.__stdout__, repr(self.get_current_edit_buffer()) |
|
321 | print >>sys.__stdout__, repr(self.get_current_edit_buffer()) | |
248 | PrefilterFrontEnd._on_enter(self) |
|
322 | PrefilterFrontEnd._on_enter(self) | |
249 |
|
323 |
@@ -14,10 +14,8 b' __docformat__ = "restructuredtext en"' | |||||
14 | #------------------------------------------------------------------------------- |
|
14 | #------------------------------------------------------------------------------- | |
15 | # Imports |
|
15 | # Imports | |
16 | #------------------------------------------------------------------------------- |
|
16 | #------------------------------------------------------------------------------- | |
17 |
|
||||
18 | import sys |
|
17 | import sys | |
19 |
|
18 | |||
20 |
|
||||
21 | class TracebackTrap(object): |
|
19 | class TracebackTrap(object): | |
22 | """ Object to trap and format tracebacks. |
|
20 | """ Object to trap and format tracebacks. | |
23 | """ |
|
21 | """ | |
@@ -38,9 +36,6 b' class TracebackTrap(object):' | |||||
38 | def hook(self, *args): |
|
36 | def hook(self, *args): | |
39 | """ This method actually implements the hook. |
|
37 | """ This method actually implements the hook. | |
40 | """ |
|
38 | """ | |
41 | import sys |
|
|||
42 | print >>sys.stderr, "I have been raised" |
|
|||
43 |
|
||||
44 | self.args = args |
|
39 | self.args = args | |
45 |
|
40 | |||
46 | def set(self): |
|
41 | def set(self): |
General Comments 0
You need to be logged in to leave comments.
Login now