Answer a question

I am trying to implement timeout for a particular function. I have checked many of the questions in SE and couldn't find any solution which fits my problem, because:

  1. I am running python in Windows
  2. Timeout is applied on a python function which I don't have control on, i.e. it is defined in an already designed module.
  3. The python function is not a subprocess

I am having a already designed custom module (say MyModule) developed for particular tasks, and there are functions defined in it. One of the function (say MyFunc) has a tendency to run forever because of external factors, and I just don't want the python script to hang.

I am planning to add a timeout feature, as said below pseudocode:

import MyModule

set_timeout(T)
MyResult=MyModule.MyFunc()

#Come to this part of script after execution of MyFunc() or after T seconds (the latter on priority)
if Timeout occurred:
    print 'MyFunc did not execute completely'
else:
    print 'MyFunc completed'

But I am not sure which module can be used to achieve this on python. Note that I am a newbie, and all the scripts I have written are directly based on SE Answers or Python Documentation.

Answers

I think a good way to approach this is to create a decorator and use the Thread.join(timeout=seconds) method. Bear in mind that there's no good way to kill the thread, so it will continue to run in the background, more or less, as long as your program is running.

First, create a decorator like this:

from threading import Thread
import functools

def timeout(timeout):
    def deco(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            res = [Exception('function [%s] timeout [%s seconds] exceeded!' % (func.__name__, timeout))]
            def newFunc():
                try:
                    res[0] = func(*args, **kwargs)
                except Exception as e:
                    res[0] = e
            t = Thread(target=newFunc)
            t.daemon = True
            try:
                t.start()
                t.join(timeout)
            except Exception as je:
                print ('error starting thread')
                raise je
            ret = res[0]
            if isinstance(ret, BaseException):
                raise ret
            return ret
        return wrapper
    return deco

Then, do something like this:

func = timeout(timeout=16)(MyModule.MyFunc)
try:
    func()
except:
    pass #handle errors here

You can use this decorator anywhere you need with something like:

@timeout(60)
def f():
    ...
Logo

Python社区为您提供最前沿的新闻资讯和知识内容

更多推荐