Jonathan Komar Jonathan Komar - 2 months ago 36
Python Question

How can I detect whether the timeout in Python's subprocess has been reached?

I would like to differentiate between failed processes and timed-out processes. Python does catch the error and clearly identifies it. That is good, but no cigar, because I'd like to write my own log message that corresponds to the timeout error. See below for my current implementation and an explanation of what I want.

If program something like this:

#!/usr/bin/env python3

"""
My job is to demonstrate a problem detecting timeout failures.
"""

import os
import sys
import logging
import subprocess
import time

# Create main (root) logging object
logger = logging.getLogger('{}'.format(__file__))
logger.setLevel(logging.DEBUG)

# Formatter
consoleh = logging.StreamHandler(sys.stdout)
consoleh.setLevel(logging.INFO)
console_formatter = logging.Formatter('%(asctime)s %(name)s PID: %(process)d TID: %(thread)d %(levelname)s \n ==> %(message)s',datefmt='%Y-%m-%d at %H:%M:%S.%s')
consoleh.setFormatter(console_formatter)
logger.addHandler(consoleh)

def preHook(script):
logger.debug('preHook called.')
command = "{}".format(script)
logger.info('preHook Executing with 15 second timeout: \n /bin/sh -c {}'.format(command))
process = subprocess.Popen(command,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)# timeout in seconds
process.wait(timeout=15)
proc_stdout, proc_stderr = process.communicate()
if process.returncode == 0:
logger.info('preHook: Process complete: \n Command: /bin/sh -c {}\n STDOUT: "{}"\n STDERR: "{}"\n Exit Code: {}\n at: {}'.format(command,proc_stdout.decode('utf8').strip(),proc_stderr.decode('utf8').strip(),process.returncode,time.time()))
else:
exitcode = 1
logger.error('preHook: Process failed: \n Command: /bin/sh -c {}\n STDOUT: "{}"\n STDERR: "{}"\n Exit Code: {}\n at: {}'.format(command,proc_stdout.decode('utf8').strip(), proc_stderr.decode('utf8').strip(),process.returncode,time.time()))

def main():
preHook('find -type f')

if __name__ == "__main__":
main()


How can I catch the timeout error and write a related message in the standard error output?

Console Output



Python' subprocess package clearly catches the timeout error.

2017-08-28 at 09:44:57.1503906297 detecttimeout.py PID: 16915 TID: 140534594959104 INFO
==> preHook Executing with 15 second timeout:
/bin/sh -c find -type f
Traceback (most recent call last):
File "detecttimeout.py", line 40, in <module>
main()
File "detecttimeout.py", line 37, in main
preHook('find -type f')
File "detecttimeout.py", line 28, in preHook
process.wait(timeout=15)
File "/home/USER/devel/python/Python-3.4.5/Lib/subprocess.py", line 1561, in wait
raise TimeoutExpired(self.args, timeout)
subprocess.TimeoutExpired: Command 'find -type f' timed out after 15 seconds


I'd like to catch the timeout like I do the failed process. To implement catching failed processes, I use the return codes as shown in the logic. The message
preHook: Process failed...
occurs. I'd like another message:
preHook: Process timed out...
.

Answer Source

You can replace

process.wait(timeout=15)

by

try:
    process.wait(timeout=15)
except subprocess.TimeoutExpired:
    logger.error(<your error message>)
    return