I am writing a watchdog, of sorts, for processes in a test suite. I need to determine if a test hangs.
I could simply start the process with subprocess.Popen(...)
, and use Popen.wait(timeout=to)
or Popen.poll()
and keep my own timer. However, the tests differ greatly in execution time, which makes it impossible to have a good 'timeout' value that is sensible for all tests.
I have found that a good way to determine if a test has hung is to have a 'timeout' for the last time the process output anything. To that end, I considered using
process = subprocess.Popen(args='<program>', stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ...)
and Popen.communicate()
, to determine when stdout
and/or stderr
are not None
. The problem is that Popen.communicate()
, without a 'timeout' will just wait until the process terminates, and with a 'timeout' will raise a TimeoutExpired
exception, from which I can't determine if anything was read. TimeoutExpired.output
is empty, BTW.
I could not find anything in the documentation that allows one to perform the 'reads' manually. Also, there is usually a lot of output from the process, so starting it with stdout=<open_file_descriptor>
would be beneficial, as I would have no concern for overflowing pipe buffers.
Update/Solution:
Popen.stdout
and Popen.stderr
return a "readable stream object", which one can use to manually poll/select and read. I ended up using select 'Polling Objects', which use the poll()
system call, as bellow:
import os
import select
import subprocess
p = subprocess.Popen(args="<program>", shell=True, universal_newlines=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
poll_obj = select.poll()
poll_obj.register(p.stdout, select.POLLIN)
poll_obj.register(p.stderr, select.POLLIN)
while p.poll() is None:
events = True
while events:
events = poll_obj.poll(10)
for fd, event in events:
if event & select.POLLIN:
print("STDOUT: " if fd == p.stdout.fileno() else "STDERR: ")
print(os.read(fd, 1024).decode())
# else some other error (see 'Polling Objects')