Changeup how the function decorator works.
Rename to "set_func_timeout" - Give it 2 possible arguments. First is either a floating point timeout, or a function which will be invoked each call of decorated function and passed same arguments, to calculate the timeout that should be used. Second is "allowOverride", default False. If True, adds a kwarg to the function being decorated, "forceTimeout", which if provided will override any default timeout and prevent a calculation attempt.
This commit is contained in:
parent
84a3abd11d
commit
32078a8065
@ -9,7 +9,7 @@
|
||||
__version__ = '4.0.0'
|
||||
__version_tuple__ = (4, 0, 0)
|
||||
|
||||
__all__ = ('func_timeout', 'set_timeout', 'set_modifiable_timeout', 'FunctionTimedOut')
|
||||
__all__ = ('func_timeout', 'func_set_timeout', 'FunctionTimedOut')
|
||||
|
||||
from .exceptions import FunctionTimedOut
|
||||
from .dafunc import func_timeout, set_timeout, set_modifiable_timeout
|
||||
from .dafunc import func_timeout, func_set_timeout
|
||||
|
||||
@ -1,59 +1,24 @@
|
||||
|
||||
# vim: set ts=4 sw=4 expandtab :
|
||||
|
||||
'''
|
||||
Copyright (c) 2016, 2017 Tim Savannah All Rights Reserved.
|
||||
|
||||
Licensed under the Lesser GNU Public License Version 3, LGPLv3. You should have recieved a copy of this with the source distribution as
|
||||
LICENSE, otherwise it is available at https://github.com/kata198/func_timeout/LICENSE
|
||||
'''
|
||||
|
||||
import inspect
|
||||
import threading
|
||||
import time
|
||||
import types
|
||||
|
||||
from .exceptions import FunctionTimedOut
|
||||
from .StoppableThread import StoppableThread
|
||||
|
||||
__all__ = ('set_timeout', 'func_timeout')
|
||||
__all__ = ('func_timeout', 'func_set_timeout')
|
||||
|
||||
|
||||
def set_timeout(timeout):
|
||||
'''
|
||||
set_timeout - Wrapper to run a function with a given timeout (max execution time)
|
||||
|
||||
@param timeout <float> - Number of seconds max to allow function to execute
|
||||
|
||||
@throws FunctionTimedOut If time alloted passes without function returning naturally
|
||||
|
||||
@see func_timeout
|
||||
@see set_modifiable_timeout
|
||||
'''
|
||||
def _function_decorator(func):
|
||||
def _function_wrapper(*args, **kwargs):
|
||||
return func_timeout(timeout, func, args=args, kwargs=kwargs)
|
||||
return _function_wrapper
|
||||
|
||||
return _function_decorator
|
||||
|
||||
def set_modifiable_timeout(timeout):
|
||||
'''
|
||||
set_modifiable_timeout - Wrapper to run a function with a given timeout (max execution time)
|
||||
which can be overriden by passing "forceTimeout" to the function being decorated
|
||||
|
||||
@param timeout <float> - Default Number of seconds max to allow function to execute
|
||||
|
||||
@throws FunctionTimedOut If time alloted passes without function returning naturally
|
||||
|
||||
@see func_timeout
|
||||
@see set_timeout
|
||||
'''
|
||||
def _function_decorator(func):
|
||||
def _function_wrapper(*args, **kwargs):
|
||||
if 'forceTimeout' in kwargs:
|
||||
useTimeout = kwargs.pop('forceTimeout')
|
||||
else:
|
||||
useTimeout = timeout
|
||||
|
||||
return func_timeout(useTimeout, func, args=args, kwargs=kwargs)
|
||||
return _function_wrapper
|
||||
return _function_decorator
|
||||
|
||||
def func_timeout(timeout, func, args=(), kwargs=None):
|
||||
'''
|
||||
func_timeout - Runs the given function for up to #timeout# seconds.
|
||||
@ -122,3 +87,126 @@ def func_timeout(timeout, func, args=(), kwargs=None):
|
||||
return ret[0]
|
||||
|
||||
|
||||
def func_set_timeout(timeout, allowOverride=False):
|
||||
'''
|
||||
set_timeout - Wrapper to run a function with a given/calculated timeout (max execution time).
|
||||
Optionally (if #allowOverride is True), adds a paramater, "forceTimeout", to the
|
||||
function which, if provided, will override the default timeout for that invocation.
|
||||
|
||||
If #timeout is provided as a lambda/function, it will be called
|
||||
prior to each invocation of the decorated function to calculate the timeout to be used
|
||||
for that call, based on the arguments passed to the decorated function.
|
||||
|
||||
For example, you may have a "processData" function whose execution time
|
||||
depends on the number of "data" elements, so you may want a million elements to have a
|
||||
much higher timeout than seven elements.)
|
||||
|
||||
If #allowOverride is True AND a kwarg of "forceTimeout" is passed to the wrapped function, that timeout
|
||||
will be used for that single call.
|
||||
|
||||
@param timeout <float OR lambda/function> -
|
||||
|
||||
**If float:**
|
||||
Default number of seconds max to allow function to execute
|
||||
before throwing FunctionTimedOut
|
||||
|
||||
**If lambda/function:
|
||||
|
||||
If a function/lambda is provided, it will be called for every
|
||||
invocation of the decorated function (unless #allowOverride=True and "forceTimeout" was passed)
|
||||
to determine the timeout to use based on the arguments to the decorated function.
|
||||
|
||||
The arguments as passed into the decorated function will be passed to this function.
|
||||
They either must match exactly to what the decorated function has, OR
|
||||
if you prefer to get the *args (list of ordered args) and **kwargs ( key : value keyword args form),
|
||||
define your calculate function like:
|
||||
|
||||
def calculateTimeout(*args, **kwargs):
|
||||
...
|
||||
|
||||
or lambda like:
|
||||
|
||||
calculateTimeout = lambda *args, **kwargs : ...
|
||||
|
||||
otherwise the args to your calculate function should match exactly the decorated function.
|
||||
|
||||
|
||||
@param allowOverride <bool> Default False, if True adds a keyword argument to the decorated function,
|
||||
"forceTimeout" which, if provided, will override the #timeout. If #timeout was provided as a lambda / function, it
|
||||
will not be called.
|
||||
|
||||
@throws FunctionTimedOut If time alloted passes without function returning naturally
|
||||
|
||||
@see func_timeout
|
||||
'''
|
||||
# Try to be as efficent as possible... don't compare the args more than once
|
||||
|
||||
# Helps closure issue on some versions of python
|
||||
defaultTimeout = copy.copy(timeout)
|
||||
|
||||
isTimeoutAFunction = bool( issubclass(timeout.__class__, (types.FunctionType, types.MethodType, types.LambdaType, types.BuiltinFunctionType, types.BuiltinMethodType) ) )
|
||||
|
||||
if not isTimeoutAFunction:
|
||||
if not issubclass(timeout.__class__, (float, int)):
|
||||
try:
|
||||
timeout = float(timeout)
|
||||
except:
|
||||
raise ValueError('timeout argument must be a float/int for number of seconds, or a function/lambda which gets passed the function arguments and returns a calculated timeout (as float or int). Passed type: < %s > is not of any of these, and cannot be converted to a float.' %( timeout.__class__.__name__, ))
|
||||
|
||||
|
||||
if not allowOverride and not isTimeoutAFunction:
|
||||
# Only defaultTimeout provided. Simple function wrapper
|
||||
def _function_decorator(func):
|
||||
|
||||
return lambda *args, **kwargs : func_timeout(defaultTimeout, func, args=args, kwargs=kwargs)
|
||||
|
||||
# def _function_wrapper(*args, **kwargs):
|
||||
# return func_timeout(defaultTimeout, func, args=args, kwargs=kwargs)
|
||||
# return _function_wrapper
|
||||
return _function_decorator
|
||||
|
||||
if not isTimeoutAFunction:
|
||||
# allowOverride is True and timeout is not a function. Simple conditional on every call
|
||||
def _function_decorator(func):
|
||||
def _function_wrapper(*args, **kwargs):
|
||||
if 'forceTimeout' in kwargs:
|
||||
useTimeout = kwargs.pop('forceTimeout')
|
||||
else:
|
||||
useTimeout = defaultTimeout
|
||||
|
||||
return func_timeout(useTimeout, func, args=args, kwargs=kwargs)
|
||||
|
||||
return _function_wrapper
|
||||
return _function_decorator
|
||||
|
||||
|
||||
# At this point, timeout IS known to be a function.
|
||||
timeoutFunction = timeout
|
||||
|
||||
if allowOverride:
|
||||
# Could use a lambda here... but want traceback to highlight the calculate function,
|
||||
# and not the invoked function
|
||||
def _function_decorator(func):
|
||||
def _function_wrapper(*args, **kwargs):
|
||||
if 'forceTimeout' in kwargs:
|
||||
useTimeout = kwargs.pop('forceTimeout')
|
||||
else:
|
||||
useTimeout = timeoutFunction(*args, **kwargs)
|
||||
|
||||
return func_timeout(useTimeout, func, args=args, kwargs=kwargs)
|
||||
|
||||
return _function_wrapper
|
||||
return _function_decorator
|
||||
|
||||
# Cannot override, and calculate timeout function
|
||||
def _function_decorator(func):
|
||||
def _function_wrapper(*args, **kwargs):
|
||||
useTimeout = timeoutFunction(*args, **kwargs)
|
||||
|
||||
return func_timeout(useTimeout, func, args=args, kwargs=kwargs)
|
||||
|
||||
return _function_wrapper
|
||||
return _function_decorator
|
||||
|
||||
|
||||
# vim: set ts=4 sw=4 expandtab :
|
||||
|
||||
Loading…
Reference in New Issue
Block a user