util/ProcessLogger.py
changeset 1784 64beb9e9c749
parent 1780 c52d1460cea8
child 1831 56b48961cc68
equal deleted inserted replaced
1729:31e63e25b4cc 1784:64beb9e9c749
    20 #
    20 #
    21 # You should have received a copy of the GNU General Public License
    21 # You should have received a copy of the GNU General Public License
    22 # along with this program; if not, write to the Free Software
    22 # along with this program; if not, write to the Free Software
    23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
    23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
    24 
    24 
       
    25 import os
       
    26 import sys
    25 import time
    27 import time
    26 import wx
    28 import wx
    27 import subprocess, ctypes
    29 import subprocess
       
    30 import ctypes
    28 from threading import Timer, Lock, Thread, Semaphore
    31 from threading import Timer, Lock, Thread, Semaphore
    29 import os, sys
       
    30 if os.name == 'posix':
    32 if os.name == 'posix':
    31     from signal import SIGTERM, SIGKILL
    33     from signal import SIGTERM, SIGKILL
    32 
    34 
    33 
    35 
    34 class outputThread(Thread):
    36 class outputThread(Thread):
    46         self.fd = fd
    48         self.fd = fd
    47 
    49 
    48     def run(self):
    50     def run(self):
    49         outchunk = None
    51         outchunk = None
    50         self.retval = None
    52         self.retval = None
    51         while self.retval is None and not self.killed :
    53         while self.retval is None and not self.killed:
    52             if self.endcallback:
    54             if self.endcallback:
    53                 self.retval = self.Proc.poll()
    55                 self.retval = self.Proc.poll()
    54             else:
    56             else:
    55                 self.retval = self.Proc.returncode
    57                 self.retval = self.Proc.returncode
    56                 
    58 
    57             outchunk = self.fd.readline()
    59             outchunk = self.fd.readline()
    58             if self.callback : self.callback(outchunk)
    60             if self.callback:
    59         while outchunk != '' and not self.killed :
    61                 self.callback(outchunk)
       
    62         while outchunk != '' and not self.killed:
    60             outchunk = self.fd.readline()
    63             outchunk = self.fd.readline()
    61             if self.callback : self.callback(outchunk)
    64             if self.callback:
       
    65                 self.callback(outchunk)
    62         if self.endcallback:
    66         if self.endcallback:
    63             try:
    67             try:
    64                 err = self.Proc.wait()
    68                 err = self.Proc.wait()
    65             except:
    69             except Exception:
    66                 err = self.retval
    70                 err = self.retval
    67             self.finished = True
    71             self.finished = True
    68             self.endcallback(self.Proc.pid, err)
    72             self.endcallback(self.Proc.pid, err)
    69 
    73 
       
    74 
    70 class ProcessLogger:
    75 class ProcessLogger:
    71     def __init__(self, logger, Command, finish_callback = None,
    76     def __init__(self, logger, Command, finish_callback=None,
    72                  no_stdout = False, no_stderr = False, no_gui = True,
    77                  no_stdout=False, no_stderr=False, no_gui=True,
    73                  timeout = None, outlimit = None, errlimit = None,
    78                  timeout=None, outlimit=None, errlimit=None,
    74                  endlog = None, keyword = None, kill_it = False, cwd = None,
    79                  endlog=None, keyword=None, kill_it=False, cwd=None,
    75                  encoding = None):
    80                  encoding=None):
    76         self.logger = logger
    81         self.logger = logger
    77         if not isinstance(Command, list):
    82         if not isinstance(Command, list):
    78             self.Command_str = Command
    83             self.Command_str = Command
    79             self.Command = []
    84             self.Command = []
    80             for i,word in enumerate(Command.replace("'",'"').split('"')):
    85             for i, word in enumerate(Command.replace("'", '"').split('"')):
    81                 if i % 2 == 0:
    86                 if i % 2 == 0:
    82                     word = word.strip()
    87                     word = word.strip()
    83                     if len(word) > 0:
    88                     if len(word) > 0:
    84                         self.Command.extend(word.split())
    89                         self.Command.extend(word.split())
    85                 else:
    90                 else:
   106         self.exitcode = None
   111         self.exitcode = None
   107         self.outdata = []
   112         self.outdata = []
   108         self.errdata = []
   113         self.errdata = []
   109         self.keyword = keyword
   114         self.keyword = keyword
   110         self.kill_it = kill_it
   115         self.kill_it = kill_it
   111         self.startsem = Semaphore(0)        
   116         self.startsem = Semaphore(0)
   112         self.finishsem = Semaphore(0)
   117         self.finishsem = Semaphore(0)
   113         self.endlock = Lock()
   118         self.endlock = Lock()
   114 
   119 
   115         popenargs= {
   120         popenargs = {
   116                "cwd":os.getcwd() if cwd is None else cwd,
   121                "cwd":    os.getcwd() if cwd is None else cwd,
   117                "stdin":subprocess.PIPE,
   122                "stdin":  subprocess.PIPE,
   118                "stdout":subprocess.PIPE,
   123                "stdout": subprocess.PIPE,
   119                "stderr":subprocess.PIPE}
   124                "stderr": subprocess.PIPE
   120 
   125         }
   121         if no_gui == True and wx.Platform == '__WXMSW__':
   126 
       
   127         if no_gui and wx.Platform == '__WXMSW__':
   122             self.startupinfo = subprocess.STARTUPINFO()
   128             self.startupinfo = subprocess.STARTUPINFO()
   123             self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
   129             self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
   124             popenargs["startupinfo"] = self.startupinfo
   130             popenargs["startupinfo"] = self.startupinfo
   125         elif wx.Platform == '__WXGTK__':
   131         elif wx.Platform == '__WXGTK__':
   126             popenargs["shell"] = False
   132             popenargs["shell"] = False
   127 
   133 
   128         if timeout:
   134         if timeout:
   129             self.timeout = Timer(timeout,self.endlog)
   135             self.timeout = Timer(timeout, self.endlog)
   130             self.timeout.start()
   136             self.timeout.start()
   131         else:
   137         else:
   132             self.timeout = None
   138             self.timeout = None
   133             
   139 
   134         self.Proc = subprocess.Popen( self.Command, **popenargs )
   140         self.Proc = subprocess.Popen(self.Command, **popenargs)
   135 
   141 
   136         self.outt = outputThread(
   142         self.outt = outputThread(
   137                       self.Proc,
   143                       self.Proc,
   138                       self.Proc.stdout,
   144                       self.Proc.stdout,
   139                       self.output,
   145                       self.output,
   145                       self.Proc.stderr,
   151                       self.Proc.stderr,
   146                       self.errors)
   152                       self.errors)
   147         self.errt.start()
   153         self.errt.start()
   148         self.startsem.release()
   154         self.startsem.release()
   149 
   155 
   150 
   156     def output(self, v):
   151     def output(self,v):
       
   152         self.outdata.append(v)
   157         self.outdata.append(v)
   153         self.outlen += 1
   158         self.outlen += 1
   154         if not self.no_stdout:
   159         if not self.no_stdout:
   155             self.logger.write(v)
   160             self.logger.write(v)
   156         if (self.keyword and v.find(self.keyword)!=-1) or (self.outlimit and self.outlen > self.outlimit):
   161         if (self.keyword and v.find(self.keyword) != -1) or (self.outlimit and self.outlen > self.outlimit):
   157             self.endlog()
   162             self.endlog()
   158 
   163 
   159     def errors(self,v):
   164     def errors(self, v):
   160         self.errdata.append(v)
   165         self.errdata.append(v)
   161         self.errlen += 1
   166         self.errlen += 1
   162         if not self.no_stderr:
   167         if not self.no_stderr:
   163             self.logger.write_warning(v)
   168             self.logger.write_warning(v)
   164         if self.errlimit and self.errlen > self.errlimit:
   169         if self.errlimit and self.errlen > self.errlimit:
   165             self.endlog()
   170             self.endlog()
   166 
   171 
   167     def log_the_end(self,ecode,pid):
   172     def log_the_end(self, ecode, pid):
   168         self.logger.write(self.Command_str + "\n")
   173         self.logger.write(self.Command_str + "\n")
   169         self.logger.write_warning(_("exited with status {a1} (pid {a2})\n").format(a1 = str(ecode), a2 = str(pid)))
   174         self.logger.write_warning(_("exited with status {a1} (pid {a2})\n").format(a1=str(ecode), a2=str(pid)))
   170 
   175 
   171     def finish(self, pid,ecode):
   176     def finish(self, pid, ecode):
   172         # avoid running function before start is finished        
   177         # avoid running function before start is finished
   173         self.startsem.acquire()
   178         self.startsem.acquire()
   174         if self.timeout:
   179         if self.timeout:
   175             self.timeout.cancel()
   180             self.timeout.cancel()
   176         self.exitcode = ecode
   181         self.exitcode = ecode
   177         if self.exitcode != 0:
   182         if self.exitcode != 0:
   178             self.log_the_end(ecode,pid)
   183             self.log_the_end(ecode, pid)
   179         if self.finish_callback is not None:
   184         if self.finish_callback is not None:
   180             self.finish_callback(self,ecode,pid)
   185             self.finish_callback(self, ecode, pid)
   181         self.errt.join()
   186         self.errt.join()
   182         self.finishsem.release()
   187         self.finishsem.release()
   183 
   188 
   184     def kill(self,gently=True):
   189     def kill(self, gently=True):
   185         # avoid running kill before start is finished
   190         # avoid running kill before start is finished
   186         self.startsem.acquire()
   191         self.startsem.acquire()
   187         self.startsem.release()
   192         self.startsem.release()
   188         
   193 
   189         self.outt.killed = True
   194         self.outt.killed = True
   190         self.errt.killed = True
   195         self.errt.killed = True
   191         if wx.Platform == '__WXMSW__':
   196         if wx.Platform == '__WXMSW__':
   192             PROCESS_TERMINATE = 1
   197             PROCESS_TERMINATE = 1
   193             handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.Proc.pid)
   198             handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.Proc.pid)
   194             ctypes.windll.kernel32.TerminateProcess(handle, -1)
   199             ctypes.windll.kernel32.TerminateProcess(handle, -1)
   195             ctypes.windll.kernel32.CloseHandle(handle)
   200             ctypes.windll.kernel32.CloseHandle(handle)
   196         else:
   201         else:
   197             if gently:
   202             if gently:
   198                 sig=SIGTERM
   203                 sig = SIGTERM
   199             else:
   204             else:
   200                 sig=SIGKILL
   205                 sig = SIGKILL
   201             try:
   206             try:
   202                 os.kill(self.Proc.pid, sig)
   207                 os.kill(self.Proc.pid, sig)
   203             except:
   208             except Exception:
   204                 pass
   209                 pass
   205         self.outt.join()
   210         self.outt.join()
   206         self.errt.join()
   211         self.errt.join()
   207 
   212 
   208     def endlog(self):
   213     def endlog(self):
   209         if self.endlock.acquire(False):
   214         if self.endlock.acquire(False):
   210             if not self.outt.finished and self.kill_it:
   215             if not self.outt.finished and self.kill_it:
   211                self.kill()
   216                 self.kill()
   212             self.finishsem.release()
   217             self.finishsem.release()
   213 
       
   214 
   218 
   215     def spin(self):
   219     def spin(self):
   216         self.finishsem.acquire()
   220         self.finishsem.acquire()
   217         return [self.exitcode, "".join(self.outdata), "".join(self.errdata)]
   221         return [self.exitcode, "".join(self.outdata), "".join(self.errdata)]
   218