|
1 #!/usr/bin/env python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 #This file is part of Beremiz, a Integrated Development Environment for |
|
5 #programming IEC 61131-3 automates supporting plcopen standard and CanFestival. |
|
6 # |
|
7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD |
|
8 # |
|
9 #See COPYING file for copyrights details. |
|
10 # |
|
11 #This library is free software; you can redistribute it and/or |
|
12 #modify it under the terms of the GNU General Public |
|
13 #License as published by the Free Software Foundation; either |
|
14 #version 2.1 of the License, or (at your option) any later version. |
|
15 # |
|
16 #This library is distributed in the hope that it will be useful, |
|
17 #but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
19 #General Public License for more details. |
|
20 # |
|
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 |
|
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
24 |
|
25 |
|
26 import time |
|
27 import wx |
|
28 import subprocess, ctypes |
|
29 from threading import Timer, Lock, Thread, Semaphore |
|
30 import os |
|
31 if os.name == 'posix': |
|
32 from signal import SIGTERM, SIGKILL |
|
33 |
|
34 |
|
35 class outputThread(Thread): |
|
36 """ |
|
37 Thread is used to print the output of a command to the stdout |
|
38 """ |
|
39 def __init__(self, Proc, fd, callback=None, endcallback=None): |
|
40 Thread.__init__(self) |
|
41 self.killed = False |
|
42 self.finished = False |
|
43 self.retval = None |
|
44 self.Proc = Proc |
|
45 self.callback = callback |
|
46 self.endcallback = endcallback |
|
47 self.fd = fd |
|
48 |
|
49 def run(self): |
|
50 outchunk = None |
|
51 self.retval = None |
|
52 while outchunk != '' and not self.killed : |
|
53 outchunk = self.fd.readline() |
|
54 if self.callback : self.callback(outchunk) |
|
55 while self.retval is None and not self.killed : |
|
56 self.retval = self.Proc.poll() |
|
57 outchunk = self.fd.readline() |
|
58 if self.callback : self.callback(outchunk) |
|
59 while outchunk != '' and not self.killed : |
|
60 outchunk = self.fd.readline() |
|
61 if self.callback : self.callback(outchunk) |
|
62 if self.endcallback: |
|
63 try: |
|
64 err = self.Proc.wait() |
|
65 except: |
|
66 err = self.retval |
|
67 self.finished = True |
|
68 self.endcallback(self.Proc.pid, err) |
|
69 |
|
70 class ProcessLogger: |
|
71 def __init__(self, logger, Command, finish_callback = None, |
|
72 no_stdout = False, no_stderr = False, no_gui = True, |
|
73 timeout = None, outlimit = None, errlimit = None, |
|
74 endlog = None, keyword = None, kill_it = False): |
|
75 self.logger = logger |
|
76 if not isinstance(Command, list): |
|
77 self.Command_str = Command |
|
78 self.Command = [] |
|
79 for i,word in enumerate(Command.replace("'",'"').split('"')): |
|
80 if i % 2 == 0: |
|
81 word = word.strip() |
|
82 if len(word) > 0: |
|
83 self.Command.extend(word.split()) |
|
84 else: |
|
85 self.Command.append(word) |
|
86 else: |
|
87 self.Command = Command |
|
88 self.Command_str = subprocess.list2cmdline(self.Command) |
|
89 |
|
90 self.finish_callback = finish_callback |
|
91 self.no_stdout = no_stdout |
|
92 self.no_stderr = no_stderr |
|
93 self.startupinfo = None |
|
94 self.errlen = 0 |
|
95 self.outlen = 0 |
|
96 self.errlimit = errlimit |
|
97 self.outlimit = outlimit |
|
98 self.exitcode = None |
|
99 self.outdata = [] |
|
100 self.errdata = [] |
|
101 self.keyword = keyword |
|
102 self.kill_it = kill_it |
|
103 self.finishsem = Semaphore(0) |
|
104 self.endlock = Lock() |
|
105 |
|
106 popenargs= { |
|
107 "cwd":os.getcwd(), |
|
108 "stdin":subprocess.PIPE, |
|
109 "stdout":subprocess.PIPE, |
|
110 "stderr":subprocess.PIPE} |
|
111 |
|
112 if no_gui == True and wx.Platform == '__WXMSW__': |
|
113 self.startupinfo = subprocess.STARTUPINFO() |
|
114 self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW |
|
115 popenargs["startupinfo"] = self.startupinfo |
|
116 elif wx.Platform == '__WXGTK__': |
|
117 popenargs["shell"] = False |
|
118 |
|
119 self.Proc = subprocess.Popen( self.Command, **popenargs ) |
|
120 |
|
121 self.outt = outputThread( |
|
122 self.Proc, |
|
123 self.Proc.stdout, |
|
124 self.output, |
|
125 self.finish) |
|
126 self.outt.start() |
|
127 |
|
128 self.errt = outputThread( |
|
129 self.Proc, |
|
130 self.Proc.stderr, |
|
131 self.errors) |
|
132 self.errt.start() |
|
133 |
|
134 Timer(timeout,self.endlog).start() |
|
135 |
|
136 def output(self,v): |
|
137 self.outdata.append(v) |
|
138 self.outlen += 1 |
|
139 if not self.no_stdout: |
|
140 self.logger.write(v) |
|
141 if (self.keyword and v.find(self.keyword)!=-1) or (self.outlimit and self.outlen > self.outlimit): |
|
142 self.endlog() |
|
143 |
|
144 def errors(self,v): |
|
145 self.errdata.append(v) |
|
146 self.errlen += 1 |
|
147 if not self.no_stderr: |
|
148 self.logger.write_warning(v) |
|
149 if self.errlimit and self.errlen > self.errlimit: |
|
150 self.endlog() |
|
151 |
|
152 def log_the_end(self,ecode,pid): |
|
153 self.logger.write(self.Command_str + "\n") |
|
154 self.logger.write_warning(_("exited with status %s (pid %s)\n")%(str(ecode),str(pid))) |
|
155 |
|
156 def finish(self, pid,ecode): |
|
157 self.exitcode = ecode |
|
158 if self.exitcode != 0: |
|
159 self.log_the_end(ecode,pid) |
|
160 if self.finish_callback is not None: |
|
161 self.finish_callback(self,ecode,pid) |
|
162 self.finishsem.release() |
|
163 |
|
164 def kill(self,gently=True): |
|
165 self.outt.killed = True |
|
166 self.errt.killed = True |
|
167 if wx.Platform == '__WXMSW__': |
|
168 PROCESS_TERMINATE = 1 |
|
169 handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.Proc.pid) |
|
170 ctypes.windll.kernel32.TerminateProcess(handle, -1) |
|
171 ctypes.windll.kernel32.CloseHandle(handle) |
|
172 else: |
|
173 if gently: |
|
174 sig=SIGTERM |
|
175 else: |
|
176 sig=SIGKILL |
|
177 try: |
|
178 os.kill(self.Proc.pid, sig) |
|
179 except: |
|
180 pass |
|
181 self.outt.join() |
|
182 self.errt.join() |
|
183 |
|
184 def endlog(self): |
|
185 if self.endlock.acquire(False): |
|
186 self.finishsem.release() |
|
187 if not self.outt.finished and self.kill_it: |
|
188 self.kill() |
|
189 |
|
190 |
|
191 def spin(self): |
|
192 self.finishsem.acquire() |
|
193 return [self.exitcode, "".join(self.outdata), "".join(self.errdata)] |
|
194 |