func_timeout
func_timeout copied to clipboard
FunctionTimedOut can't be catch by expcet Exception as e: .
I find that FunctionTimedOut is the same level exceptions as Exception, and both create from BaseException. this thing makes me a little bit confused at the first time.
Agree. I have to use except BaseException as e to catch the error. Really confused. What's the design purpose?
Raising an exception from BaseException rather than Exception makes multiprocessing.map failed to handle the exceptions in subprocess, causing the process pool failed to join.
import os
import multiprocessing
from multiprocessing import Pool
import func_timeout
from func_timeout import func_set_timeout
@func_set_timeout(1)
def dummy(x):
os.system("sleep 1")
return x
def wrapper(x):
try:
return dummy(x)
except Exception as e:
# func_timeout.exceptions.FunctionTimedOut
# raise
print(e)
return None
logger = multiprocessing.log_to_stderr()
logger.setLevel(multiprocessing.SUBDEBUG)
with Pool(8) as pool:
r = pool.map(wrapper, range(100), chunksize=8)
print(r)
I think it should definitely inherits from Exception rather than BaseException. It causes issues on our side because we want to just catch Exception error and not catch BaseException errors like a systemexit or keyboardinterrupt. So we need to wrap calls to this libs just to rethrow a normal error that inherits from Exception instead of BaseException. A bit convoluted imo.
same issue today, an exception handler i configured in FastApi wouldn't handle such timeout errors
# Exception handler for general exceptions
@app.exception_handler(Exception)
async def generic_exception_handler(request, exc: Exception):
return await _error_formatter(error_message=str(exc),
error_type="server_error",
status_code=500,
)
@app.get("/some_long_job")
async def some_long_job(arg1, arg2):
result = func_timeout.func_timeout(timeout=0.02, func=some_specific_long_job, args=(arg1, arg2))
return result
went to source code of "FunctionTimedOut" and changed baseException to Exception, obviously a bad idea.
this is my current workaroud:
class TimeoutException(Exception):
pass
def func_timeout_handleable(timeout: float,
func: any,
args: tuple = (),
kwargs: dict = None,
specific_error_message: str = None
):
try:
result = func_timeout.func_timeout(timeout=timeout, func=func, args=args, kwargs=kwargs)
return result
except func_timeout.exceptions.FunctionTimedOut:
if specific_error_message is None:
raise TimeoutException(f"timed out after {timeout} seconds")
else:
raise TimeoutException(specific_error_message)