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__ = '4.0.0'
|
||||||
__version_tuple__ = (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 .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.
|
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
|
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
|
LICENSE, otherwise it is available at https://github.com/kata198/func_timeout/LICENSE
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import inspect
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import types
|
||||||
|
|
||||||
from .exceptions import FunctionTimedOut
|
from .exceptions import FunctionTimedOut
|
||||||
from .StoppableThread import StoppableThread
|
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):
|
def func_timeout(timeout, func, args=(), kwargs=None):
|
||||||
'''
|
'''
|
||||||
func_timeout - Runs the given function for up to #timeout# seconds.
|
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]
|
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