1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
from __future__ import print_function, division, absolute_import #, unicode_literals
import time
import os
import errno
import signal
import subprocess
class TimeoutError(Exception):
"""Exceptions raised by SubProcessWithTimeout."""
class SubProcessWithTimeout(object):
"""
Based on from http://stackoverflow.com/questions/3876886/timeout-a-subprocess?rq=1
"""
Error = TimeoutError
def __init__(self, timeout, delay=.05):
self.timeout = float(timeout)
self.delay = float(delay)
if self.delay > self.timeout or self.delay <= 0 or self.timeout <= 0:
raise ValueError("delay and timeout must be positive with delay <= timeout")
def run(self, args,
bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None,
close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0):
"""Same interface as Popen"""
self.proc = subprocess.Popen(args,
bufsize, executable, stdin, stdout, stderr, preexec_fn,
close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags)
return_code = self._wait_testcomplete()
return self.proc, return_code
def _wait_testcomplete(self):
start = time.time()
while (time.time()-start) < self.timeout:
if self.proc.poll() is not None: # 0 just means successful exit
return self.proc.returncode
else:
time.sleep(self.delay)
# The process may exit between the time we check and the
# time we send the signal.
try:
os.kill(-self.proc.pid, signal.SIGTERM)
except OSError as e:
# If it's not because the process no longer exists, something weird is wrong.
if e.errno != errno.ESRCH:
raise e
time.sleep(1)
if self.proc.poll() is None: # Still hasn't exited.
try:
os.kill(-self.proc.pid, signal.SIGKILL)
except OSError as e:
if e.errno != errno.ESRCH:
raise e
return 137 # timeout return code for SIGKILL
else:
return 124 # timeout return code for SIGTERM
#############################################################################################################
# Unit tests
import unittest
class TestSubProcessWithTimeout(unittest.TestCase):
def test_with_sleep(self):
""""Testing if sleep 5 raises TimeoutError"""
proc, retcode = SubProcessWithTimeout(1).run(["sleep", "5"])
self.assertEqual(retcode, 124)
if __name__ == "__main__":
unittest.main()
|