Commit c010b729 authored by Jonathan Michalon's avatar Jonathan Michalon

Merge branch 'subprocess-timeout' into 'master'

Subprocess Timeout

Adds support for timeout in subprocess communication for Python < 3.3, and use it in `Check.exec_with_timeout()`.
parents 75eba0ac e3fe6f81
from subprocess import Popen, PIPE
import concurrent.futures
from time import sleep
from IPy import IP
from sys import hexversion as sys_hexversion
# Import or implement Popen() with timeout support
if sys_hexversion >= 0x03030000:
# on Python 3.3 Popen() supports timeout, we have nothing to do
from subprocess import TimeoutExpired, Popen, PIPE
# on Python < 3.3, implement timeout with a thread
from threading import Thread
import subprocess
from subprocess import PIPE
class TimeoutExpired(subprocess.SubprocessError):
def __init__(self, args, timeout=None, output=None):
self.cmd, self.timeout, self.output = args, timeout, output
def __str__(self):
return 'Command %s timed out after %g seconds' % (self.args,
class Popen(subprocess.Popen):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._out = None
self._err = None
self._exc = None
self._thread = None
def _communicate_thread(self, input=None):
self._out, self._err = super().communicate(input=input)
except Exception as ex:
self._exc = ex
def communicate(self, input=None, timeout=None):
if timeout is None and not self._thread:
# without a timeout, default implementation is fine
return super().communicate(input=input)
elif self._thread and not self._thread.is_alive():
# if this is a second call and the thread finished, return
# the existing result
if self._exc:
raise self._exc
return self._out, self._err
# otherwise, implement the timeout
if not self._thread:
self._thread = Thread(target=self._communicate_thread,
if self._thread.is_alive():
raise TimeoutExpired(self.args, timeout)
if self._exc:
raise self._exc
return self._out, self._err
class Check(object):
......@@ -37,19 +94,18 @@ class Check(object):
return self.ok
def exec_with_timeout(self, command, timeout=2):
self.errmsg = ''
p = Popen(command, stdout=PIPE, stderr=PIPE)
out, err = p.communicate(timeout=timeout)
except TimeoutExpired:
out, err = p.communicate()
# with 3.3 we got timeout…
# try:
# out, err = p.communicate(timeout=timeout)
# except TimeoutExpired:
# p.kill()
# out, err = p.communicate()
ret = p.poll()
if ret != 0:
self.errmsg = "stdout: " + str(out) + '\n' + \
self.errmsg += "Operation timed out\n"
if p.returncode != 0:
self.errmsg += "stdout: " + str(out) + '\n' + \
"stderr: " + str(err) + '\n'
return ret == 0
return p.returncode == 0
def exec_by_ip_family(self, addr, v4command, v6command):
ipv = IP(addr).version()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment