不稳定的超时装饰? (即超时装饰func调用超时装饰func)

时间:2016-03-12 04:40:09

标签: python timeout decorator

我需要一个装饰器(或功能相同的东西),它允许下面的代码按预期工作:

@timeout(1)
def outer():
    inner()

@timeout(5)
def inner():
    time.sleep(3)
    print("Should never be printed if you call outer()")

outer()
# The outer timeout is ignored and "property" finishes

代码似乎毫无意义,但实际上,outer调用多个函数,这些函数占用了不确定的时间,其中一些函数有自己的超时。

我尝试了timeout-decoratortwo SO answers here,但都没有效果。

2 个答案:

答案 0 :(得分:4)

这样的事情:

def timeout(timeout, raise_exc=True):
    """
    raise_exc - if exception should be raised on timeout 
                or exception inside decorated func.
                Otherwise None will be returned.
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            res = None
            exc = None
            def _run():
                nonlocal res
                nonlocal exc
                try:
                    res = func(*args, **kwargs)
                except Exception as e:
                    exc = e
            t = threading.Thread(target=_run)
            t.daemon = True
            t.start()
            t.join(timeout=timeout)
            if raise_exc and t.is_alive():
                raise TimeoutError()
            elif raise_exc and (exc is not None):
                raise exc
            else:
                return res
        return wrapper
    return decorator

示例:

@timeout(0.5, raise_exc=False)
def outer():
    return inner()

@timeout(2)
def inner():
    time.sleep(1)
    return "Shouldn't be printed"

print(outer())  # None

@timeout(2, raise_exc=False)
def outer():
    return inner()

@timeout(2)
def inner():
    time.sleep(1)
    return "Should be printed"

print(outer())  # Should be printed

请注意,您的任务只能通过线程或进程来解决,但这可能会导致一些非显而易见的problems。我建议您考虑一下如果没有它可以解决您的任务。在大多数情况下,您可以将代码拆分为多个部分并在每个部分之后检查超时。像这样:

def outer(arg, timeout=None):
    t = Timeout(timeout)
    # some operation:
    time.sleep(1)
    if t.is_timeout: return None
    # use time left as subfunction's timeout:
    return inner(arg, timeout=t.time_left)

答案 1 :(得分:2)

timeout函数使用threading.Timer设置计时器,thread.interrupt_main使用中断主线程。

from thread import interrupt_main
from threading import Timer
from time import time, sleep

def timeout(secs):
    def wrapper(func):
        timer = Timer(secs, interrupt_main)
        def decorated(*args, **kwargs):
            timer.start()
            return func(*args, **kwargs)
        return decorated
    return wrapper            

@timeout(1)
def outer():
    inner()

@timeout(5)
def inner():
    sleep(3)
    print("Should never be printed if you call outer()")

try:
    outer()
except:
    print('timed out')