File: subprocesswithtimeout.py

package info (click to toggle)
abinit 9.10.4-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 518,712 kB
  • sloc: xml: 877,568; f90: 577,240; python: 80,760; perl: 7,019; ansic: 4,585; sh: 1,925; javascript: 601; fortran: 557; cpp: 454; objc: 323; makefile: 77; csh: 42; pascal: 31
file content (80 lines) | stat: -rw-r--r-- 2,777 bytes parent folder | download | duplicates (3)
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()