ProcessLogger.py
changeset 722 a94f361fc42e
parent 704 5993b16fe2d0
child 723 cd5a51829416
equal deleted inserted replaced
721:ecf4d203c4d4 722:a94f361fc42e
       
     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