# This file is part of Beremiz, a Integrated Development Environment for
# programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
# See COPYING file for copyrights details.
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import subprocess, ctypes
from threading import Timer, Lock, Thread, Semaphore
from signal import SIGTERM, SIGKILL
class outputThread(Thread):
Thread is used to print the output of a command to the stdout
def __init__(self, Proc, fd, callback=None, endcallback=None):
self.endcallback = endcallback
while self.retval is None and not self.killed :
self.retval = self.Proc.poll()
self.retval = self.Proc.returncode
outchunk = self.fd.readline()
if self.callback : self.callback(outchunk)
while outchunk != '' and not self.killed :
outchunk = self.fd.readline()
if self.callback : self.callback(outchunk)
self.endcallback(self.Proc.pid, err)
def __init__(self, logger, Command, finish_callback = None,
no_stdout = False, no_stderr = False, no_gui = True,
timeout = None, outlimit = None, errlimit = None,
endlog = None, keyword = None, kill_it = False, cwd = None,
if not isinstance(Command, list):
self.Command_str = Command
for i,word in enumerate(Command.replace("'",'"').split('"')):
self.Command.extend(word.split())
self.Command.append(word)
self.Command_str = subprocess.list2cmdline(self.Command)
fsencoding = sys.getfilesystemencoding()
self.Command = [self.Command[0].encode(fsencoding)]+map(
lambda x: x.encode(encoding), self.Command[1:])
self.finish_callback = finish_callback
self.no_stdout = no_stdout
self.no_stderr = no_stderr
self.startsem = Semaphore(0)
self.finishsem = Semaphore(0)
"cwd":os.getcwd() if cwd is None else cwd,
"stdout":subprocess.PIPE,
"stderr":subprocess.PIPE}
if no_gui == True and wx.Platform == '__WXMSW__':
self.startupinfo = subprocess.STARTUPINFO()
self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
popenargs["startupinfo"] = self.startupinfo
elif wx.Platform == '__WXGTK__':
popenargs["shell"] = False
self.timeout = Timer(timeout,self.endlog)
self.Proc = subprocess.Popen( self.Command, **popenargs )
self.outt = outputThread(
self.errt = outputThread(
if (self.keyword and v.find(self.keyword)!=-1) or (self.outlimit and self.outlen > self.outlimit):
self.logger.write_warning(v)
if self.errlimit and self.errlen > self.errlimit:
def log_the_end(self,ecode,pid):
self.logger.write(self.Command_str + "\n")
self.logger.write_warning(_("exited with status %s (pid %s)\n")%(str(ecode),str(pid)))
def finish(self, pid,ecode):
# avoid running function before start is finished
self.log_the_end(ecode,pid)
if self.finish_callback is not None:
self.finish_callback(self,ecode,pid)
def kill(self,gently=True):
# avoid running kill before start is finished
if wx.Platform == '__WXMSW__':
handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.Proc.pid)
ctypes.windll.kernel32.TerminateProcess(handle, -1)
ctypes.windll.kernel32.CloseHandle(handle)
os.kill(self.Proc.pid, sig)
if self.endlock.acquire(False):
if not self.outt.finished and self.kill_it:
return [self.exitcode, "".join(self.outdata), "".join(self.errdata)]