This commit is contained in:
Ilya Kamenshchikov 2021-01-17 13:31:21 +01:00 committed by GitHub
commit 37a0187270
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 55 additions and 73 deletions

View File

@ -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 func_set_timeout(timeout, allowOverride=False):
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:

View File

@ -69,11 +69,11 @@ class FunctionTimedOut(BaseException):
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):
'''

View File

@ -1,5 +0,0 @@
# Python2 allows specifying an alternate traceback.
def raise_exception(exception):
raise exception[0] , None , exception[0].__traceback__

View File

@ -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

View File

@ -26,11 +26,11 @@ if __name__ == '__main__':
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',
version='4.3.5',
version='4.4.0',
packages=['func_timeout'],
author='Tim Savannah',
author_email='kata198@gmail.com',
@ -44,12 +44,10 @@ if __name__ == '__main__':
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'
]
)

View File

@ -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')

View File

@ -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):

View File

@ -38,9 +38,9 @@ class TestBasic(object):
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 @@ class TestBasic(object):
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 @@ class TestBasic(object):
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 @@ class TestBasic(object):
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 @@ class TestBasic(object):
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 @@ class TestBasic(object):
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.'

View File

@ -331,7 +331,7 @@ class TestDecorator(object):
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 @@ class TestDecorator(object):
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 @@ class TestDecorator(object):
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 @@ class TestDecorator(object):
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'

View File

@ -38,9 +38,9 @@ class TestBasic(object):
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 @@ class TestBasic(object):
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 @@ class TestBasic(object):
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 @@ class TestBasic(object):
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 {}'

View File

@ -37,7 +37,7 @@ try:
# 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')