3

I am trying to implement a timeout exception handler if a function call is taking too long.

EDIT: In fact, I am writing a Python script using subprocess, which calls an old C++ program with arguments. I know that the program hangs from time to time, not returning anything. That's why I am trying to put a time limit and to move on to next call with different argument and etc.

I've been searching and trying to implement it, but it doesn't quite work, so I wish to get some help. What I have so far is:

#! /usr/bin/env python

import signal

class TimeOutException(Exception):
    def __init__(self, message, errors):
        super(TimeOutException, self).__init__(message)
        self.errors = errors

def signal_handler(signum, frame):
    raise TimeOutException("Timeout!")

signal.signal(signal.SIGALRM, signal_handler)
signal.alarm(3)

try:
    while True:
        pass

except TimeOutException:
    print "Timed out!"

signal.alarm(0)

EDIT: The Error message I receive currently is "TypeError: init() takes exactly 3 arguments (2 given)

Also, I would like ask a basic question regarding the except block. what's the role difference between the code right below "except TimeOutException" and the code in the "Exception handler"? It seems both can do the same thing?

Any help would be appreciated.

Yui Park
  • 289
  • 2
  • 4
  • 9

1 Answers1

3

if a function call is taking too long

I realize that this might not be obvious for inexperienced developers, but the methods applicable for approaching this problem entirely depend on what you are doing in this "busy function", such as:

  • Is this a heavy computation? If yes, which Python interpreter are you using? CPython or PyPy? If CPython: does this computation only use Python bytecode or does it involve function calls outsourced to compiled machine code (which may hold Python's Global Interpreter Lock for quite an uncontrollable amount of time)?

  • Is this a lot of I/O work? If yes, can you abort this I/O work in an arbitrary state? Or do you need to properly clean up? Are you using a certain framework such as gevent or Twisted?

Edit: So, it looks you are just spawning a subprocess and wait for it to terminate. Great, that is actually one of the most simple problems to implement a timeout control for. Python (3) ships a corresponding feature! :-) Have a look at

https://docs.python.org/3/library/subprocess.html#subprocess.call

The timeout argument is passed to Popen.wait(). If the timeout expires, the child process will be killed and then waited for again. The TimeoutExpired exception will be re-raised after the child process has terminated.

Edit2:

Example code for you, save this to a file and execute it with Python 3.3, at least:

import subprocess


try:
    subprocess.call(['python', '-c', 'print("hello")'], timeout=2)
except subprocess.TimeoutExpired as e:
    print("%s was terminated as of timeout. Its output was:\n%s" % (e.cmd, e.output))


try:
    subprocess.call(['python'], timeout=2)
except subprocess.TimeoutExpired as e:
    print("%s was terminated as of timeout. Its output was:\n%s" % (e.cmd, e.output))

In the first case, the subprocess immediately returns. No timeout exception will be raised. In the second case, the timeout expires, and your controlling process (the process running above's script) will attempt to terminate the subprocess. This succeeds. After that, the subprocess.TimeoutExpired is raised and the exception handler deals with it. For me the output of the script above is ['python'] was terminated as of timeout. Its output was: None:

Dr. Jan-Philip Gehrcke
  • 33,287
  • 14
  • 85
  • 130
  • Actually, what I am doing is calling another C++ program, which is old and buggy and I am using Python as a scripting language. I'll edit the question. – Yui Park Feb 06 '15 at 20:44
  • So, you are using subprocess? `subprocess` provides a Timeout control. – Dr. Jan-Philip Gehrcke Feb 06 '15 at 20:48
  • Oh, so it might be as simple as replacing my `subprocess.Popen` command with `subprocess.call` with timer argument added to it? – Yui Park Feb 06 '15 at 21:09
  • Right, and you need to wrap your `call()` with a `subprocess.TimeoutExpired` catcher. – Dr. Jan-Philip Gehrcke Feb 06 '15 at 21:16
  • Sadly, I don't think I can use this ... I am using Python 2.6 on an old machine and the subprocess.TimeoutExpired is new on Python 3.3 I think :s ... or maybe I am doing something wrong... sad face* – Yui Park Feb 06 '15 at 21:44
  • This is only available in Python 3.3, as stated in my answer. Build your own Python, you can surely use that on that old machine. – Dr. Jan-Philip Gehrcke Feb 06 '15 at 21:46
  • @YuiPark: You can of course obtain timeout control for subprocesses in Python 2.X, too. Have a look into this thread, for some nice solutions: http://stackoverflow.com/questions/1191374/subprocess-with-timeout -- it will for sure work! Just please also consider using a modern Python version. – Dr. Jan-Philip Gehrcke Feb 06 '15 at 22:33
  • I was able to install the newest Python on my machine. And the code/method you presented works great. Thank you very much! – Yui Park Feb 07 '15 at 00:12
  • Glad to hear that. Don't forget to upvote/check the answer then :-). – Dr. Jan-Philip Gehrcke Feb 07 '15 at 00:53
  • Um. As a sample code, it works great. But I realized that there are differences between Popen and call. Apparently, call doesn't allow me to use the output value that I was reading in. I did upvote, though :) – Yui Park Feb 07 '15 at 01:03
  • There are differences, but should be able to work around. Clearly, you can just as well retrieve `stdout` via `call()`. – Dr. Jan-Philip Gehrcke Feb 07 '15 at 01:25