From af7d6e8a397855825bb3291dacc3d95ff8f2b34c Mon Sep 17 00:00:00 2001 From: Tim Savannah Date: Mon, 19 Aug 2019 17:28:33 -0400 Subject: [PATCH] Allow instantiation of FunctionTimedOut with empty arguments (to support some patterns in unit testing / mocking). Will replace function name with "Unknown Function" if not provided, and timedOutAfter will be "Unknown" or a float of how long until timeout occurred. --- func_timeout/exceptions.py | 29 +++++++++++++++++++++- tests/FuncTimeoutTests/test_Basic.py | 37 +++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/func_timeout/exceptions.py b/func_timeout/exceptions.py index ce7688a..e67c06d 100644 --- a/func_timeout/exceptions.py +++ b/func_timeout/exceptions.py @@ -25,6 +25,22 @@ class FunctionTimedOut(BaseException): def __init__(self, msg='', timedOutAfter=None, timedOutFunction=None, timedOutArgs=None, timedOutKwargs=None): + ''' + __init__ - Create this exception type. + + You should not need to do this outside of testing, it will be created by the func_timeout API + + @param msg - A predefined message, otherwise we will attempt to generate one from the other arguments. + + @param timedOutAfter - Number of seconds before timing-out. Filled-in by API, None will produce "Unknown" + + @param timedOutFunction - Reference to the function that timed-out. Filled-in by API." None will produce "Unknown Function" + + @param timedOutArgs - List of fixed-order arguments ( *args ), or None for no args. + + @param timedOutKwargs - Dict of keyword arg ( **kwargs ) names to values, or None for no kwargs. + + ''' self.timedOutAfter = timedOutAfter @@ -46,7 +62,18 @@ class FunctionTimedOut(BaseException): @return - Message ''' - return 'Function %s (args=%s) (kwargs=%s) timed out after %f seconds.\n' %(self.timedOutFunction.__name__, repr(self.timedOutArgs), repr(self.timedOutKwargs), self.timedOutAfter) + # Try to gather the function name, if available. + # If it is not, default to an "unknown" string to allow default instantiation + if self.timedOutFunction is not None: + timedOutFuncName = self.timedOutFunction.__name__ + else: + timedOutFuncName = 'Unknown Function' + if self.timedOutAfter is not None: + timedOutAfterStr = "%f" %(self.timedOutAfter, ) + else: + timedOutAfterStr = "Unknown" + + return 'Function %s (args=%s) (kwargs=%s) timed out after %s seconds.\n' %(timedOutFuncName, repr(self.timedOutArgs), repr(self.timedOutKwargs), timedOutAfterStr) def retry(self, timeout=RETRY_SAME_TIMEOUT): ''' diff --git a/tests/FuncTimeoutTests/test_Basic.py b/tests/FuncTimeoutTests/test_Basic.py index cb4c351..2ed1e5c 100755 --- a/tests/FuncTimeoutTests/test_Basic.py +++ b/tests/FuncTimeoutTests/test_Basic.py @@ -122,7 +122,7 @@ class TestBasic(object): threadsCleanedUp = True break - + assert threadsCleanedUp , 'Expected other threads to get cleaned up after gc collection' def test_exception(self): @@ -148,10 +148,39 @@ class TestBasic(object): assert functionTimedOut.timedOutFunction == sleepFunction , 'Expected timedOutFunction to equal sleepFunction' assert functionTimedOut.timedOutArgs == (5, 19) , 'Expected args to equal (5, 19)' assert functionTimedOut.timedOutKwargs == {} , 'Expected timedOutKwargs to equal {}' - - - + + def test_instantiateExceptionNoArgs(self): + + gotException = False + + try: + exc = FunctionTimedOut() + msg = str(exc) + msg2 = exc.getMsg() + + except Exception as _e: + sys.stderr.write('Got unexpected exception in test_instantiateExceptionNoArgs with no arguments. %s %s\n\n' %(str(type(_e)), str(_e))) + gotException = True + + assert gotException is False, 'Expected to be able to create FunctionTimedOut exception without arguments.' + + gotException = False + + try: + exc = FunctionTimedOut('test') + msg = str(exc) + msg2 = str(exc.getMsg()) + + except Exception as _e: + sys.stderr.write('Got unexpected exception in test_instantiateExceptionNoArgs with fixed message string. %s %s\n\n' %(str(type(_e)), str(_e))) + gotException = True + + assert gotException is False , 'Expected to be able to create a FunctionTimedOut exception with a fixed message.' + + # Other forms (providing the function name) are tested elsewhere. + + if __name__ == '__main__': sys.exit(subprocess.Popen('GoodTests.py -n1 "%s" %s' %(sys.argv[0], ' '.join(['"%s"' %(arg.replace('"', '\\"'), ) for arg in sys.argv[1:]]) ), shell=True).wait())