20 # |
20 # |
21 #You should have received a copy of the GNU General Public |
21 #You should have received a copy of the GNU General Public |
22 #License along with this library; if not, write to the Free Software |
22 #License along with this library; if not, write to the Free Software |
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
24 |
24 |
25 # |
|
26 # based on wxPopen.py from boa-constructor |
|
27 # |
|
28 |
25 |
29 import time |
26 import time |
30 from StringIO import StringIO |
27 import wx |
|
28 import subprocess, ctypes |
|
29 import threading |
|
30 import os |
31 |
31 |
32 import wx |
32 |
|
33 class outputThread(threading.Thread): |
|
34 """ |
|
35 Thread is used to print the output of a command to the stdout |
|
36 """ |
|
37 def __init__(self, Proc, fd, callback=None, endcallback=None): |
|
38 threading.Thread.__init__(self) |
|
39 self.killed = False |
|
40 self.finished = False |
|
41 self.retval = None |
|
42 self.Proc = Proc |
|
43 self.callback = callback |
|
44 self.endcallback = endcallback |
|
45 self.fd = fd |
33 |
46 |
34 class ProcessRunnerMix: |
47 def run(self): |
|
48 outeof = False |
|
49 self.retval = self.Proc.poll() |
|
50 while not self.retval and not self.killed and not outeof: |
|
51 outchunk = self.fd.readline() |
|
52 if outchunk == '': outeof = True |
|
53 if self.callback : |
|
54 wx.CallAfter(self.callback,outchunk) |
|
55 self.retval=self.Proc.poll() |
|
56 if self.endcallback: |
|
57 err = self.Proc.wait() |
|
58 self.finished = True |
|
59 wx.CallAfter(self.endcallback, self.Proc.pid, self.retval) |
35 |
60 |
36 if wx.VERSION < (2, 6, 0): |
61 class ProcessLogger: |
37 def Bind(self, event, function, id = None): |
62 def __init__(self, logger, Command, finish_callback=None, no_stdout=False, no_stderr=False): |
38 if id is not None: |
63 self.logger = logger |
39 event(self, id, function) |
64 self.Command = Command |
40 else: |
65 self.finish_callback = finish_callback |
41 event(self, function) |
66 self.no_stdout = no_stdout |
42 |
67 self.no_stderr = no_stderr |
43 def __init__(self, input, handler=None): |
68 self.errlen = 0 |
44 if handler is None: |
69 self.outlen = 0 |
45 handler = self |
70 self.exitcode = None |
46 self.handler = handler |
71 self.outdata = "" |
47 handler.Bind(wx.EVT_IDLE, self.OnIdle) |
72 self.errdata = "" |
48 handler.Bind(wx.EVT_END_PROCESS, self.OnProcessEnded) |
73 self.finished = False |
49 |
74 |
50 input.reverse() # so we can pop |
75 self.Proc = subprocess.Popen(self.Command, |
51 self.input = input |
76 cwd = os.getcwd(), |
52 |
77 stdin = subprocess.PIPE, |
53 self.reset() |
78 stdout = subprocess.PIPE, |
|
79 stderr = subprocess.STDOUT) |
|
80 # stderr = subprocess.PIPE) |
54 |
81 |
55 def reset(self): |
82 self.outt = outputThread( |
56 self.process = None |
83 self.Proc, |
57 self.pid = -1 |
84 self.Proc.stdout, |
58 self.output = [] |
85 self.output, |
59 self.errors = [] |
86 self.finish) |
60 self.inputStream = None |
|
61 self.errorStream = None |
|
62 self.outputStream = None |
|
63 self.outputFunc = None |
|
64 self.errorsFunc = None |
|
65 self.finishedFunc = None |
|
66 self.finished = False |
|
67 self.responded = False |
|
68 |
87 |
69 def execute(self, cmd): |
88 self.outt.start() |
70 self.process = wx.Process(self.handler) |
|
71 self.process.Redirect() |
|
72 |
89 |
73 self.pid = wx.Execute(cmd, wx.EXEC_ASYNC, self.process) |
90 # self.errt = outputThread( |
|
91 # self.Proc, |
|
92 # self.Proc.stderr, |
|
93 # self.errors) |
|
94 # |
|
95 # self.errt.start() |
74 |
96 |
75 self.inputStream = self.process.GetOutputStream() |
97 def output(self,v): |
76 self.errorStream = self.process.GetErrorStream() |
98 self.outdata += v |
77 self.outputStream = self.process.GetInputStream() |
99 self.outlen += 1 |
|
100 if not self.no_stdout: |
|
101 self.logger.write(v) |
78 |
102 |
79 #self.OnIdle() |
103 def errors(self,v): |
80 wx.WakeUpIdle() |
104 self.errdata += v |
81 |
105 self.errlen += 1 |
82 def setCallbacks(self, output, errors, finished): |
106 if not self.no_stderr: |
83 self.outputFunc = output |
107 self.logger.write_warning(v) |
84 self.errorsFunc = errors |
|
85 self.finishedFunc = finished |
|
86 |
108 |
87 def detach(self): |
109 def finish(self, pid,ecode): |
88 if self.process is not None: |
110 self.finished = True |
89 self.process.CloseOutput() |
111 self.exitcode = ecode |
90 self.process.Detach() |
112 if self.exitcode != 0: |
91 self.process = None |
113 self.logger.write(self.Command + "\n") |
|
114 self.logger.write_warning("exited with status %s (pid %s)\n"%(str(ecode),str(pid))) |
|
115 if self.finish_callback is not None: |
|
116 self.finish_callback(self,ecode,pid) |
92 |
117 |
93 def kill(self): |
118 def kill(self): |
94 if self.process is not None: |
119 self.outt.killed = True |
95 self.process.CloseOutput() |
120 # self.errt.killed = True |
96 if wx.Process.Kill(self.pid, wx.SIGTERM) != wx.KILL_OK: |
121 if wx.Platform == '__WXMSW__': |
97 wx.Process.Kill(self.pid, wx.SIGKILL) |
122 PROCESS_TERMINATE = 1 |
98 self.process = None |
123 handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.Proc.pid) |
|
124 ctypes.windll.kernel32.TerminateProcess(handle, -1) |
|
125 ctypes.windll.kernel32.CloseHandle(handle) |
|
126 else: |
|
127 os.kill(self.Proc.pid) |
99 |
128 |
100 def updateStream(self, stream, data): |
129 def spin(self, timeout=None, out_limit=None, err_limit=None, keyword = None, kill_it = True): |
101 if stream and stream.CanRead(): |
130 count = 0 |
102 if not self.responded: |
131 while not self.finished: |
103 self.responded = True |
132 if err_limit and self.errlen > err_limit: |
104 text = stream.read() |
133 break |
105 data.append(text) |
134 if out_limit and self.outlen > out_limit: |
106 return text |
135 break |
107 else: |
136 if timeout: |
108 return None |
137 if count > timeout: |
109 |
138 break |
110 def updateInpStream(self, stream, input): |
139 count += 1 |
111 if stream and input: |
140 if keyword and self.outdata.find(keyword)!=-1: |
112 line = input.pop() |
141 break |
113 stream.write(line) |
|
114 |
|
115 def updateErrStream(self, stream, data): |
|
116 return self.updateStream(stream, data) |
|
117 |
|
118 def updateOutStream(self, stream, data): |
|
119 return self.updateStream(stream, data) |
|
120 |
|
121 def OnIdle(self, event=None): |
|
122 if self.process is not None: |
|
123 self.updateInpStream(self.inputStream, self.input) |
|
124 e = self.updateErrStream(self.errorStream, self.errors) |
|
125 if e is not None and self.errorsFunc is not None: |
|
126 wx.CallAfter(self.errorsFunc, e) |
|
127 o = self.updateOutStream(self.outputStream, self.output) |
|
128 if o is not None and self.outputFunc is not None: |
|
129 wx.CallAfter(self.outputFunc, o) |
|
130 |
|
131 #wx.WakeUpIdle() |
|
132 #time.sleep(0.001) |
|
133 |
|
134 def OnProcessEnded(self, event): |
|
135 self.OnIdle() |
|
136 pid,exitcode = event.GetPid(), event.GetExitCode() |
|
137 if self.process: |
|
138 self.process.Destroy() |
|
139 self.process = None |
|
140 |
|
141 self.finished = True |
|
142 |
|
143 # XXX doesn't work ??? |
|
144 #self.handler.Disconnect(-1, wx.EVT_IDLE) |
|
145 |
|
146 if self.finishedFunc: |
|
147 wx.CallAfter(self.finishedFunc, pid, exitcode) |
|
148 |
|
149 class ProcessRunner(wx.EvtHandler, ProcessRunnerMix): |
|
150 def __init__(self, input): |
|
151 wx.EvtHandler.__init__(self) |
|
152 ProcessRunnerMix.__init__(self, input) |
|
153 |
|
154 def wxPopen3(cmd, input, output, errors, finish, handler=None): |
|
155 p = ProcessRunnerMix(input, handler) |
|
156 p.setCallbacks(output, errors, finish) |
|
157 p.execute(cmd) |
|
158 return p |
|
159 |
|
160 def _test(): |
|
161 app = wx.PySimpleApp() |
|
162 f = wx.Frame(None, -1, 'asd')#, style=0) |
|
163 f.Show() |
|
164 |
|
165 def output(v): |
|
166 print 'OUTPUT:', v |
|
167 def errors(v): |
|
168 print 'ERRORS:', v |
|
169 def fin(): |
|
170 p.Close() |
|
171 f.Close() |
|
172 print 'FINISHED' |
|
173 |
|
174 |
|
175 def spin(p): |
|
176 while not p.finished: |
|
177 wx.Yield() |
142 wx.Yield() |
178 time.sleep(0.01) |
143 time.sleep(0.01) |
179 |
144 |
180 def evt(self, event): |
145 if not self.outt.finished and kill_it: |
181 input = [] |
146 self.kill() |
182 p = wxPopen3('''c:\\python23\\python.exe -c "print '*'*5000"''', |
|
183 input, output, errors, fin, f) |
|
184 print p.pid |
|
185 |
147 |
186 app.MainLoop() |
148 return [self.exitcode, self.outdata, self.errdata] |
187 |
149 |
188 if __name__ == '__main__': |
|
189 _test() |
|