author | Laurent Bessard |
Thu, 10 Oct 2013 17:04:23 +0200 | |
changeset 1355 | 9183fb765f16 |
parent 958 | 511bf048b8b7 |
child 1407 | cf3d2b53dd68 |
permissions | -rw-r--r-- |
79 | 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 |
|
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
27 |
import wx |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
28 |
import subprocess, ctypes |
704 | 29 |
from threading import Timer, Lock, Thread, Semaphore |
788 | 30 |
import os, sys |
235 | 31 |
if os.name == 'posix': |
32 |
from signal import SIGTERM, SIGKILL |
|
79 | 33 |
|
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
34 |
|
704 | 35 |
class outputThread(Thread): |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
36 |
""" |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
37 |
Thread is used to print the output of a command to the stdout |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
38 |
""" |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
39 |
def __init__(self, Proc, fd, callback=None, endcallback=None): |
704 | 40 |
Thread.__init__(self) |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
41 |
self.killed = False |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
42 |
self.finished = False |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
43 |
self.retval = None |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
44 |
self.Proc = Proc |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
45 |
self.callback = callback |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
46 |
self.endcallback = endcallback |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
47 |
self.fd = fd |
79 | 48 |
|
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
49 |
def run(self): |
162
bf3eac08a96b
Try to fix strange wxPopen behavior. Feedback appreciated.
etisserant
parents:
154
diff
changeset
|
50 |
outchunk = None |
149 | 51 |
self.retval = None |
162
bf3eac08a96b
Try to fix strange wxPopen behavior. Feedback appreciated.
etisserant
parents:
154
diff
changeset
|
52 |
while outchunk != '' and not self.killed : |
bf3eac08a96b
Try to fix strange wxPopen behavior. Feedback appreciated.
etisserant
parents:
154
diff
changeset
|
53 |
outchunk = self.fd.readline() |
bf3eac08a96b
Try to fix strange wxPopen behavior. Feedback appreciated.
etisserant
parents:
154
diff
changeset
|
54 |
if self.callback : self.callback(outchunk) |
149 | 55 |
while self.retval is None and not self.killed : |
56 |
self.retval = self.Proc.poll() |
|
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
57 |
outchunk = self.fd.readline() |
162
bf3eac08a96b
Try to fix strange wxPopen behavior. Feedback appreciated.
etisserant
parents:
154
diff
changeset
|
58 |
if self.callback : self.callback(outchunk) |
bf3eac08a96b
Try to fix strange wxPopen behavior. Feedback appreciated.
etisserant
parents:
154
diff
changeset
|
59 |
while outchunk != '' and not self.killed : |
bf3eac08a96b
Try to fix strange wxPopen behavior. Feedback appreciated.
etisserant
parents:
154
diff
changeset
|
60 |
outchunk = self.fd.readline() |
704 | 61 |
if self.callback : self.callback(outchunk) |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
62 |
if self.endcallback: |
130
9af34a1d33b7
fixed short process wainting bug. Seems wait() fail when process already finisshed... TO BE CONFIRMED.
greg
parents:
128
diff
changeset
|
63 |
try: |
232 | 64 |
err = self.Proc.wait() |
130
9af34a1d33b7
fixed short process wainting bug. Seems wait() fail when process already finisshed... TO BE CONFIRMED.
greg
parents:
128
diff
changeset
|
65 |
except: |
149 | 66 |
err = self.retval |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
67 |
self.finished = True |
421 | 68 |
self.endcallback(self.Proc.pid, err) |
69 |
||
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
70 |
class ProcessLogger: |
704 | 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, |
|
958
511bf048b8b7
Added CWD to ProcessLogger, and make sure local runtime have CWD setup correctly
Edouard Tisserant
parents:
788
diff
changeset
|
74 |
endlog = None, keyword = None, kill_it = False, cwd = None): |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
75 |
self.logger = logger |
424 | 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) |
|
788 | 89 |
|
90 |
self.Command = map(lambda x: x.encode(sys.getfilesystemencoding()), |
|
91 |
self.Command) |
|
92 |
||
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
93 |
self.finish_callback = finish_callback |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
94 |
self.no_stdout = no_stdout |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
95 |
self.no_stderr = no_stderr |
111
e2e498333fbc
fixed display/hide console when launch external programs
greg
parents:
110
diff
changeset
|
96 |
self.startupinfo = None |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
97 |
self.errlen = 0 |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
98 |
self.outlen = 0 |
704 | 99 |
self.errlimit = errlimit |
100 |
self.outlimit = outlimit |
|
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
101 |
self.exitcode = None |
704 | 102 |
self.outdata = [] |
103 |
self.errdata = [] |
|
104 |
self.keyword = keyword |
|
105 |
self.kill_it = kill_it |
|
106 |
self.finishsem = Semaphore(0) |
|
107 |
self.endlock = Lock() |
|
111
e2e498333fbc
fixed display/hide console when launch external programs
greg
parents:
110
diff
changeset
|
108 |
|
128
3db703a78e9c
fixed subprocess launching on linux (avoid use of undefied self.startupinfo and use use Shell=True (bash will split arguments))
greg
parents:
112
diff
changeset
|
109 |
popenargs= { |
958
511bf048b8b7
Added CWD to ProcessLogger, and make sure local runtime have CWD setup correctly
Edouard Tisserant
parents:
788
diff
changeset
|
110 |
"cwd":os.getcwd() if cwd is None else cwd, |
128
3db703a78e9c
fixed subprocess launching on linux (avoid use of undefied self.startupinfo and use use Shell=True (bash will split arguments))
greg
parents:
112
diff
changeset
|
111 |
"stdin":subprocess.PIPE, |
3db703a78e9c
fixed subprocess launching on linux (avoid use of undefied self.startupinfo and use use Shell=True (bash will split arguments))
greg
parents:
112
diff
changeset
|
112 |
"stdout":subprocess.PIPE, |
3db703a78e9c
fixed subprocess launching on linux (avoid use of undefied self.startupinfo and use use Shell=True (bash will split arguments))
greg
parents:
112
diff
changeset
|
113 |
"stderr":subprocess.PIPE} |
424 | 114 |
|
111
e2e498333fbc
fixed display/hide console when launch external programs
greg
parents:
110
diff
changeset
|
115 |
if no_gui == True and wx.Platform == '__WXMSW__': |
e2e498333fbc
fixed display/hide console when launch external programs
greg
parents:
110
diff
changeset
|
116 |
self.startupinfo = subprocess.STARTUPINFO() |
e2e498333fbc
fixed display/hide console when launch external programs
greg
parents:
110
diff
changeset
|
117 |
self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW |
128
3db703a78e9c
fixed subprocess launching on linux (avoid use of undefied self.startupinfo and use use Shell=True (bash will split arguments))
greg
parents:
112
diff
changeset
|
118 |
popenargs["startupinfo"] = self.startupinfo |
3db703a78e9c
fixed subprocess launching on linux (avoid use of undefied self.startupinfo and use use Shell=True (bash will split arguments))
greg
parents:
112
diff
changeset
|
119 |
elif wx.Platform == '__WXGTK__': |
704 | 120 |
popenargs["shell"] = False |
111
e2e498333fbc
fixed display/hide console when launch external programs
greg
parents:
110
diff
changeset
|
121 |
|
128
3db703a78e9c
fixed subprocess launching on linux (avoid use of undefied self.startupinfo and use use Shell=True (bash will split arguments))
greg
parents:
112
diff
changeset
|
122 |
self.Proc = subprocess.Popen( self.Command, **popenargs ) |
112 | 123 |
|
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
124 |
self.outt = outputThread( |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
125 |
self.Proc, |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
126 |
self.Proc.stdout, |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
127 |
self.output, |
232 | 128 |
self.finish) |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
129 |
self.outt.start() |
79 | 130 |
|
112 | 131 |
self.errt = outputThread( |
132 |
self.Proc, |
|
133 |
self.Proc.stderr, |
|
134 |
self.errors) |
|
232 | 135 |
self.errt.start() |
79 | 136 |
|
723 | 137 |
if timeout: |
138 |
self.timeout = Timer(timeout,self.endlog) |
|
139 |
self.timeout.start() |
|
140 |
else: |
|
141 |
self.timeout = None |
|
704 | 142 |
|
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
143 |
def output(self,v): |
704 | 144 |
self.outdata.append(v) |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
145 |
self.outlen += 1 |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
146 |
if not self.no_stdout: |
686
e4e1da75d411
More robust Logger, now resist to flooding.
Edouard Tisserant
parents:
424
diff
changeset
|
147 |
self.logger.write(v) |
704 | 148 |
if (self.keyword and v.find(self.keyword)!=-1) or (self.outlimit and self.outlen > self.outlimit): |
149 |
self.endlog() |
|
421 | 150 |
|
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
151 |
def errors(self,v): |
704 | 152 |
self.errdata.append(v) |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
153 |
self.errlen += 1 |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
154 |
if not self.no_stderr: |
686
e4e1da75d411
More robust Logger, now resist to flooding.
Edouard Tisserant
parents:
424
diff
changeset
|
155 |
self.logger.write_warning(v) |
704 | 156 |
if self.errlimit and self.errlen > self.errlimit: |
157 |
self.endlog() |
|
79 | 158 |
|
232 | 159 |
def log_the_end(self,ecode,pid): |
160 |
self.logger.write(self.Command_str + "\n") |
|
361 | 161 |
self.logger.write_warning(_("exited with status %s (pid %s)\n")%(str(ecode),str(pid))) |
232 | 162 |
|
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
163 |
def finish(self, pid,ecode): |
723 | 164 |
if self.timeout: self.timeout.cancel() |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
165 |
self.exitcode = ecode |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
166 |
if self.exitcode != 0: |
686
e4e1da75d411
More robust Logger, now resist to flooding.
Edouard Tisserant
parents:
424
diff
changeset
|
167 |
self.log_the_end(ecode,pid) |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
168 |
if self.finish_callback is not None: |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
169 |
self.finish_callback(self,ecode,pid) |
704 | 170 |
self.finishsem.release() |
79 | 171 |
|
235 | 172 |
def kill(self,gently=True): |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
173 |
self.outt.killed = True |
112 | 174 |
self.errt.killed = True |
110
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
175 |
if wx.Platform == '__WXMSW__': |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
176 |
PROCESS_TERMINATE = 1 |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
177 |
handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.Proc.pid) |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
178 |
ctypes.windll.kernel32.TerminateProcess(handle, -1) |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
179 |
ctypes.windll.kernel32.CloseHandle(handle) |
a05e8b30c024
Fixed way apps are launched in parralel with single log window... Tested in win32 only.
etisserant
parents:
89
diff
changeset
|
180 |
else: |
235 | 181 |
if gently: |
182 |
sig=SIGTERM |
|
183 |
else: |
|
184 |
sig=SIGKILL |
|
154
f3134b2c6d92
Fixed killing app on Linux in wxPopen. Do not use shell anymore. Command line is splitted into args, taking care of double and simple cotes. To be tested on win32.
etisserant
parents:
151
diff
changeset
|
185 |
try: |
235 | 186 |
os.kill(self.Proc.pid, sig) |
154
f3134b2c6d92
Fixed killing app on Linux in wxPopen. Do not use shell anymore. Command line is splitted into args, taking care of double and simple cotes. To be tested on win32.
etisserant
parents:
151
diff
changeset
|
187 |
except: |
f3134b2c6d92
Fixed killing app on Linux in wxPopen. Do not use shell anymore. Command line is splitted into args, taking care of double and simple cotes. To be tested on win32.
etisserant
parents:
151
diff
changeset
|
188 |
pass |
232 | 189 |
self.outt.join() |
190 |
self.errt.join() |
|
79 | 191 |
|
704 | 192 |
def endlog(self): |
193 |
if self.endlock.acquire(False): |
|
194 |
self.finishsem.release() |
|
195 |
if not self.outt.finished and self.kill_it: |
|
196 |
self.kill() |
|
89 | 197 |
|
704 | 198 |
|
199 |
def spin(self): |
|
200 |
self.finishsem.acquire() |
|
201 |
return [self.exitcode, "".join(self.outdata), "".join(self.errdata)] |
|
89 | 202 |