Skip to content

Commit

Permalink
0.9.60 新增 timeout_decorator 装饰器
Browse files Browse the repository at this point in the history
  • Loading branch information
zengbin93 committed Oct 15, 2024
1 parent 32e469e commit 06e93ce
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 16 deletions.
62 changes: 49 additions & 13 deletions czsc/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# coding: utf-8
import os
import functools
import threading
import pandas as pd
from typing import List, Union
from loguru import logger
Expand Down Expand Up @@ -230,28 +231,63 @@ def to_arrow(df: pd.DataFrame):
return sink.getvalue()


# def timeout_decorator(timeout):
# """超时装饰器
#
# :param timeout: int, 超时时间,单位秒
# """
#
# def decorator(func):
# @functools.wraps(func)
# def wrapper(*args, **kwargs):
# from concurrent.futures import ThreadPoolExecutor, TimeoutError
#
# with ThreadPoolExecutor() as executor:
# future = executor.submit(func, *args, **kwargs)
# try:
# result = future.result(timeout=timeout)
# return result
# except TimeoutError:
# logger.warning(
# f"{func.__name__} timed out after {timeout} seconds;" f"args: {args}; kwargs: {kwargs}"
# )
# return None
#
# return wrapper
#
# return decorator


def timeout_decorator(timeout):
"""超时装饰器
"""Timeout decorator using threading
:param timeout: int, 超时时间,单位秒
:param timeout: int, timeout duration in seconds
"""

def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
from concurrent.futures import ThreadPoolExecutor, TimeoutError
result = [None]
exception = [None]

with ThreadPoolExecutor() as executor:
future = executor.submit(func, *args, **kwargs)
def target():
try:
result = future.result(timeout=timeout)
return result
except TimeoutError:
# print(f"{func.__name__} timed out after {timeout} seconds")
logger.warning(
f"{func.__name__} timed out after {timeout} seconds;" f"args: {args}; kwargs: {kwargs}"
)
raise ValueError(f"{func.__name__} timed out after {timeout} seconds")
result[0] = func(*args, **kwargs)
except Exception as e:
exception[0] = e

thread = threading.Thread(target=target)
thread.start()
thread.join(timeout)

if thread.is_alive():
logger.warning(f"{func.__name__} timed out after {timeout} seconds; args: {args}; kwargs: {kwargs}")
return None

if exception[0]:
raise exception[0]

return result[0]

return wrapper

Expand Down
5 changes: 2 additions & 3 deletions test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,7 @@ def fast_function():
def test_timeout_decorator_timeout():
@timeout_decorator(1)
def slow_function():
time.sleep(2)
time.sleep(5)
return "Completed"

with pytest.raises(ValueError, match="timed out after 1 seconds"):
slow_function()
assert slow_function() is None

0 comments on commit 06e93ce

Please sign in to comment.