Cmd.py 3.8 KB
import sys
mswindows = (sys.platform == "win32")

import os
import process
import string
import threading
import types

if mswindows:
    from win32process import CREATE_NO_WINDOW, TerminateProcess


class Cmd(threading.Thread):
    """Base class for all scripted commands.

       This allows to run external command and to pipe their output to a file object."""

    def __init__(self, callback_fn = None):
        """Command constructor. You need to specify a function which will be called
           for each output line. The callback_obj parameter is the object given in first
           parameter of the callback function."""
        threading.Thread.__init__(self)
        self.set_output_callback(callback_fn)
        self.__stop_command = False
        self.__return_value = None
##        self.__inFile = None
##        self.__outFile = None
        self.__path = os.getcwd()
        self.__process = None

    def __del__( self):
        self.stop()
        self.wait_for_cmd()
##        if self.__inFile != None:
##            self.__inFile.close()
##        if self.__outFile != None:
##            self.__outFile.close()

    def __call__(self, cmd, sync = True):
        """Run the specified command."""
        return self.run_cmd(cmd, sync)

    def set_path(self, path):
        """Defines the command path execution."""
        self.__path = path

    def set_output_callback(self, callback_fn):
        """Define the output callback function."""
        if callback_fn == None:
            self.__callback = sys.stdout.write
        else:
            self.__callback = callback_fn

    def run_cmd(self, cmd, sync = True):
        """Run the specified command, redirecting its output with the callback."""
        if sync:
            return self.__run_cmd(cmd)
        else:
            self.__cmd = cmd
            threading.Thread.start(self)

    def wait_for_cmd(self):
        """Waits for the command thread to be terminated. Returns the command returned value."""
        if threading.Thread.isAlive(self):
            threading.Thread.join(self)
        return self.__return_value

    def stop(self):
        """Stop the currently running command."""
        self.__stop_command = True
        if self.__process != None:
            self.__callback('\nTrying to close command...\n')
            if mswindows:
                #self.__process._handle.Close()
                TerminateProcess(self.__process._handle, -1)
            else:
                os.kill(self.pid, signal.SIGKILL)
            

    def run(self):
        """Private, should never be called directly."""
        self.__return_value = __run_cmd(self.__cmd)
        return self.__return_value

    def __run_cmd(self, cmd):
        """Private, should never be called directly."""
        oldCwd = os.getcwd()
        os.chdir(self.__path)
        if not isinstance(cmd, types.StringTypes):
            cmd = string.join(cmd)
        print '*** Calling ', cmd
        self.__process = process.Popen( cmd, stdin = process.PIPE, \
                                        stdout = process.PIPE, \
                                        stderr = process.STDOUT, \
                                        creationflags = CREATE_NO_WINDOW)
##        self.__process = process.Popen( cmd, 0, None, None, process.PIPE, process.STDOUT, creationflags = CREATE_NO_WINDOW)
        line = self.__process.stdout.readline()
        while len(line) > 0:
            self.__callback(line)
            #print line,
            #sys.stdout.write(line)
            if self.__stop_command:
                self.__callback('\n*** Command aborted! ***')
                break
            line = self.__process.stdout.readline()
        self.__process.wait()
        ret = self.__process.returncode
        print '*** Command return code ', ret
        os.chdir(oldCwd)
        self.__process = None
        return ret