4

I'm creating a Python application that calls a few system commands. However, I want it to terminate those commands if they take too much time (say, 10s). I tried to do this on my own, using some subprocesses - without much success. After searching on stackoverflow, I found the following question: Using module 'subprocess' with timeout

It has an answer, that almost works for me - the problem is, that when the process is "terminated", it actually isn't - in fact, the process remains running in the background even after my script finishes. Of course this is not a desirable effect, but I can't find a workaround. Is there a preffered solution to this problem?

Code from mentioned answer (the bad one) for reference:

import subprocess, threading

class Command(object):
    def __init__(self, cmd):
        self.cmd = cmd
        self.process = None

    def run(self, timeout):
        def target():
            print 'Thread started'
            self.process = subprocess.Popen(self.cmd, shell=True)
            self.process.communicate()
            print 'Thread finished'

        thread = threading.Thread(target=target)
        thread.start()

        thread.join(timeout)
        if thread.is_alive():
            print 'Terminating process'
            self.process.terminate()
            thread.join()
        print self.process.returncode

command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
command.run(timeout=3)
command.run(timeout=1)
Community
  • 1
  • 1
akrasuski1
  • 820
  • 1
  • 8
  • 25
  • If you can use Python ≥3.3, that has a *timeout* argument for `Popen.wait()`. This will raise a *TimeoutExpired* exception if the child doesn't finish within the timeout period. You can then call `Popen.kill()` to forcibly terminate the child process. – Roland Smith Dec 21 '14 at 12:02
  • Thanks, but I use 2.7.6 – akrasuski1 Dec 21 '14 at 12:11

1 Answers1

1

You can find your answer here:

How to terminate a python subprocess launched with shell=True

just add preexec_fn=os.setsid to the Popen command

Terminate using:

os.killpg(self.process.pid, signal.SIGTERM)

Instead of:

self.process.terminate()

Don't forget to import os,signal

so you will get:

import subprocess, threading
import os,signal
class Command(object):
    def __init__(self, cmd):
        self.cmd = cmd
        self.process = None

    def run(self, timeout):
        def target():
            print 'Thread started'
            self.process = subprocess.Popen(self.cmd, shell=True, preexec_fn=os.setsid)
            self.process.communicate()
            print 'Thread finished'

        thread = threading.Thread(target=target)
        thread.start()

        thread.join(timeout)
        if thread.is_alive():
            print 'Terminating process'
            os.killpg(self.process.pid, signal.SIGTERM)
            thread.join()
        print self.process.returncode

command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
command.run(timeout=3)
command.run(timeout=1)
Community
  • 1
  • 1
Udy
  • 2,492
  • 4
  • 23
  • 33