# HG changeset patch # User etisserant # Date 1190302372 -7200 # Node ID 6b30cfee163e2ff9f4db1107728439d727d6e763 # Parent fd45c291fed021754ab409f3f88b7261b439c606 Enhanced and really multi-platform process logging. Use wxProcess and wxExecute instead of python popen3 (unix only). diff -r fd45c291fed0 -r 6b30cfee163e Beremiz.py --- a/Beremiz.py Thu Sep 20 17:30:32 2007 +0200 +++ b/Beremiz.py Thu Sep 20 17:32:52 2007 +0200 @@ -28,10 +28,14 @@ import types +import time + import os, re, platform, sys, time, traceback, getopt, commands from plugger import PluginsRoot +from wxPopen import wxPopen3 + class LogPseudoFile: """ Base class for file like objects to facilitate StdOut for the Shell.""" def __init__(self, output = None): @@ -63,40 +67,39 @@ def isatty(self): return false - def LogCommand(self, Command, sz_limit = 100): - - import os, popen2, select, signal - - child = popen2.Popen3(Command, 1) # capture stdout and stderr from command - child.tochild.close() # don't need to talk to child - outfile = child.fromchild - outfd = outfile.fileno() - errfile = child.childerr - errfd = errfile.fileno() - outdata = errdata = '' - outeof = erreof = 0 - outlen = errlen = 0 - while 1: - ready = select.select([outfd,errfd],[],[]) # wait for input - if outfd in ready[0]: - outchunk = outfile.readline() - if outchunk == '': outeof = 1 - else : outlen += 1 - outdata += outchunk - self.write(outchunk) - if errfd in ready[0]: - errchunk = errfile.readline() - if errchunk == '': erreof = 1 - else : errlen += 1 - errdata += errchunk - self.write_warning(errchunk) - if outeof and erreof : break - if errlen > sz_limit or outlen > sz_limit : - os.kill(child.pid, signal.SIGTERM) - self.write_error("Output size reached limit -- killed\n") - break - err = child.wait() - return (err, outdata, errdata) + def LogCommand(self, Command, sz_limit = 100, no_stdout=False): + self.errlen = 0 + self.exitcode = None + self.outdata = "" + self.errdata = "" + + def output(v): + self.outdata += v + if not no_stdout: + self.write(v) + + def errors(v): + self.errdata += v + self.errlen += 1 + if self.errlen > sz_limit: + p.kill() + self.write_warning(v) + + def fin(pid,ecode): + self.exitcode = ecode + if self.exitcode != 0: + self.write("pid %d exited with status %d\n"%(pid,ecode)) + + def spin(p): + while not p.finished: + wx.Yield() + time.sleep(0.01) + + input = [] + p = wxPopen3(Command, input, output, errors, fin, self.output) + spin(p) + + return (self.exitcode, self.outdata, self.errdata) [ID_BEREMIZ, ID_BEREMIZMAINSPLITTER, ID_BEREMIZSECONDSPLITTER, ID_BEREMIZLEFTPANEL, diff -r fd45c291fed0 -r 6b30cfee163e wxPopen.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wxPopen.py Thu Sep 20 17:32:52 2007 +0200 @@ -0,0 +1,152 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#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 library 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.1 of the License, or (at your option) any later version. +# +#This library 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 library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# +# based on wxPopen.py from boa-constructor +# + +import time +from StringIO import StringIO + +from wxPython.wx import * + +class ProcessRunnerMix: + def __init__(self, input, handler=None): + if handler is None: + handler = self + self.handler = handler + EVT_IDLE(handler, self.OnIdle) + EVT_END_PROCESS(handler, -1, self.OnProcessEnded) + + input.reverse() # so we can pop + self.input = input + + self.reset() + + def reset(self): + self.process = None + self.pid = -1 + self.output = [] + self.errors = [] + self.inputStream = None + self.errorStream = None + self.outputStream = None + self.outputFunc = None + self.errorsFunc = None + self.finishedFunc = None + self.finished = false + self.responded = false + + def execute(self, cmd): + self.process = wxProcess(self.handler) + self.process.Redirect() + + self.pid = wxExecute(cmd, wxEXEC_NOHIDE, self.process) + + self.inputStream = self.process.GetOutputStream() + self.errorStream = self.process.GetErrorStream() + self.outputStream = self.process.GetInputStream() + + #self.OnIdle() + wxWakeUpIdle() + + def setCallbacks(self, output, errors, finished): + self.outputFunc = output + self.errorsFunc = errors + self.finishedFunc = finished + + def detach(self): + if self.process is not None: + self.process.CloseOutput() + self.process.Detach() + self.process = None + + def kill(self): + if self.process is not None: + self.process.CloseOutput() + if wxProcess_Kill(self.pid, wxSIGTERM) != wxKILL_OK: + wxProcess_Kill(self.pid, wxSIGKILL) + self.process = None + + def updateStream(self, stream, data): + if stream and stream.CanRead(): + if not self.responded: + self.responded = true + text = stream.read() + data.append(text) + return text + else: + return None + + def updateInpStream(self, stream, input): + if stream and input: + line = input.pop() + stream.write(line) + + def updateErrStream(self, stream, data): + return self.updateStream(stream, data) + + def updateOutStream(self, stream, data): + return self.updateStream(stream, data) + + def OnIdle(self, event=None): + if self.process is not None: + self.updateInpStream(self.inputStream, self.input) + e = self.updateErrStream(self.errorStream, self.errors) + if e is not None and self.errorsFunc is not None: + wxCallAfter(self.errorsFunc, e) + o = self.updateOutStream(self.outputStream, self.output) + if o is not None and self.outputFunc is not None: + wxCallAfter(self.outputFunc, o) + + #wxWakeUpIdle() + #time.sleep(0.001) + + def OnProcessEnded(self, event): + self.OnIdle() + pid,exitcode = event.GetPid(), event.GetExitCode() + if self.process: + self.process.Destroy() + self.process = None + + self.finished = true + + # XXX doesn't work ??? + #self.handler.Disconnect(-1, wxEVT_IDLE) + + if self.finishedFunc: + wxCallAfter(self.finishedFunc, pid, exitcode) + +class ProcessRunner(wxEvtHandler, ProcessRunnerMix): + def __init__(self, input): + wxEvtHandler.__init__(self) + ProcessRunnerMix.__init__(self, input) + +def wxPopen3(cmd, input, output, errors, finish, handler=None): + p = ProcessRunnerMix(input, handler) + p.setCallbacks(output, errors, finish) + p.execute(cmd) + return p + + \ No newline at end of file