views:

113

answers:

4

Hello is there any argument or options to setup kinda subprocess.Popen(['..'], ..., timeout=20)?

Sultan

A: 

subprocess.Popen doesn't block so you can do something like this:

p = subprocess.Popen(['...'])
time.sleep(20)
if p.poll() is None:
  p.kill()
  print 'timed out'
else:
  print p.communicate()
Abhishek Amit
This would freeze this process for 20 seconds. Is that acceptable?
Noufal Ibrahim
doesn't this kinda defeat the point of using a subprocess?
aaronasterling
seem it is! I think this is a little inconvenience of using subprocess.
sultan
This code has a race condition.
Mike Graham
+1  A: 

Unfortunately, there isn't such a solution. I managed to do this using a threaded timer that would launch along with the process that would kill it after the timeout but I did run into some stale file descriptor issues because of zombie processes or some such.

Noufal Ibrahim
+1 This would be my solution.
aaronasterling
I experienced such a problem too with file descriptors on zombie processes.
sultan
Sultan. It should be possible to correct those. I did manage to finally polish my application into something workable but it was not generic enough to publish.
Noufal Ibrahim
+1  A: 

No there is no time out. I guess, what you are looking for is to kill the sub process after some time. Since you are able to signal the subprocess, you should be able to kill it too.

generic approach to sending a signal to subprocess:

proc = subprocess.Popen([command])
time.sleep(1)
print 'signaling child'
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)

You could use this mechanism to terminate after a time out period.

pyfunc
Is it universal sys.stdout.flush() for all apps using subprocess?
sultan
This is pretty much like Abhishek's answer. He's using SIGKILL and you're using SIGUSR1. It has the same issues.
Noufal Ibrahim
Ok thanks guys!
sultan
+2  A: 

You could do

from twisted.internet import reactor, protocol, error, defer

class DyingProcessProtocol(protocol.ProcessProtocol):
    def __init__(self, timeout):
        self.timeout = timeout

    def connectionMade(self):
        @defer.inlineCallbacks
        def killIfAlive():
            try:
                yield self.transport.signalProcess('KILL')
            except error.ProcessExitedAlready:
                pass

        d = reactor.callLater(self.timeout, killIfAlive)

reactor.spawnProcess(DyingProcessProtocol(20), ...)

using Twisted's asynchronous process API.

Mike Graham
+1 for providing an alternative rather than "It can't be done".
Noufal Ibrahim