Python start process with timeout
The typical scenario to start process and wait for the result takes only a few seconds to write in other object oriented languages (other than Python) It looks like:
Process p = new Process(command = "my command", redirect_output = True) p.Wait(timeout = 10) return p.stdout.result
In Python there is no such functionality provided. You have to check the process using process.poll() method. If there is too much of text in buffer you might achieve situation where your application is deadlocked.
To avoid the deadlock you have to call process.communicate(). However, if there is nothing in stdout the (main) thread waits until the process is finished or the process prints something to stdout. There are a few solutions available that does not work for all scenarios or require unix only modules.
This solution should be platform independent, should work in every situation and should give the process one second to finish gracefully by sending ctrl+c and ctrl+break signals:
def get_cmd_output(self, cmd, timeout = -1, shell = False, cwd = None): class ProcessHandler: # there is no subprocess.CREATE_NEW_PROCESS_GROUP available so let's declare it with all necessary constants.. CREATE_NEW_PROCESS_GROUP = 512 CTRL_C_EVENT = 0 CTRL_BREAK_EVENT = 1 def __init__ (self, process, timeout) #, logger): self.process = process self.timer = threading.Timer(timeout, self) self.cancelled = False self.logger = logger self.__dict__["result"] = "" if self.timer.interval > 0: self.timer.start() def __setattr__(self, name, val): # if we set result value then we have to cancel timer if name == 'result': self.timer.cancel() self.__dict__[name] = val def __getattr__(self, name): if name == 'result': if self.cancelled: return None return self.__dict__[name] def __call__ (self): #self.logger.log("Command timeout (%s seconds) for `%s`" % (timeout, cmd)) self.cancelled = True # send keyboard interrupt if platform.system() == 'Windows': self.logger.log("Sending break signal to windows process") GenerateConsoleCtrlEvent = ctypes.windll.kernel32.GenerateConsoleCtrlEvent GenerateConsoleCtrlEvent(ProcessHandler.CTRL_BREAK_EVENT, p.pid) GenerateConsoleCtrlEvent(ProcessHandler.CTRL_C_EVENT, p.pid) else: self.logger.log("Sending break signal to non-windows process") p.send_signal(ProcessHandler.CTRL_BREAK_EVENT) p.send_signal(ProcessHandler.CTRL_C_EVENT) # let's wait 1 second before finally killing the process time.sleep(1) # killing brutally if p.poll() is None: #self.logger.log("Killing process brutally...") p.kill() cmd_split = cmd.split(" ") p = subprocess.Popen(cmd_split, shell = shell, cwd = cwd, stdout = subprocess.PIPE, stderr = subprocess.PIPE, creationflags = ProcessHandler.CREATE_NEW_PROCESS_GROUP) h = ProcessHandler(p, timeout) #, logger) h.result = p.stdout.read() return h.result