https://github.com/kata198/func_timeout/pull/18 diff --git a/func_timeout/dafunc.py b/func_timeout/dafunc.py index 681a01c..afa193f 100644 --- a/func_timeout/dafunc.py +++ b/func_timeout/dafunc.py @@ -1,4 +1,3 @@ - # vim: set ts=4 sw=4 expandtab : ''' @@ -9,27 +8,25 @@ ''' import copy -import inspect -import threading -import time import types import sys from .exceptions import FunctionTimedOut from .StoppableThread import StoppableThread -try: - from .py3_raise import raise_exception -except SyntaxError: - from .py2_raise import raise_exception -except ImportError: - from .py2_raise import raise_exception - from functools import wraps __all__ = ('func_timeout', 'func_set_timeout') +# PEP 409 - Raise with the chained exception context disabled +# This, in effect, prevents the "funcwrap" wrapper ( chained +# in context to an exception raised here, due to scope ) +# Only available in python3.3+ +def raise_exception(exception): + raise exception[0] from None + + def func_timeout(timeout, func, args=(), kwargs=None): ''' func_timeout - Runs the given function for up to #timeout# seconds. @@ -175,7 +172,7 @@ def calculateTimeout(*args, **kwargs): 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__, )) + raise ValueError(f'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: < {timeout.__class__.__name__} > is not of any of these, and cannot be converted to a float.') if not allowOverride and not isTimeoutAFunction: diff --git a/func_timeout/exceptions.py b/func_timeout/exceptions.py index cd0d130..016320f 100644 --- a/func_timeout/exceptions.py +++ b/func_timeout/exceptions.py @@ -69,11 +69,11 @@ def getMsg(self): else: timedOutFuncName = 'Unknown Function' if self.timedOutAfter is not None: - timedOutAfterStr = "%f" %(self.timedOutAfter, ) + timedOutAfterStr = f"{self.timedOutAfter:f}" else: timedOutAfterStr = "Unknown" - return 'Function %s (args=%s) (kwargs=%s) timed out after %s seconds.\n' %(timedOutFuncName, repr(self.timedOutArgs), repr(self.timedOutKwargs), timedOutAfterStr) + return f'Function {timedOutFuncName} (args={repr(self.timedOutArgs)}) (kwargs={repr(self.timedOutKwargs)}) timed out after {timedOutAfterStr} seconds.\n' def retry(self, timeout=RETRY_SAME_TIMEOUT): ''' diff --git a/func_timeout/py2_raise.py b/func_timeout/py2_raise.py deleted file mode 100644 index 7bace72..0000000 --- a/func_timeout/py2_raise.py +++ /dev/null @@ -1,5 +0,0 @@ - - -# Python2 allows specifying an alternate traceback. -def raise_exception(exception): - raise exception[0] , None , exception[0].__traceback__ diff --git a/func_timeout/py3_raise.py b/func_timeout/py3_raise.py deleted file mode 100644 index dde334b..0000000 --- a/func_timeout/py3_raise.py +++ /dev/null @@ -1,7 +0,0 @@ - -# PEP 409 - Raise with the chained exception context disabled -# This, in effect, prevents the "funcwrap" wrapper ( chained -# in context to an exception raised here, due to scope ) -# Only available in python3.3+ -def raise_exception(exception): - raise exception[0] from None diff --git a/setup.py b/setup.py index f5767b8..62031f3 100755 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ with open('README.rst', 'rt') as f: long_description = f.read() except Exception as e: - sys.stderr.write('Error reading from README.rst: %s\n' %(str(e),)) + sys.stderr.write(f'Error reading from README.rst: {str(e)}\n') log_description = summary setup(name='func_timeout', @@ -44,12 +44,10 @@ classifiers=['Development Status :: 5 - Production/Stable', 'Programming Language :: Python', 'License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Topic :: Software Development :: Libraries :: Python Modules' ] ) diff --git a/testit.py b/testit.py index 0ff5ea2..709d622 100755 --- a/testit.py +++ b/testit.py @@ -17,19 +17,19 @@ def doit(howmany): if __name__ == '__main__': print ( "Should get return value of 23:" ) - print ( "\tGot Return: %s\n" %(str(func_timeout(4, doit, args=(6,))),) ) + print ( f"\tGot Return: {str(func_timeout(4, doit, args=(6,)))}\n" ) print ( "\nShould time out (exception):" ) myException = None try: - print ("\tGot Return: %s\n" %(str(func_timeout(1, doit, kwargs={'howmany' : 16})),)) + print (f"\tGot Return: {str(func_timeout(1, doit, kwargs={'howmany': 16}))}\n") except FunctionTimedOut as e: - sys.stderr.write('\tGot Exception: %s\n' %(str(e),)) + sys.stderr.write(f'\tGot Exception: {str(e)}\n') myException = e pass print ( "\nRetrying with longer timeout, should get 16+17=33:" ) if myException is not None: - print ( "\nGot: %s\n" %( str(myException.retry(2.5)), ) ) + print ( f"\nGot: {str(myException.retry(2.5))}\n" ) else: sys.stderr.write('Did not get exception before?\n') diff --git a/tests/FuncTimeoutTests/TestUtils.py b/tests/FuncTimeoutTests/TestUtils.py index fdb9181..30f6b51 100644 --- a/tests/FuncTimeoutTests/TestUtils.py +++ b/tests/FuncTimeoutTests/TestUtils.py @@ -1,4 +1,3 @@ - # vim: set ts=4 sw=4 expandtab : ''' @@ -93,7 +92,7 @@ def getSleepLambda(sleepTime): - return eval('''lambda a, b : int(bool(time.sleep(%f))) + a + b''' %(_sleepTime,)) + return eval(f'''lambda a, b : int(bool(time.sleep({_sleepTime:f}))) + a + b''') def getSleepLambdaWithArgs(sleepTime, args): @@ -145,7 +144,7 @@ def getSleepLambdaWithArgs(sleepTime, args): # print ( 'Function is: %s' %('''lambda %s : int(bool(time.sleep(%f))) + %s''' %(argStr, sleepTime, sumStr, ) ) ) - return eval('''lambda %s : int(bool(time.sleep(%f))) + %s''' % (argStr, sleepTime, sumStr, ) ) + return eval(f'''lambda {argStr} : int(bool(time.sleep({sleepTime:f}))) + {sumStr}''' ) def compareTimes(timeEnd, timeStart, cmpTime, roundTo=None, deltaFixed=.05, deltaPct=None): diff --git a/tests/FuncTimeoutTests/test_Basic.py b/tests/FuncTimeoutTests/test_Basic.py index 2ed1e5c..fc518ea 100755 --- a/tests/FuncTimeoutTests/test_Basic.py +++ b/tests/FuncTimeoutTests/test_Basic.py @@ -38,9 +38,9 @@ def test_funcTimeout(self): try: result = func_timeout(2.5, sleepFunction, args=(5, 13)) except FunctionTimedOut as te: - raise AssertionError('Got unexpected timeout at 2.5 second timeout for 2.00 second function: %s' %(str(te),)) + raise AssertionError(f'Got unexpected timeout at 2.5 second timeout for 2.00 second function: {str(te)}') - assert result == expectedResult , 'Got wrong return from func_timeout.\nGot: %s\nExpected: %s\n' %(repr(result), repr(expectedResult)) + assert result == expectedResult , f'Got wrong return from func_timeout.\nGot: {repr(result)}\nExpected: {repr(expectedResult)}\n' gotException = False try: @@ -53,9 +53,9 @@ def test_funcTimeout(self): try: result = func_timeout(2.5, sleepFunction, args=(5,), kwargs={ 'b' : 13}) except FunctionTimedOut as te: - raise AssertionError('Got unexpected timeout at 2.5 second timeout for 2.00 second function: %s' %(str(te), )) + raise AssertionError(f'Got unexpected timeout at 2.5 second timeout for 2.00 second function: {str(te)}') except Exception as e: - raise AssertionError('Got unknown exception mixing args and kwargs: < %s > %s' %(e.__class__.__name__, str(e))) + raise AssertionError(f'Got unknown exception mixing args and kwargs: < {e.__class__.__name__} > {str(e)}') assert result == expectedResult , 'Got wrong result when mixing args and kwargs' @@ -76,7 +76,7 @@ def test_retry(self): endTime = time.time() assert gotException , 'Expected to get exception' - assert compareTimes(endTime, startTime, .8, 3, deltaFixed=.15) == 0 , 'Expected to wait .8 seconds. Was: %f - %f = %f' %(endTime, startTime, round(endTime - startTime, 3)) + assert compareTimes(endTime, startTime, .8, 3, deltaFixed=.15) == 0 , f'Expected to wait .8 seconds. Was: {endTime:f} - {startTime:f} = {round(endTime - startTime, 3):f}' gotException = False startTime = time.time() @@ -143,8 +143,8 @@ def test_exception(self): assert gotException , 'Expected to get exception' - assert 'timed out after ' in functionTimedOut.msg , 'Expected message to be constructed. Got: %s' %(repr(functionTimedOut.msg), ) - assert round(functionTimedOut.timedOutAfter, 1) == .5 , 'Expected timedOutAfter to equal timeout ( .5 ). Got: %s' %(str(round(functionTimedOut.timedOutAfter, 1)), ) + assert 'timed out after ' in functionTimedOut.msg , f'Expected message to be constructed. Got: {repr(functionTimedOut.msg)}' + assert round(functionTimedOut.timedOutAfter, 1) == .5 , f'Expected timedOutAfter to equal timeout ( .5 ). Got: {str(round(functionTimedOut.timedOutAfter, 1))}' 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 {}' @@ -160,7 +160,7 @@ def test_instantiateExceptionNoArgs(self): 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))) + sys.stderr.write(f'Got unexpected exception in test_instantiateExceptionNoArgs with no arguments. {str(type(_e))} {str(_e)}\n\n') gotException = True assert gotException is False, 'Expected to be able to create FunctionTimedOut exception without arguments.' @@ -173,7 +173,7 @@ def test_instantiateExceptionNoArgs(self): 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))) + sys.stderr.write(f'Got unexpected exception in test_instantiateExceptionNoArgs with fixed message string. {str(type(_e))} {str(_e)}\n\n') gotException = True assert gotException is False , 'Expected to be able to create a FunctionTimedOut exception with a fixed message.' diff --git a/tests/FuncTimeoutTests/test_Decorator.py b/tests/FuncTimeoutTests/test_Decorator.py index ca149ca..b5a8430 100755 --- a/tests/FuncTimeoutTests/test_Decorator.py +++ b/tests/FuncTimeoutTests/test_Decorator.py @@ -331,7 +331,7 @@ def doSleepFuncUnder(a, b): except FunctionTimedOut as fte2: gotException = True except Exception as e: - raise AssertionError('Got exception trying to retry with same timeout: < %s > : %s' %(e.__name__, str(e))) + raise AssertionError(f'Got exception trying to retry with same timeout: < {e.__name__} > : {str(e)}') endTime = time.time() assert gotException , 'Expected to get exception with calculated same 80% timeout on retry' @@ -345,7 +345,7 @@ def doSleepFuncUnder(a, b): except FunctionTimedOut as fte2: gotException = True except Exception as e: - raise AssertionError('Got exception trying to retry with same timeout: < %s > : %s' %(e.__name__, str(e))) + raise AssertionError(f'Got exception trying to retry with same timeout: < {e.__name__} > : {str(e)}') endTime = time.time() assert not gotException , 'Expected to get exception with calculated 80% timeout on retry ( None ) [ No timeout ]' @@ -361,7 +361,7 @@ def doSleepFuncUnder(a, b): except FunctionTimedOut as fte2: gotException = True except Exception as e: - raise AssertionError('Got exception trying to retry with same timeout: < %s > : %s' %(e.__name__, str(e))) + raise AssertionError(f'Got exception trying to retry with same timeout: < {e.__name__} > : {str(e)}') endTime = time.time() assert gotException , 'Expected to get exception with calculated 80% timeout overriden by 60% timeout on retry' @@ -375,7 +375,7 @@ def doSleepFuncUnder(a, b): except FunctionTimedOut as fte2: gotException = True except Exception as e: - raise AssertionError('Got exception trying to retry with same timeout: < %s > : %s' %(e.__name__, str(e))) + raise AssertionError(f'Got exception trying to retry with same timeout: < {e.__name__} > : {str(e)}') endTime = time.time() assert not gotException , 'Expected to get exception with calculated 80% timeout overriden by 150% timeout on retry' diff --git a/tests/FuncTimeoutTests/test_StoppableThread.py b/tests/FuncTimeoutTests/test_StoppableThread.py index 1120a53..eeabf7f 100755 --- a/tests/FuncTimeoutTests/test_StoppableThread.py +++ b/tests/FuncTimeoutTests/test_StoppableThread.py @@ -38,9 +38,9 @@ def test_funcTimeout(self): try: result = func_timeout(1.5, sleepFunction, args=(5, 13)) except FunctionTimedOut as te: - raise AssertionError('Got unexpected timeout at 1.5 second timeout for 1.25 second function: %s' %(str(te),)) + raise AssertionError(f'Got unexpected timeout at 1.5 second timeout for 1.25 second function: {str(te)}') - assert result == expectedResult , 'Got wrong return from func_timeout.\nGot: %s\nExpected: %s\n' %(repr(result), repr(expectedResult)) + assert result == expectedResult , f'Got wrong return from func_timeout.\nGot: {repr(result)}\nExpected: {repr(expectedResult)}\n' gotException = False try: @@ -53,9 +53,9 @@ def test_funcTimeout(self): try: result = func_timeout(1.5, sleepFunction, args=(5,), kwargs={ 'b' : 13}) except FunctionTimedOut as te: - raise AssertionError('Got unexpected timeout at 1.5 second timeout for 1.25 second function: %s' %(str(te), )) + raise AssertionError(f'Got unexpected timeout at 1.5 second timeout for 1.25 second function: {str(te)}') except Exception as e: - raise AssertionError('Got unknown exception mixing args and kwargs: < %s > %s' %(e.__class__.__name__, str(e))) + raise AssertionError(f'Got unknown exception mixing args and kwargs: < {e.__class__.__name__} > {str(e)}') assert result == expectedResult , 'Got wrong result when mixing args and kwargs' @@ -76,7 +76,7 @@ def test_retry(self): endTime = time.time() assert gotException , 'Expected to get exception' - assert compareTimes(endTime, startTime, .3, 3, .15, None) == 0 , 'Expected to wait .3 seconds. Was: %f - %f = %f' %(endTime, startTime, round(endTime - startTime, 3)) + assert compareTimes(endTime, startTime, .3, 3, .15, None) == 0 , f'Expected to wait .3 seconds. Was: {endTime:f} - {startTime:f} = {round(endTime - startTime, 3):f}' gotException = False startTime = time.time() @@ -143,8 +143,8 @@ def test_exception(self): assert gotException , 'Expected to get exception' - assert 'timed out after ' in functionTimedOut.msg , 'Expected message to be constructed. Got: %s' %(repr(functionTimedOut.msg), ) - assert round(functionTimedOut.timedOutAfter, 1) == .3 , 'Expected timedOutAfter to equal timeout ( .3 ). Got: %s' %(str(round(functionTimedOut.timedOutAfter, 1)), ) + assert 'timed out after ' in functionTimedOut.msg , f'Expected message to be constructed. Got: {repr(functionTimedOut.msg)}' + assert round(functionTimedOut.timedOutAfter, 1) == .3 , f'Expected timedOutAfter to equal timeout ( .3 ). Got: {str(round(functionTimedOut.timedOutAfter, 1))}' 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 {}' diff --git a/tests/runTests.py b/tests/runTests.py index 0c6ec12..0a63444 100755 --- a/tests/runTests.py +++ b/tests/runTests.py @@ -37,7 +37,7 @@ def find_mod(modName): # imp.find_module raises import error if cannot find, # but find_spec just returns None # So simulate the ImportError for common interface - raise ImportError('No module named %s' %(modName, )) + raise ImportError(f'No module named {modName}') return modSpec @@ -183,9 +183,9 @@ def try_pip_install(): sys.stderr.write('Failed to install GoodTests via pip module. Falling back to pip executable...\n\n') pipPath = os.path.dirname(sys.executable) + os.sep + 'pip' - print ( 'Searching for pip at "%s"' %(pipPath, ) ) + print ( f'Searching for pip at "{pipPath}"' ) if not os.path.exists(pipPath): - print ( '"%s" does not exist. Scanning PATH to locate a usable pip executable' %(pipPath, )) + print ( f'"{pipPath}" does not exist. Scanning PATH to locate a usable pip executable') pipPath = None searchResults = findExecutable('pip') if not searchResults['success']: @@ -194,8 +194,8 @@ def try_pip_install(): pipPath = searchResults['path'] - print ( 'Found pip executable at "%s"' %(pipPath, ) ) - print ( "Executing: %s %s 'install' 'GoodTests'" %(sys.executable, pipPath) ) + print ( f'Found pip executable at "{pipPath}"' ) + print ( f"Executing: {sys.executable} {pipPath} 'install' 'GoodTests'" ) pipe = subprocess.Popen([sys.executable, pipPath, 'install', 'GoodTests'], shell=False, env=os.environ) res = pipe.wait() @@ -240,7 +240,7 @@ def download_goodTests(GOODTESTS_URL=None): if str != bytes: contents = contents.decode('ascii') except Exception as e: - sys.stderr.write('Failed to download GoodTests.py from "%s"\n%s\n' %(GOODTESTS_URL, str(e))) + sys.stderr.write(f'Failed to download GoodTests.py from "{GOODTESTS_URL}"\n{str(e)}\n') sys.stderr.write('\nTrying pip.\n') res = try_pip_install() if res != 0: @@ -250,7 +250,7 @@ def download_goodTests(GOODTESTS_URL=None): with open('GoodTests.py', 'w') as f: f.write(contents) except Exception as e: - sys.stderr.write('Failed to write to GoodTests.py\n%s\n' %(str(e,))) + sys.stderr.write(f'Failed to write to GoodTests.py\n{str(e)}\n') return 1 try: os.chmod('GoodTests.py', 0o775) @@ -311,7 +311,7 @@ def main(thisDir=None, additionalArgs=[], MY_PACKAGE_MODULE=None, ALLOW_SITE_INS return downloadRet goodTestsInfo = findGoodTests() if goodTestsInfo['success'] is False: - sys.stderr.write('Could not download or find GoodTests.py. Try to download it yourself using "pip install GoodTests", or wget %s\n' %( GOODTESTS_URL,)) + sys.stderr.write(f'Could not download or find GoodTests.py. Try to download it yourself using "pip install GoodTests", or wget {GOODTESTS_URL}\n') return 1 baseName = os.path.basename(MY_PACKAGE_MODULE) @@ -339,13 +339,13 @@ def main(thisDir=None, additionalArgs=[], MY_PACKAGE_MODULE=None, ALLOW_SITE_INS except ImportError as e: sys.path = oldSysPath if not ALLOW_SITE_INSTALL: - sys.stderr.write('Cannot find "%s" locally.\n' %(MY_PACKAGE_MODULE,)) + sys.stderr.write(f'Cannot find "{MY_PACKAGE_MODULE}" locally.\n') return 2 else: try: __import__(baseName) except: - sys.stderr.write('Cannot find "%s" locally or in global python path.\n' %(MY_PACKAGE_MODULE,)) + sys.stderr.write(f'Cannot find "{MY_PACKAGE_MODULE}" locally or in global python path.\n') return 2 if foundIt is True: @@ -372,16 +372,16 @@ def main(thisDir=None, additionalArgs=[], MY_PACKAGE_MODULE=None, ALLOW_SITE_INS eName = e.message.split()[-1] if eName != MY_PACKAGE_MODULE: - sys.stderr.write('Error while importing %s: %s\n Likely this is another dependency that needs to be installed\nPerhaps run "pip install %s" or install the providing package.\n\n' %(eName, str(e), eName)) + sys.stderr.write(f'Error while importing {eName}: {str(e)}\n Likely this is another dependency that needs to be installed\nPerhaps run "pip install {eName}" or install the providing package.\n\n') return 1 - sys.stderr.write('Could not import %s. Either install it or otherwise add to PYTHONPATH\n%s\n' %(MY_PACKAGE_MODULE, str(e))) + sys.stderr.write(f'Could not import {MY_PACKAGE_MODULE}. Either install it or otherwise add to PYTHONPATH\n{str(e)}\n') return 1 if not os.path.isdir(MY_TEST_DIRECTORY): if not os.path.exists(MY_TEST_DIRECTORY): - sys.stderr.write('Cannot find test directory: %s\n' %(MY_TEST_DIRECTORY,)) + sys.stderr.write(f'Cannot find test directory: {MY_TEST_DIRECTORY}\n') else: - sys.stderr.write('Provided test directory, "%s" is not a directory.\n' %(MY_TEST_DIRECTORY,)) + sys.stderr.write(f'Provided test directory, "{MY_TEST_DIRECTORY}" is not a directory.\n') return 3 sys.stdout.write('Starting test..\n')