20 # |
20 # |
21 # You should have received a copy of the GNU General Public License |
21 # You should have received a copy of the GNU General Public License |
22 # along with this program; if not, write to the Free Software |
22 # along with this program; if not, write to the Free Software |
23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
24 |
24 |
|
25 import os |
|
26 import sys |
25 import time |
27 import time |
26 import wx |
28 import wx |
27 import subprocess, ctypes |
29 import subprocess |
|
30 import ctypes |
28 from threading import Timer, Lock, Thread, Semaphore |
31 from threading import Timer, Lock, Thread, Semaphore |
29 import os, sys |
|
30 if os.name == 'posix': |
32 if os.name == 'posix': |
31 from signal import SIGTERM, SIGKILL |
33 from signal import SIGTERM, SIGKILL |
32 |
34 |
33 |
35 |
34 class outputThread(Thread): |
36 class outputThread(Thread): |
46 self.fd = fd |
48 self.fd = fd |
47 |
49 |
48 def run(self): |
50 def run(self): |
49 outchunk = None |
51 outchunk = None |
50 self.retval = None |
52 self.retval = None |
51 while self.retval is None and not self.killed : |
53 while self.retval is None and not self.killed: |
52 if self.endcallback: |
54 if self.endcallback: |
53 self.retval = self.Proc.poll() |
55 self.retval = self.Proc.poll() |
54 else: |
56 else: |
55 self.retval = self.Proc.returncode |
57 self.retval = self.Proc.returncode |
56 |
58 |
57 outchunk = self.fd.readline() |
59 outchunk = self.fd.readline() |
58 if self.callback : self.callback(outchunk) |
60 if self.callback: |
59 while outchunk != '' and not self.killed : |
61 self.callback(outchunk) |
|
62 while outchunk != '' and not self.killed: |
60 outchunk = self.fd.readline() |
63 outchunk = self.fd.readline() |
61 if self.callback : self.callback(outchunk) |
64 if self.callback: |
|
65 self.callback(outchunk) |
62 if self.endcallback: |
66 if self.endcallback: |
63 try: |
67 try: |
64 err = self.Proc.wait() |
68 err = self.Proc.wait() |
65 except: |
69 except Exception: |
66 err = self.retval |
70 err = self.retval |
67 self.finished = True |
71 self.finished = True |
68 self.endcallback(self.Proc.pid, err) |
72 self.endcallback(self.Proc.pid, err) |
69 |
73 |
|
74 |
70 class ProcessLogger: |
75 class ProcessLogger: |
71 def __init__(self, logger, Command, finish_callback = None, |
76 def __init__(self, logger, Command, finish_callback=None, |
72 no_stdout = False, no_stderr = False, no_gui = True, |
77 no_stdout=False, no_stderr=False, no_gui=True, |
73 timeout = None, outlimit = None, errlimit = None, |
78 timeout=None, outlimit=None, errlimit=None, |
74 endlog = None, keyword = None, kill_it = False, cwd = None, |
79 endlog=None, keyword=None, kill_it=False, cwd=None, |
75 encoding = None): |
80 encoding=None): |
76 self.logger = logger |
81 self.logger = logger |
77 if not isinstance(Command, list): |
82 if not isinstance(Command, list): |
78 self.Command_str = Command |
83 self.Command_str = Command |
79 self.Command = [] |
84 self.Command = [] |
80 for i,word in enumerate(Command.replace("'",'"').split('"')): |
85 for i, word in enumerate(Command.replace("'", '"').split('"')): |
81 if i % 2 == 0: |
86 if i % 2 == 0: |
82 word = word.strip() |
87 word = word.strip() |
83 if len(word) > 0: |
88 if len(word) > 0: |
84 self.Command.extend(word.split()) |
89 self.Command.extend(word.split()) |
85 else: |
90 else: |
106 self.exitcode = None |
111 self.exitcode = None |
107 self.outdata = [] |
112 self.outdata = [] |
108 self.errdata = [] |
113 self.errdata = [] |
109 self.keyword = keyword |
114 self.keyword = keyword |
110 self.kill_it = kill_it |
115 self.kill_it = kill_it |
111 self.startsem = Semaphore(0) |
116 self.startsem = Semaphore(0) |
112 self.finishsem = Semaphore(0) |
117 self.finishsem = Semaphore(0) |
113 self.endlock = Lock() |
118 self.endlock = Lock() |
114 |
119 |
115 popenargs= { |
120 popenargs = { |
116 "cwd":os.getcwd() if cwd is None else cwd, |
121 "cwd": os.getcwd() if cwd is None else cwd, |
117 "stdin":subprocess.PIPE, |
122 "stdin": subprocess.PIPE, |
118 "stdout":subprocess.PIPE, |
123 "stdout": subprocess.PIPE, |
119 "stderr":subprocess.PIPE} |
124 "stderr": subprocess.PIPE |
120 |
125 } |
121 if no_gui == True and wx.Platform == '__WXMSW__': |
126 |
|
127 if no_gui and wx.Platform == '__WXMSW__': |
122 self.startupinfo = subprocess.STARTUPINFO() |
128 self.startupinfo = subprocess.STARTUPINFO() |
123 self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW |
129 self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW |
124 popenargs["startupinfo"] = self.startupinfo |
130 popenargs["startupinfo"] = self.startupinfo |
125 elif wx.Platform == '__WXGTK__': |
131 elif wx.Platform == '__WXGTK__': |
126 popenargs["shell"] = False |
132 popenargs["shell"] = False |
127 |
133 |
128 if timeout: |
134 if timeout: |
129 self.timeout = Timer(timeout,self.endlog) |
135 self.timeout = Timer(timeout, self.endlog) |
130 self.timeout.start() |
136 self.timeout.start() |
131 else: |
137 else: |
132 self.timeout = None |
138 self.timeout = None |
133 |
139 |
134 self.Proc = subprocess.Popen( self.Command, **popenargs ) |
140 self.Proc = subprocess.Popen(self.Command, **popenargs) |
135 |
141 |
136 self.outt = outputThread( |
142 self.outt = outputThread( |
137 self.Proc, |
143 self.Proc, |
138 self.Proc.stdout, |
144 self.Proc.stdout, |
139 self.output, |
145 self.output, |
145 self.Proc.stderr, |
151 self.Proc.stderr, |
146 self.errors) |
152 self.errors) |
147 self.errt.start() |
153 self.errt.start() |
148 self.startsem.release() |
154 self.startsem.release() |
149 |
155 |
150 |
156 def output(self, v): |
151 def output(self,v): |
|
152 self.outdata.append(v) |
157 self.outdata.append(v) |
153 self.outlen += 1 |
158 self.outlen += 1 |
154 if not self.no_stdout: |
159 if not self.no_stdout: |
155 self.logger.write(v) |
160 self.logger.write(v) |
156 if (self.keyword and v.find(self.keyword)!=-1) or (self.outlimit and self.outlen > self.outlimit): |
161 if (self.keyword and v.find(self.keyword) != -1) or (self.outlimit and self.outlen > self.outlimit): |
157 self.endlog() |
162 self.endlog() |
158 |
163 |
159 def errors(self,v): |
164 def errors(self, v): |
160 self.errdata.append(v) |
165 self.errdata.append(v) |
161 self.errlen += 1 |
166 self.errlen += 1 |
162 if not self.no_stderr: |
167 if not self.no_stderr: |
163 self.logger.write_warning(v) |
168 self.logger.write_warning(v) |
164 if self.errlimit and self.errlen > self.errlimit: |
169 if self.errlimit and self.errlen > self.errlimit: |
165 self.endlog() |
170 self.endlog() |
166 |
171 |
167 def log_the_end(self,ecode,pid): |
172 def log_the_end(self, ecode, pid): |
168 self.logger.write(self.Command_str + "\n") |
173 self.logger.write(self.Command_str + "\n") |
169 self.logger.write_warning(_("exited with status {a1} (pid {a2})\n").format(a1 = str(ecode), a2 = str(pid))) |
174 self.logger.write_warning(_("exited with status {a1} (pid {a2})\n").format(a1=str(ecode), a2=str(pid))) |
170 |
175 |
171 def finish(self, pid,ecode): |
176 def finish(self, pid, ecode): |
172 # avoid running function before start is finished |
177 # avoid running function before start is finished |
173 self.startsem.acquire() |
178 self.startsem.acquire() |
174 if self.timeout: |
179 if self.timeout: |
175 self.timeout.cancel() |
180 self.timeout.cancel() |
176 self.exitcode = ecode |
181 self.exitcode = ecode |
177 if self.exitcode != 0: |
182 if self.exitcode != 0: |
178 self.log_the_end(ecode,pid) |
183 self.log_the_end(ecode, pid) |
179 if self.finish_callback is not None: |
184 if self.finish_callback is not None: |
180 self.finish_callback(self,ecode,pid) |
185 self.finish_callback(self, ecode, pid) |
181 self.errt.join() |
186 self.errt.join() |
182 self.finishsem.release() |
187 self.finishsem.release() |
183 |
188 |
184 def kill(self,gently=True): |
189 def kill(self, gently=True): |
185 # avoid running kill before start is finished |
190 # avoid running kill before start is finished |
186 self.startsem.acquire() |
191 self.startsem.acquire() |
187 self.startsem.release() |
192 self.startsem.release() |
188 |
193 |
189 self.outt.killed = True |
194 self.outt.killed = True |
190 self.errt.killed = True |
195 self.errt.killed = True |
191 if wx.Platform == '__WXMSW__': |
196 if wx.Platform == '__WXMSW__': |
192 PROCESS_TERMINATE = 1 |
197 PROCESS_TERMINATE = 1 |
193 handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.Proc.pid) |
198 handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.Proc.pid) |
194 ctypes.windll.kernel32.TerminateProcess(handle, -1) |
199 ctypes.windll.kernel32.TerminateProcess(handle, -1) |
195 ctypes.windll.kernel32.CloseHandle(handle) |
200 ctypes.windll.kernel32.CloseHandle(handle) |
196 else: |
201 else: |
197 if gently: |
202 if gently: |
198 sig=SIGTERM |
203 sig = SIGTERM |
199 else: |
204 else: |
200 sig=SIGKILL |
205 sig = SIGKILL |
201 try: |
206 try: |
202 os.kill(self.Proc.pid, sig) |
207 os.kill(self.Proc.pid, sig) |
203 except: |
208 except Exception: |
204 pass |
209 pass |
205 self.outt.join() |
210 self.outt.join() |
206 self.errt.join() |
211 self.errt.join() |
207 |
212 |
208 def endlog(self): |
213 def endlog(self): |
209 if self.endlock.acquire(False): |
214 if self.endlock.acquire(False): |
210 if not self.outt.finished and self.kill_it: |
215 if not self.outt.finished and self.kill_it: |
211 self.kill() |
216 self.kill() |
212 self.finishsem.release() |
217 self.finishsem.release() |
213 |
|
214 |
218 |
215 def spin(self): |
219 def spin(self): |
216 self.finishsem.acquire() |
220 self.finishsem.acquire() |
217 return [self.exitcode, "".join(self.outdata), "".join(self.errdata)] |
221 return [self.exitcode, "".join(self.outdata), "".join(self.errdata)] |
218 |
|