Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42473ad7ff | ||
|
|
eeda3385eb | ||
|
|
50baa8db50 | ||
|
|
45cdbdfe4f | ||
|
|
514225621f | ||
|
|
5452efab5b | ||
|
|
5d2afb9582 | ||
|
|
af7d6e8a39 | ||
|
|
cf8a9545bc | ||
|
|
176ef7b9d9 | ||
|
|
1eba58f599 | ||
|
|
f759dbfeec | ||
|
|
82493fe61d | ||
|
|
945b971637 | ||
|
|
64c3098922 | ||
|
|
3fc998c3ca | ||
|
|
caffd41f95 | ||
|
|
1722f34da2 | ||
|
|
bdc7072176 | ||
|
|
e90d850871 | ||
|
|
8ab818f94a | ||
|
|
317a65ce08 | ||
|
|
5f9a1e493a | ||
|
|
6e869794ef | ||
|
|
84528e67ed | ||
|
|
81759d9b2b |
22
ChangeLog
22
ChangeLog
@ -1,3 +1,25 @@
|
|||||||
|
|
||||||
|
* 4.3.5 - Aug 19 2019
|
||||||
|
|
||||||
|
- Increase times in unit tests to ensure passing on slow systems
|
||||||
|
|
||||||
|
- Allow instantiating FunctionTimedOut exception without arguments ( will replace function name with "Unknown Function" and timedOutAfter with "Unknown" when absent)
|
||||||
|
|
||||||
|
- Update runTests.py from latest GoodTests distrib, 3.0.5 from 2.1.1
|
||||||
|
|
||||||
|
|
||||||
|
* 4.3.4 - Aug 19 2019
|
||||||
|
|
||||||
|
- Merge patch by Rafal Florczak to use threading.Thread.is_alive vs now deprecated threading.Thread.isAlive
|
||||||
|
|
||||||
|
- Regenerate docs
|
||||||
|
|
||||||
|
|
||||||
|
* 4.3.3 - May 13 2019
|
||||||
|
|
||||||
|
- More documentation updates
|
||||||
|
|
||||||
|
|
||||||
* 4.3.2 - May 13 2019
|
* 4.3.2 - May 13 2019
|
||||||
|
|
||||||
- Update docs, update README, note that this still works with Python 3.7
|
- Update docs, update README, note that this still works with Python 3.7
|
||||||
|
|||||||
72
README.md
72
README.md
@ -103,20 +103,54 @@ The way it works is that you pass it an exception, and it raises it via the cpyt
|
|||||||
Using StoppableThread
|
Using StoppableThread
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
Your thread should extend func\_timeout.StoppableThread\.StoppableThread and implement the "run" method, same as a normal thread.
|
You can use StoppableThread one of two ways:
|
||||||
|
|
||||||
It is recommended that you create an exception that extends BaseException instead of Exception, otherwise code like this will never stop:
|
**As a Parent Class**
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
doSomething()
|
|
||||||
except Exception as e:
|
|
||||||
continue
|
|
||||||
|
|
||||||
If you can't avoid such code (third-party lib?) you can set the "repeatEvery" to a very very low number (like .00001 ), so hopefully it will raise, go to the except clause, and then raise again before "continue" is hit.
|
|
||||||
|
|
||||||
|
|
||||||
Stopping A Thread
|
Your thread can extend func\_timeout.StoppableThread\.StoppableThread and implement the "run" method, same as a normal thread.
|
||||||
|
|
||||||
|
|
||||||
|
from func_timeout.StoppableThread import StoppableThread
|
||||||
|
|
||||||
|
class MyThread(StoppableThread):
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
# Code here
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
Then, you can create and start this thread like:
|
||||||
|
|
||||||
|
myThread = MyThread()
|
||||||
|
|
||||||
|
# Uncomment next line to start thread in "daemon mode" -- i.e. will terminate/join automatically upon main thread exit
|
||||||
|
|
||||||
|
#myThread.daemon = True
|
||||||
|
|
||||||
|
myThread.start()
|
||||||
|
|
||||||
|
|
||||||
|
Then, at any time during the thread's execution, you can call \.stop( StopExceptionType ) to stop it ( more in "Stopping a Thread" below
|
||||||
|
|
||||||
|
**Direct Thread To Execute A Function**
|
||||||
|
|
||||||
|
Alternatively, you can instantiate StoppableThread directly and pass the "target", "args", and "kwargs" arguments to the constructor
|
||||||
|
|
||||||
|
myThread = StoppableThread( target=myFunction, args=('ordered', 'args', 'here'), kwargs={ 'keyword args' : 'here' } )
|
||||||
|
|
||||||
|
# Uncomment next line to start thread in "daemon mode" -- i.e. will terminate/join automatically upon main thread exit
|
||||||
|
|
||||||
|
#myThread.daemon = True
|
||||||
|
|
||||||
|
myThread.start()
|
||||||
|
|
||||||
|
|
||||||
|
This will allow you to call functions in stoppable threads, for example handlers in an event loop, which can be stopped later via the \.stop() method.
|
||||||
|
|
||||||
|
|
||||||
|
Stopping a Thread
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
||||||
@ -156,6 +190,20 @@ Consider using a custom exception type which extends BaseException, which you ca
|
|||||||
The exception type you pass will be raised every #raiseEvery seconds in the context of that stoppable thread. You can tweak this value to give yourself more time for cleanups, or you can shrink it down to break out of empty exception handlers ( try/except with bare except ).
|
The exception type you pass will be raised every #raiseEvery seconds in the context of that stoppable thread. You can tweak this value to give yourself more time for cleanups, or you can shrink it down to break out of empty exception handlers ( try/except with bare except ).
|
||||||
|
|
||||||
|
|
||||||
|
**Notes on Exception Type**
|
||||||
|
|
||||||
|
It is recommended that you create an exception that extends BaseException instead of Exception, otherwise code like this will never stop:
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
doSomething()
|
||||||
|
except Exception as e:
|
||||||
|
continue
|
||||||
|
|
||||||
|
If you can't avoid such code (third-party lib?) you can set the "repeatEvery" to a very very low number (like .00001 ), so hopefully it will raise, go to the except clause, and then raise again before "continue" is hit.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You may want to consider using singleton types with fixed error messages, so that tracebacks, etc. log that the call timed out.
|
You may want to consider using singleton types with fixed error messages, so that tracebacks, etc. log that the call timed out.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
@ -174,7 +222,7 @@ This will force 'Server is shutting down' as the message held by this exception.
|
|||||||
Pydoc
|
Pydoc
|
||||||
=====
|
=====
|
||||||
|
|
||||||
Find the latest pydoc at http://htmlpreview.github.io/?https://github.com/kata198/func_timeout/blob/master/doc/func_timeout.html?vers=4.3.2 .
|
Find the latest pydoc at http://htmlpreview.github.io/?https://github.com/kata198/func_timeout/blob/master/doc/func_timeout.html?vers=4.3.5 .
|
||||||
|
|
||||||
|
|
||||||
Support
|
Support
|
||||||
|
|||||||
81
README.rst
81
README.rst
@ -119,24 +119,55 @@ The way it works is that you pass it an exception, and it raises it via the cpyt
|
|||||||
Using StoppableThread
|
Using StoppableThread
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
Your thread should extend func\_timeout.StoppableThread\.StoppableThread and implement the "run" method, same as a normal thread.
|
You can use StoppableThread one of two ways:
|
||||||
|
|
||||||
It is recommended that you create an exception that extends BaseException instead of Exception, otherwise code like this will never stop:
|
**As a Parent Class**
|
||||||
|
|
||||||
while True:
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
doSomething()
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
|
|
||||||
continue
|
|
||||||
|
|
||||||
If you can't avoid such code (third-party lib?) you can set the "repeatEvery" to a very very low number (like .00001 ), so hopefully it will raise, go to the except clause, and then raise again before "continue" is hit.
|
|
||||||
|
|
||||||
|
|
||||||
Stopping A Thread
|
Your thread can extend func\_timeout.StoppableThread\.StoppableThread and implement the "run" method, same as a normal thread.
|
||||||
|
|
||||||
|
|
||||||
|
from func\_timeout.StoppableThread import StoppableThread
|
||||||
|
|
||||||
|
class MyThread(StoppableThread):
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
# Code here
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
Then, you can create and start this thread like:
|
||||||
|
|
||||||
|
myThread = MyThread()
|
||||||
|
|
||||||
|
# Uncomment next line to start thread in "daemon mode" \-\- i.e. will terminate/join automatically upon main thread exit
|
||||||
|
|
||||||
|
#myThread.daemon = True
|
||||||
|
|
||||||
|
myThread.start()
|
||||||
|
|
||||||
|
|
||||||
|
Then, at any time during the thread's execution, you can call \.stop( StopExceptionType ) to stop it ( more in "Stopping a Thread" below
|
||||||
|
|
||||||
|
**Direct Thread To Execute A Function**
|
||||||
|
|
||||||
|
Alternatively, you can instantiate StoppableThread directly and pass the "target", "args", and "kwargs" arguments to the constructor
|
||||||
|
|
||||||
|
myThread = StoppableThread( target=myFunction, args=('ordered', 'args', 'here'), kwargs={ 'keyword args' : 'here' } )
|
||||||
|
|
||||||
|
# Uncomment next line to start thread in "daemon mode" \-\- i.e. will terminate/join automatically upon main thread exit
|
||||||
|
|
||||||
|
#myThread.daemon = True
|
||||||
|
|
||||||
|
myThread.start()
|
||||||
|
|
||||||
|
|
||||||
|
This will allow you to call functions in stoppable threads, for example handlers in an event loop, which can be stopped later via the \.stop() method.
|
||||||
|
|
||||||
|
|
||||||
|
Stopping a Thread
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
||||||
@ -179,6 +210,24 @@ Consider using a custom exception type which extends BaseException, which you ca
|
|||||||
The exception type you pass will be raised every #raiseEvery seconds in the context of that stoppable thread. You can tweak this value to give yourself more time for cleanups, or you can shrink it down to break out of empty exception handlers ( try/except with bare except ).
|
The exception type you pass will be raised every #raiseEvery seconds in the context of that stoppable thread. You can tweak this value to give yourself more time for cleanups, or you can shrink it down to break out of empty exception handlers ( try/except with bare except ).
|
||||||
|
|
||||||
|
|
||||||
|
**Notes on Exception Type**
|
||||||
|
|
||||||
|
It is recommended that you create an exception that extends BaseException instead of Exception, otherwise code like this will never stop:
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
doSomething()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
If you can't avoid such code (third-party lib?) you can set the "repeatEvery" to a very very low number (like .00001 ), so hopefully it will raise, go to the except clause, and then raise again before "continue" is hit.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You may want to consider using singleton types with fixed error messages, so that tracebacks, etc. log that the call timed out.
|
You may want to consider using singleton types with fixed error messages, so that tracebacks, etc. log that the call timed out.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
@ -197,7 +246,7 @@ This will force 'Server is shutting down' as the message held by this exception.
|
|||||||
Pydoc
|
Pydoc
|
||||||
=====
|
=====
|
||||||
|
|
||||||
Find the latest pydoc at http://htmlpreview.github.io/?https://github.com/kata198/func_timeout/blob/master/doc/func_timeout.html?vers=4.3.2 .
|
Find the latest pydoc at http://htmlpreview.github.io/?https://github.com/kata198/func_timeout/blob/master/doc/func_timeout.html?vers=4.3.5 .
|
||||||
|
|
||||||
|
|
||||||
Support
|
Support
|
||||||
|
|||||||
@ -53,7 +53,19 @@ LICENSE, otherwise it is available at https://gith
|
|||||||
</dl>
|
</dl>
|
||||||
<hr />
|
<hr />
|
||||||
Methods defined here:<br />
|
Methods defined here:<br />
|
||||||
<dl ><dt ><a name="FunctionTimedOut-__init__" ><strong >__init__</strong></a>(self, msg='', timedOutAfter=None, timedOutFunction=None, timedOutArgs=None, timedOutKwargs=None)</dt><dd ><tt >Initialize self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
<dl ><dt ><a name="FunctionTimedOut-__init__" ><strong >__init__</strong></a>(self, msg='', timedOutAfter=None, timedOutFunction=None, timedOutArgs=None, timedOutKwargs=None)</dt><dd ><tt >__init__ - Create this exception type.<br />
|
||||||
|
<br />
|
||||||
|
You should not need to do this outside of testing, it will be created by the func_timeout API<br />
|
||||||
|
<br />
|
||||||
|
@param msg <str> - A predefined message, otherwise we will attempt to generate one from the other arguments.<br />
|
||||||
|
<br />
|
||||||
|
@param timedOutAfter <None/float> - Number of seconds before timing-out. Filled-in by API, None will produce "Unknown"<br />
|
||||||
|
<br />
|
||||||
|
@param timedOutFunction <None/function> - Reference to the function that timed-out. Filled-in by API." None will produce "Unknown Function"<br />
|
||||||
|
<br />
|
||||||
|
@param timedOutArgs <None/tuple/list> - List of fixed-order arguments ( *args ), or None for no args.<br />
|
||||||
|
<br />
|
||||||
|
@param timedOutKwargs <None/dict> - Dict of keyword arg ( **kwargs ) names to values, or None for no kwargs.</tt></dd></dl>
|
||||||
|
|
||||||
<dl ><dt ><a name="FunctionTimedOut-getMsg" ><strong >getMsg</strong></a>(self)</dt><dd ><tt >getMsg - Generate a default message based on parameters to <a href="#FunctionTimedOut" >FunctionTimedOut</a> exception'<br />
|
<dl ><dt ><a name="FunctionTimedOut-getMsg" ><strong >getMsg</strong></a>(self)</dt><dd ><tt >getMsg - Generate a default message based on parameters to <a href="#FunctionTimedOut" >FunctionTimedOut</a> exception'<br />
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="heading" >
|
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="heading" >
|
||||||
<tr bgcolor="#7799ee" >
|
<tr bgcolor="#7799ee" >
|
||||||
<td valign="bottom" > <br />
|
<td valign="bottom" > <br />
|
||||||
<font color="#ffffff" face="helvetica, arial" > <br /><big ><big ><strong >func_timeout</strong></big></big> (version 4.3.2)</font></td><td align="right" valign="bottom" ><font color="#ffffff" face="helvetica, arial" ><a href="func_timeout.html" >index</a></font></td></tr></table>
|
<font color="#ffffff" face="helvetica, arial" > <br /><big ><big ><strong >func_timeout</strong></big></big> (version 4.3.5)</font></td><td align="right" valign="bottom" ><font color="#ffffff" face="helvetica, arial" ><a href="func_timeout.html" >index</a></font></td></tr></table>
|
||||||
<p ><tt >Copyright (c) 2016, 2017, 2019 Tim Savannah All Rights Reserved.<br />
|
<p ><tt >Copyright (c) 2016, 2017, 2019 Tim Savannah All Rights Reserved.<br />
|
||||||
<br />
|
<br />
|
||||||
Licensed under the Lesser GNU Public License Version 3, LGPLv3. You should have recieved a copy of this with the source distribution as<br />
|
Licensed under the Lesser GNU Public License Version 3, LGPLv3. You should have recieved a copy of this with the source distribution as<br />
|
||||||
@ -71,7 +71,19 @@ LICENSE, otherwise it is available at https://gith
|
|||||||
</dl>
|
</dl>
|
||||||
<hr />
|
<hr />
|
||||||
Methods defined here:<br />
|
Methods defined here:<br />
|
||||||
<dl ><dt ><a name="FunctionTimedOut-__init__" ><strong >__init__</strong></a>(self, msg='', timedOutAfter=None, timedOutFunction=None, timedOutArgs=None, timedOutKwargs=None)</dt><dd ><tt >Initialize self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
<dl ><dt ><a name="FunctionTimedOut-__init__" ><strong >__init__</strong></a>(self, msg='', timedOutAfter=None, timedOutFunction=None, timedOutArgs=None, timedOutKwargs=None)</dt><dd ><tt >__init__ - Create this exception type.<br />
|
||||||
|
<br />
|
||||||
|
You should not need to do this outside of testing, it will be created by the func_timeout API<br />
|
||||||
|
<br />
|
||||||
|
@param msg <str> - A predefined message, otherwise we will attempt to generate one from the other arguments.<br />
|
||||||
|
<br />
|
||||||
|
@param timedOutAfter <None/float> - Number of seconds before timing-out. Filled-in by API, None will produce "Unknown"<br />
|
||||||
|
<br />
|
||||||
|
@param timedOutFunction <None/function> - Reference to the function that timed-out. Filled-in by API." None will produce "Unknown Function"<br />
|
||||||
|
<br />
|
||||||
|
@param timedOutArgs <None/tuple/list> - List of fixed-order arguments ( *args ), or None for no args.<br />
|
||||||
|
<br />
|
||||||
|
@param timedOutKwargs <None/dict> - Dict of keyword arg ( **kwargs ) names to values, or None for no kwargs.</tt></dd></dl>
|
||||||
|
|
||||||
<dl ><dt ><a name="FunctionTimedOut-getMsg" ><strong >getMsg</strong></a>(self)</dt><dd ><tt >getMsg - Generate a default message based on parameters to <a href="#FunctionTimedOut" >FunctionTimedOut</a> exception'<br />
|
<dl ><dt ><a name="FunctionTimedOut-getMsg" ><strong >getMsg</strong></a>(self)</dt><dd ><tt >getMsg - Generate a default message based on parameters to <a href="#FunctionTimedOut" >FunctionTimedOut</a> exception'<br />
|
||||||
<br />
|
<br />
|
||||||
@ -383,5 +395,5 @@ to return cleanly, but in most cases it
|
|||||||
|
|
||||||
<tr ><td bgcolor="#55aa55" ><tt > </tt></td><td > </td>
|
<tr ><td bgcolor="#55aa55" ><tt > </tt></td><td > </td>
|
||||||
<td width="100%" ><strong >__all__</strong> = ('func_timeout', 'func_set_timeout', 'FunctionTimedOut', 'StoppableThread')<br />
|
<td width="100%" ><strong >__all__</strong> = ('func_timeout', 'func_set_timeout', 'FunctionTimedOut', 'StoppableThread')<br />
|
||||||
<strong >__version_tuple__</strong> = (4, 3, 2)</td></tr></table>
|
<strong >__version_tuple__</strong> = (4, 3, 5)</td></tr></table>
|
||||||
</p></p></p></p></body></html>
|
</p></p></p></p></body></html>
|
||||||
@ -1,23 +1,20 @@
|
|||||||
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||||
<html><head><title>Python: module func_timeout.py2_raise</title>
|
<html ><head ><title >Python: module func_timeout.py2_raise</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
</head><body bgcolor="#f0f0f8">
|
</head><body bgcolor="#f0f0f8" >
|
||||||
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="heading" >
|
||||||
<tr bgcolor="#7799ee">
|
<tr bgcolor="#7799ee" >
|
||||||
<td valign=bottom> <br>
|
<td valign="bottom" > <br />
|
||||||
<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong><a href="func_timeout.html"><font color="#ffffff">func_timeout</font></a>.py2_raise</strong></big></big></font></td
|
<font color="#ffffff" face="helvetica, arial" > <br /><big ><big ><strong ><a href="func_timeout.html" ><font color="#ffffff" >func_timeout</font></a>.py2_raise</strong></big></big></font></td><td align="right" valign="bottom" ><font color="#ffffff" face="helvetica, arial" ><a href="func_timeout.html" >index</a></font></td></tr></table>
|
||||||
><td align=right valign=bottom
|
<p ><tt ># Python2 allows specifying an alternate traceback.</tt></p>
|
||||||
><font color="#ffffff" face="helvetica, arial"><a href="func_timeout.html">index</a><br></td></tr></table>
|
<p >
|
||||||
<p><tt># Python2 allows specifying an alternate traceback.</tt></p>
|
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||||
<p>
|
<tr bgcolor="#eeaa77" >
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
<td colspan="3" valign="bottom" > <br />
|
||||||
<tr bgcolor="#eeaa77">
|
<font color="#ffffff" face="helvetica, arial" ><big ><strong >Functions</strong></big></font></td></tr>
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
|
|
||||||
|
|
||||||
<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td>
|
<tr ><td bgcolor="#eeaa77" ><tt > </tt></td><td > </td>
|
||||||
<td width="100%"><dl><dt><a name="-raise_exception"><strong>raise_exception</strong></a>(exception)</dt><dd><tt># Python2 allows specifying an alternate traceback.</tt></dd></dl>
|
<td width="100%" ><dl ><dt ><a name="-raise_exception" ><strong >raise_exception</strong></a>(exception)</dt><dd ><tt ># Python2 allows specifying an alternate traceback.</tt></dd></dl>
|
||||||
</td></tr></table>
|
</td></tr></table>
|
||||||
</body></html>
|
</p></body></html>
|
||||||
@ -38,7 +38,7 @@ class StoppableThread(threading.Thread):
|
|||||||
'''
|
'''
|
||||||
_stopThread - @see StoppableThread.stop
|
_stopThread - @see StoppableThread.stop
|
||||||
'''
|
'''
|
||||||
if self.isAlive() is False:
|
if self.is_alive() is False:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
self._stderr = open(os.devnull, 'w')
|
self._stderr = open(os.devnull, 'w')
|
||||||
@ -120,7 +120,7 @@ class JoinThread(threading.Thread):
|
|||||||
# If py2, call this first to start thread termination cleanly.
|
# If py2, call this first to start thread termination cleanly.
|
||||||
# Python3 does not need such ( nor does it provide.. )
|
# Python3 does not need such ( nor does it provide.. )
|
||||||
self.otherThread._Thread__stop()
|
self.otherThread._Thread__stop()
|
||||||
while self.otherThread.isAlive():
|
while self.otherThread.is_alive():
|
||||||
# We loop raising exception incase it's caught hopefully this breaks us far out.
|
# We loop raising exception incase it's caught hopefully this breaks us far out.
|
||||||
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self.otherThread.ident), ctypes.py_object(self.exception))
|
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self.otherThread.ident), ctypes.py_object(self.exception))
|
||||||
self.otherThread.join(self.repeatEvery)
|
self.otherThread.join(self.repeatEvery)
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
__version__ = '4.3.2'
|
__version__ = '4.3.5'
|
||||||
__version_tuple__ = (4, 3, 2)
|
__version_tuple__ = (4, 3, 5)
|
||||||
|
|
||||||
__all__ = ('func_timeout', 'func_set_timeout', 'FunctionTimedOut', 'StoppableThread')
|
__all__ = ('func_timeout', 'func_set_timeout', 'FunctionTimedOut', 'StoppableThread')
|
||||||
|
|
||||||
|
|||||||
@ -86,7 +86,7 @@ def func_timeout(timeout, func, args=(), kwargs=None):
|
|||||||
thread.join(timeout)
|
thread.join(timeout)
|
||||||
|
|
||||||
stopException = None
|
stopException = None
|
||||||
if thread.isAlive():
|
if thread.is_alive():
|
||||||
isStopped = True
|
isStopped = True
|
||||||
|
|
||||||
class FunctionTimedOutTempType(FunctionTimedOut):
|
class FunctionTimedOutTempType(FunctionTimedOut):
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
'''
|
'''
|
||||||
Copyright (c) 2016 Tim Savannah All Rights Reserved.
|
Copyright (c) 2016, 2017, 2019 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
|
||||||
@ -25,6 +25,22 @@ class FunctionTimedOut(BaseException):
|
|||||||
|
|
||||||
|
|
||||||
def __init__(self, msg='', timedOutAfter=None, timedOutFunction=None, timedOutArgs=None, timedOutKwargs=None):
|
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 <str> - A predefined message, otherwise we will attempt to generate one from the other arguments.
|
||||||
|
|
||||||
|
@param timedOutAfter <None/float> - Number of seconds before timing-out. Filled-in by API, None will produce "Unknown"
|
||||||
|
|
||||||
|
@param timedOutFunction <None/function> - Reference to the function that timed-out. Filled-in by API." None will produce "Unknown Function"
|
||||||
|
|
||||||
|
@param timedOutArgs <None/tuple/list> - List of fixed-order arguments ( *args ), or None for no args.
|
||||||
|
|
||||||
|
@param timedOutKwargs <None/dict> - Dict of keyword arg ( **kwargs ) names to values, or None for no kwargs.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
self.timedOutAfter = timedOutAfter
|
self.timedOutAfter = timedOutAfter
|
||||||
|
|
||||||
@ -46,7 +62,18 @@ class FunctionTimedOut(BaseException):
|
|||||||
|
|
||||||
@return <str> - Message
|
@return <str> - 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):
|
def retry(self, timeout=RETRY_SAME_TIMEOUT):
|
||||||
'''
|
'''
|
||||||
|
|||||||
6
mkdoc.sh
6
mkdoc.sh
@ -1,9 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Ensure we are in the project root directory
|
||||||
|
cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
ALL_MODS="$(echo func_timeout/*.py | tr ' ' '\n' | sed -e 's|/|.|g' -e 's|.py$||g' -e 's|.__init__$||g' | tr '\n' ' ')"
|
ALL_MODS="$(echo func_timeout/*.py | tr ' ' '\n' | sed -e 's|/|.|g' -e 's|.py$||g' -e 's|.__init__$||g' | tr '\n' ' ')"
|
||||||
|
|
||||||
pydoc -w ${ALL_MODS}
|
pydoc -w ${ALL_MODS}
|
||||||
mv *.html doc/
|
mv func_timeout*.html doc/
|
||||||
pushd doc >/dev/null 2>&1
|
pushd doc >/dev/null 2>&1
|
||||||
rm -f index.html
|
rm -f index.html
|
||||||
|
|
||||||
|
|||||||
5
setup.py
5
setup.py
@ -30,7 +30,7 @@ if __name__ == '__main__':
|
|||||||
log_description = summary
|
log_description = summary
|
||||||
|
|
||||||
setup(name='func_timeout',
|
setup(name='func_timeout',
|
||||||
version='4.3.2',
|
version='4.3.7',
|
||||||
packages=['func_timeout'],
|
packages=['func_timeout'],
|
||||||
author='Tim Savannah',
|
author='Tim Savannah',
|
||||||
author_email='kata198@gmail.com',
|
author_email='kata198@gmail.com',
|
||||||
@ -50,6 +50,9 @@ if __name__ == '__main__':
|
|||||||
'Programming Language :: Python :: 3.5',
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
'Programming Language :: Python :: 3.7',
|
'Programming Language :: Python :: 3.7',
|
||||||
|
'Programming Language :: Python :: 3.8',
|
||||||
|
'Programming Language :: Python :: 3.9',
|
||||||
|
'Programming Language :: Python :: 3.10',
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
'Topic :: Software Development :: Libraries :: Python Modules'
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
'''
|
||||||
|
testit.py - Example code for ad-hoc function timeouts.
|
||||||
|
|
||||||
|
Newer tests for all features found in "tests" directory.
|
||||||
|
'''
|
||||||
|
|
||||||
from func_timeout import func_timeout, FunctionTimedOut
|
from func_timeout import func_timeout, FunctionTimedOut
|
||||||
import time
|
import time
|
||||||
@ -23,6 +28,7 @@ if __name__ == '__main__':
|
|||||||
myException = e
|
myException = e
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
print ( "\nRetrying with longer timeout, should get 16+17=33:" )
|
||||||
if myException is not None:
|
if myException is not None:
|
||||||
print ( "\nGot: %s\n" %( str(myException.retry(2.5)), ) )
|
print ( "\nGot: %s\n" %( str(myException.retry(2.5)), ) )
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -160,6 +160,7 @@ def compareTimes(timeEnd, timeStart, cmpTime, roundTo=None, deltaFixed=.05, delt
|
|||||||
@param roundTo <None/int> - Number of digits to round-off to
|
@param roundTo <None/int> - Number of digits to round-off to
|
||||||
|
|
||||||
@param deltaFixed <float/None> Default .05, If provided and if difference is within this much, the two values are considered equal
|
@param deltaFixed <float/None> Default .05, If provided and if difference is within this much, the two values are considered equal
|
||||||
|
|
||||||
@param deltaPct <float/None> Default None, if provided and if difference is within this much, the two values are considered equal. 1 = 100%, .5 = 50%
|
@param deltaPct <float/None> Default None, if provided and if difference is within this much, the two values are considered equal. 1 = 100%, .5 = 50%
|
||||||
|
|
||||||
Example: if trying to determine if function ran for 2 seconds with an error of .05 seconds,
|
Example: if trying to determine if function ran for 2 seconds with an error of .05 seconds,
|
||||||
|
|||||||
@ -25,7 +25,7 @@ class TestBasic(object):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
def test_funcTimeout(self):
|
def test_funcTimeout(self):
|
||||||
sleepFunction = getSleepLambda(1.25)
|
sleepFunction = getSleepLambda(2.00)
|
||||||
|
|
||||||
expectedResult = 5 + 13
|
expectedResult = 5 + 13
|
||||||
|
|
||||||
@ -36,9 +36,9 @@ class TestBasic(object):
|
|||||||
assert result == expectedResult , 'Did not get return from sleepFunction'
|
assert result == expectedResult , 'Did not get return from sleepFunction'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = func_timeout(1.5, sleepFunction, args=(5, 13))
|
result = func_timeout(2.5, sleepFunction, args=(5, 13))
|
||||||
except FunctionTimedOut as te:
|
except FunctionTimedOut as te:
|
||||||
raise AssertionError('Got unexpected timeout at 1.5 second timeout for 1.25 second function: %s' %(str(te),))
|
raise AssertionError('Got unexpected timeout at 2.5 second timeout for 2.00 second function: %s' %(str(te),))
|
||||||
|
|
||||||
assert result == expectedResult , 'Got wrong return from func_timeout.\nGot: %s\nExpected: %s\n' %(repr(result), repr(expectedResult))
|
assert result == expectedResult , 'Got wrong return from func_timeout.\nGot: %s\nExpected: %s\n' %(repr(result), repr(expectedResult))
|
||||||
|
|
||||||
@ -51,16 +51,16 @@ class TestBasic(object):
|
|||||||
assert gotException , 'Expected to get FunctionTimedOut exception for 1.25 sec function at 1s timeout'
|
assert gotException , 'Expected to get FunctionTimedOut exception for 1.25 sec function at 1s timeout'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = func_timeout(1.5, sleepFunction, args=(5,), kwargs={ 'b' : 13})
|
result = func_timeout(2.5, sleepFunction, args=(5,), kwargs={ 'b' : 13})
|
||||||
except FunctionTimedOut as te:
|
except FunctionTimedOut as te:
|
||||||
raise AssertionError('Got unexpected timeout at 1.5 second timeout for 1.25 second function: %s' %(str(te), ))
|
raise AssertionError('Got unexpected timeout at 2.5 second timeout for 2.00 second function: %s' %(str(te), ))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise AssertionError('Got unknown exception mixing args and kwargs: < %s > %s' %(e.__class__.__name__, str(e)))
|
raise AssertionError('Got unknown exception mixing args and kwargs: < %s > %s' %(e.__class__.__name__, str(e)))
|
||||||
|
|
||||||
assert result == expectedResult , 'Got wrong result when mixing args and kwargs'
|
assert result == expectedResult , 'Got wrong result when mixing args and kwargs'
|
||||||
|
|
||||||
def test_retry(self):
|
def test_retry(self):
|
||||||
sleepFunction = getSleepLambda(.5)
|
sleepFunction = getSleepLambda(1.2)
|
||||||
|
|
||||||
expectedResult = 5 + 19
|
expectedResult = 5 + 19
|
||||||
|
|
||||||
@ -69,14 +69,14 @@ class TestBasic(object):
|
|||||||
|
|
||||||
startTime = time.time()
|
startTime = time.time()
|
||||||
try:
|
try:
|
||||||
result = func_timeout(.3, sleepFunction, args=(5, 19))
|
result = func_timeout(.8, sleepFunction, args=(5, 19))
|
||||||
except FunctionTimedOut as fte:
|
except FunctionTimedOut as fte:
|
||||||
functionTimedOut = fte
|
functionTimedOut = fte
|
||||||
gotException = True
|
gotException = True
|
||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception'
|
assert gotException , 'Expected to get exception'
|
||||||
assert compareTimes(endTime, startTime, .3, 3, None, .10) == 0 , 'Expected to wait .3 seconds. Was: %f - %f = %f' %(endTime, startTime, round(endTime - startTime, 3))
|
assert compareTimes(endTime, startTime, .8, 3, deltaFixed=.15) == 0 , 'Expected to wait .8 seconds. Was: %f - %f = %f' %(endTime, startTime, round(endTime - startTime, 3))
|
||||||
|
|
||||||
gotException = False
|
gotException = False
|
||||||
startTime = time.time()
|
startTime = time.time()
|
||||||
@ -87,7 +87,7 @@ class TestBasic(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception on retry.'
|
assert gotException , 'Expected to get exception on retry.'
|
||||||
assert compareTimes(endTime, startTime, .3, 3, None, .10) == 0 , 'Expected retry with no arguments to use same timeout of .3'
|
assert compareTimes(endTime, startTime, .8, 3, deltaFixed=.15) == 0 , 'Expected retry with no arguments to use same timeout of .8'
|
||||||
|
|
||||||
gotException = False
|
gotException = False
|
||||||
startTime = time.time()
|
startTime = time.time()
|
||||||
@ -98,19 +98,19 @@ class TestBasic(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert not gotException , 'Did NOT to get exception with no timeout'
|
assert not gotException , 'Did NOT to get exception with no timeout'
|
||||||
assert compareTimes(endTime, startTime, .5, 3, None, .10) == 0 , 'Expected retry with None as timeout to last full length of function'
|
assert compareTimes(endTime, startTime, 1.2, 3, deltaFixed=.15) == 0 , 'Expected retry with None as timeout to last full length of function'
|
||||||
|
|
||||||
gotException = False
|
gotException = False
|
||||||
startTime = time.time()
|
startTime = time.time()
|
||||||
try:
|
try:
|
||||||
result = functionTimedOut.retry(.4)
|
result = functionTimedOut.retry(.55)
|
||||||
except FunctionTimedOut:
|
except FunctionTimedOut:
|
||||||
gotException = True
|
gotException = True
|
||||||
finally:
|
finally:
|
||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to time out after .4 seconds when providing .4'
|
assert gotException , 'Expected to time out after .55 seconds when providing .55'
|
||||||
assert compareTimes(endTime, startTime, .4, 3, .05, None) == 0 , 'Expected providing .4 would allow timeout of up to .4 seconds'
|
assert compareTimes(endTime, startTime, .55, 3, deltaFixed=.15) == 0 , 'Expected providing .55 would allow timeout of up to .55 seconds'
|
||||||
|
|
||||||
threadsCleanedUp = False
|
threadsCleanedUp = False
|
||||||
|
|
||||||
@ -122,11 +122,11 @@ class TestBasic(object):
|
|||||||
threadsCleanedUp = True
|
threadsCleanedUp = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
assert threadsCleanedUp , 'Expected other threads to get cleaned up after gc collection'
|
assert threadsCleanedUp , 'Expected other threads to get cleaned up after gc collection'
|
||||||
|
|
||||||
def test_exception(self):
|
def test_exception(self):
|
||||||
sleepFunction = getSleepLambda(.5)
|
sleepFunction = getSleepLambda(.85)
|
||||||
|
|
||||||
expectedResult = 5 + 19
|
expectedResult = 5 + 19
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ class TestBasic(object):
|
|||||||
|
|
||||||
startTime = time.time()
|
startTime = time.time()
|
||||||
try:
|
try:
|
||||||
result = func_timeout(.3, sleepFunction, args=(5, 19))
|
result = func_timeout(.5, sleepFunction, args=(5, 19))
|
||||||
except FunctionTimedOut as fte:
|
except FunctionTimedOut as fte:
|
||||||
functionTimedOut = fte
|
functionTimedOut = fte
|
||||||
gotException = True
|
gotException = True
|
||||||
@ -144,14 +144,43 @@ class TestBasic(object):
|
|||||||
assert gotException , 'Expected to get exception'
|
assert gotException , 'Expected to get exception'
|
||||||
|
|
||||||
assert 'timed out after ' in functionTimedOut.msg , 'Expected message to be constructed. Got: %s' %(repr(functionTimedOut.msg), )
|
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 round(functionTimedOut.timedOutAfter, 1) == .5 , 'Expected timedOutAfter to equal timeout ( .5 ). Got: %s' %(str(round(functionTimedOut.timedOutAfter, 1)), )
|
||||||
assert functionTimedOut.timedOutFunction == sleepFunction , 'Expected timedOutFunction to equal sleepFunction'
|
assert functionTimedOut.timedOutFunction == sleepFunction , 'Expected timedOutFunction to equal sleepFunction'
|
||||||
assert functionTimedOut.timedOutArgs == (5, 19) , 'Expected args to equal (5, 19)'
|
assert functionTimedOut.timedOutArgs == (5, 19) , 'Expected args to equal (5, 19)'
|
||||||
assert functionTimedOut.timedOutKwargs == {} , 'Expected timedOutKwargs to equal {}'
|
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__':
|
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())
|
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())
|
||||||
|
|||||||
@ -19,7 +19,7 @@ from func_timeout import func_timeout, FunctionTimedOut, func_set_timeout
|
|||||||
|
|
||||||
from TestUtils import ARG_NO_DEFAULT, getSleepLambda, getSleepLambdaWithArgs, compareTimes
|
from TestUtils import ARG_NO_DEFAULT, getSleepLambda, getSleepLambdaWithArgs, compareTimes
|
||||||
|
|
||||||
SLEEP_TIME = .5
|
SLEEP_TIME = 1.25
|
||||||
|
|
||||||
def doSleep(a, b):
|
def doSleep(a, b):
|
||||||
time.sleep(a)
|
time.sleep(a)
|
||||||
@ -52,7 +52,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception at sleep time'
|
assert gotException , 'Expected to get exception at sleep time'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, .1) == 0 , 'Expected to sleep up to sleep time'
|
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep up to sleep time'
|
||||||
|
|
||||||
expected = SLEEP_TIME * .8 + 4
|
expected = SLEEP_TIME * .8 + 4
|
||||||
gotException = False
|
gotException = False
|
||||||
@ -64,7 +64,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert not gotException , 'Expected not to get exception at 80% sleep time'
|
assert not gotException , 'Expected not to get exception at 80% sleep time'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, .1) == 0 , 'Expected to only sleep for 80% of sleep time'
|
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, deltaFixed=.15) == 0 , 'Expected to only sleep for 80% of sleep time'
|
||||||
assert result == expected , 'Got wrong result'
|
assert result == expected , 'Got wrong result'
|
||||||
|
|
||||||
def test_funcSetTimeoutOverride(self):
|
def test_funcSetTimeoutOverride(self):
|
||||||
@ -85,7 +85,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception at 130% sleep time'
|
assert gotException , 'Expected to get exception at 130% sleep time'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME * 1.2, None, .1) == 0 , 'Expected to sleep up to 120% of sleep time'
|
assert compareTimes(endTime, startTime, SLEEP_TIME * 1.2, None, deltaFixed=.15) == 0 , 'Expected to sleep up to 120% of sleep time'
|
||||||
|
|
||||||
@func_set_timeout(SLEEP_TIME, allowOverride=False)
|
@func_set_timeout(SLEEP_TIME, allowOverride=False)
|
||||||
def doSleepFuncNoOverride(a, b):
|
def doSleepFuncNoOverride(a, b):
|
||||||
@ -113,7 +113,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert not gotException , 'Expected not to get exception with forced 115% sleep time'
|
assert not gotException , 'Expected not to get exception with forced 115% sleep time'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, .1) == 0 , 'Expected to sleep for SLEEP_TIME'
|
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME'
|
||||||
assert result == expected , 'Got wrong result'
|
assert result == expected , 'Got wrong result'
|
||||||
|
|
||||||
def test_funcSetTimeCalculate(self):
|
def test_funcSetTimeCalculate(self):
|
||||||
@ -145,7 +145,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert not gotException , 'Expected not to get exception with calculated 120% timeout on sleep time'
|
assert not gotException , 'Expected not to get exception with calculated 120% timeout on sleep time'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, .1) == 0 , 'Expected to sleep for SLEEP_TIME'
|
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME'
|
||||||
assert result == expected , 'Got wrong result'
|
assert result == expected , 'Got wrong result'
|
||||||
|
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception with calculated 80% timeout on sleep time'
|
assert gotException , 'Expected to get exception with calculated 80% timeout on sleep time'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, .1) == 0 , 'Expected to sleep for 80% SLEEP_TIME'
|
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, deltaFixed=.15) == 0 , 'Expected to sleep for 80% SLEEP_TIME'
|
||||||
|
|
||||||
@func_set_timeout(calculateSleepOverArgs, allowOverride=False)
|
@func_set_timeout(calculateSleepOverArgs, allowOverride=False)
|
||||||
def doSleepFuncOverArgs(a, b):
|
def doSleepFuncOverArgs(a, b):
|
||||||
@ -181,7 +181,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert not gotException , 'Expected not to get exception with calculated 120% timeout on sleep time using *args'
|
assert not gotException , 'Expected not to get exception with calculated 120% timeout on sleep time using *args'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, .1) == 0 , 'Expected to sleep for SLEEP_TIME using *args'
|
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME using *args'
|
||||||
assert result == expected , 'Got wrong result'
|
assert result == expected , 'Got wrong result'
|
||||||
|
|
||||||
@func_set_timeout(calculateSleepUnderArgs, allowOverride=False)
|
@func_set_timeout(calculateSleepUnderArgs, allowOverride=False)
|
||||||
@ -199,7 +199,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception with calculated 80% timeout on sleep time using *args'
|
assert gotException , 'Expected to get exception with calculated 80% timeout on sleep time using *args'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, .1) == 0 , 'Expected to sleep for 80% SLEEP_TIME using *args'
|
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, deltaFixed=.15) == 0 , 'Expected to sleep for 80% SLEEP_TIME using *args'
|
||||||
|
|
||||||
|
|
||||||
def test_funcSetTimeCalculateWithOverride(self):
|
def test_funcSetTimeCalculateWithOverride(self):
|
||||||
@ -231,7 +231,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert not gotException , 'Expected not to get exception with calculated 120% timeout on sleep time'
|
assert not gotException , 'Expected not to get exception with calculated 120% timeout on sleep time'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, .1) == 0 , 'Expected to sleep for SLEEP_TIME'
|
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME'
|
||||||
assert result == expected , 'Got wrong result'
|
assert result == expected , 'Got wrong result'
|
||||||
|
|
||||||
expected = SLEEP_TIME + 4
|
expected = SLEEP_TIME + 4
|
||||||
@ -244,7 +244,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert not gotException , 'Expected not to get exception with calculated 120% timeout on sleep time but 150% timeout on override'
|
assert not gotException , 'Expected not to get exception with calculated 120% timeout on sleep time but 150% timeout on override'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, .1) == 0 , 'Expected to sleep for SLEEP_TIME with 150% timeout on override'
|
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME with 150% timeout on override'
|
||||||
assert result == expected , 'Got wrong result'
|
assert result == expected , 'Got wrong result'
|
||||||
|
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception with calculated 120% timeout on sleep time but 70% timeout on override'
|
assert gotException , 'Expected to get exception with calculated 120% timeout on sleep time but 70% timeout on override'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME * .7, None, .1) == 0 , 'Expected to sleep for 70% SLEEP_TIME with 70% timeout on override'
|
assert compareTimes(endTime, startTime, SLEEP_TIME * .7, None, deltaFixed=.15) == 0 , 'Expected to sleep for 70% SLEEP_TIME with 70% timeout on override'
|
||||||
|
|
||||||
|
|
||||||
@func_set_timeout(calculateSleepUnder, allowOverride=True)
|
@func_set_timeout(calculateSleepUnder, allowOverride=True)
|
||||||
@ -276,7 +276,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception with calculated 80% timeout on sleep time'
|
assert gotException , 'Expected to get exception with calculated 80% timeout on sleep time'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, .1) == 0 , 'Expected to sleep for 80% SLEEP_TIME'
|
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, deltaFixed=.15) == 0 , 'Expected to sleep for 80% SLEEP_TIME'
|
||||||
|
|
||||||
expected = SLEEP_TIME + 4
|
expected = SLEEP_TIME + 4
|
||||||
gotException = False
|
gotException = False
|
||||||
@ -288,7 +288,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert not gotException , 'Expected not to get exception with calculated 80% timeout on sleep time but 120% timeout on override'
|
assert not gotException , 'Expected not to get exception with calculated 80% timeout on sleep time but 120% timeout on override'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME , None, .1) == 0 , 'Expected to sleep for SLEEP_TIME with 120% timeout on override'
|
assert compareTimes(endTime, startTime, SLEEP_TIME , None, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME with 120% timeout on override'
|
||||||
|
|
||||||
|
|
||||||
def test_setFuncTimeoutetry(self):
|
def test_setFuncTimeoutetry(self):
|
||||||
@ -322,7 +322,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception with calculated 80% timeout'
|
assert gotException , 'Expected to get exception with calculated 80% timeout'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, .1) == 0 , 'Expected to sleep for 80% SLEEP_TIME with 80% timeout'
|
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, deltaFixed=.15) == 0 , 'Expected to sleep for 80% SLEEP_TIME with 80% timeout'
|
||||||
|
|
||||||
gotException = False
|
gotException = False
|
||||||
startTime = time.time()
|
startTime = time.time()
|
||||||
@ -335,7 +335,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception with calculated same 80% timeout on retry'
|
assert gotException , 'Expected to get exception with calculated same 80% timeout on retry'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, .1) == 0 , 'Expected to sleep for 80% SLEEP_TIME with same 80% timeout on retry'
|
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, deltaFixed=.15) == 0 , 'Expected to sleep for 80% SLEEP_TIME with same 80% timeout on retry'
|
||||||
|
|
||||||
result = None
|
result = None
|
||||||
gotException = False
|
gotException = False
|
||||||
@ -349,7 +349,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert not gotException , 'Expected to get exception with calculated 80% timeout on retry ( None ) [ No timeout ]'
|
assert not gotException , 'Expected to get exception with calculated 80% timeout on retry ( None ) [ No timeout ]'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, .1) == 0 , 'Expected to sleep for 100% SLEEP_TIME with 80% timeout overriden on retry ( None ) [ No timeout ]'
|
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep for 100% SLEEP_TIME with 80% timeout overriden on retry ( None ) [ No timeout ]'
|
||||||
assert result == expected , 'Got wrong result'
|
assert result == expected , 'Got wrong result'
|
||||||
|
|
||||||
|
|
||||||
@ -365,7 +365,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert gotException , 'Expected to get exception with calculated 80% timeout overriden by 60% timeout on retry'
|
assert gotException , 'Expected to get exception with calculated 80% timeout overriden by 60% timeout on retry'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME * .6, None, .1) == 0 , 'Expected to sleep for 60% SLEEP_TIME with 80% timeout overriden on retry ( SLEEP_TIME * .6 ) [ 60% timeout ]'
|
assert compareTimes(endTime, startTime, SLEEP_TIME * .6, None, deltaFixed=.15) == 0 , 'Expected to sleep for 60% SLEEP_TIME with 80% timeout overriden on retry ( SLEEP_TIME * .6 ) [ 60% timeout ]'
|
||||||
|
|
||||||
result = None
|
result = None
|
||||||
gotException = False
|
gotException = False
|
||||||
@ -379,7 +379,7 @@ class TestDecorator(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert not gotException , 'Expected to get exception with calculated 80% timeout overriden by 150% timeout on retry'
|
assert not gotException , 'Expected to get exception with calculated 80% timeout overriden by 150% timeout on retry'
|
||||||
assert compareTimes(endTime, startTime, SLEEP_TIME , None, .1) == 0 , 'Expected to sleep for 100% SLEEP_TIME with 80% timeout overriden on retry ( SLEEP_TIME * 1.5 ) [ 150% timeout ]'
|
assert compareTimes(endTime, startTime, SLEEP_TIME , None, deltaFixed=.15) == 0 , 'Expected to sleep for 100% SLEEP_TIME with 80% timeout overriden on retry ( SLEEP_TIME * 1.5 ) [ 150% timeout ]'
|
||||||
assert result == expected
|
assert result == expected
|
||||||
|
|
||||||
threadsCleanedUp = False
|
threadsCleanedUp = False
|
||||||
|
|||||||
159
tests/FuncTimeoutTests/test_StoppableThread.py
Executable file
159
tests/FuncTimeoutTests/test_StoppableThread.py
Executable file
@ -0,0 +1,159 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim: set ts=4 sw=4 expandtab :
|
||||||
|
|
||||||
|
'''
|
||||||
|
Copyright (c) 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 copy
|
||||||
|
import gc
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from func_timeout import func_timeout, FunctionTimedOut, func_set_timeout
|
||||||
|
|
||||||
|
from TestUtils import ARG_NO_DEFAULT, getSleepLambda, getSleepLambdaWithArgs, compareTimes
|
||||||
|
|
||||||
|
class TestBasic(object):
|
||||||
|
'''
|
||||||
|
TestBasic - Perform tests using the basic func_timeout function
|
||||||
|
'''
|
||||||
|
|
||||||
|
def test_funcTimeout(self):
|
||||||
|
sleepFunction = getSleepLambda(1.25)
|
||||||
|
|
||||||
|
expectedResult = 5 + 13
|
||||||
|
|
||||||
|
startTime = time.time()
|
||||||
|
result = sleepFunction(5, 13)
|
||||||
|
endTime = time.time()
|
||||||
|
|
||||||
|
assert result == expectedResult , 'Did not get return from sleepFunction'
|
||||||
|
|
||||||
|
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),))
|
||||||
|
|
||||||
|
assert result == expectedResult , 'Got wrong return from func_timeout.\nGot: %s\nExpected: %s\n' %(repr(result), repr(expectedResult))
|
||||||
|
|
||||||
|
gotException = False
|
||||||
|
try:
|
||||||
|
result = func_timeout(1, sleepFunction, args=(5, 13))
|
||||||
|
except FunctionTimedOut as te:
|
||||||
|
gotException = True
|
||||||
|
|
||||||
|
assert gotException , 'Expected to get FunctionTimedOut exception for 1.25 sec function at 1s timeout'
|
||||||
|
|
||||||
|
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), ))
|
||||||
|
except Exception as e:
|
||||||
|
raise AssertionError('Got unknown exception mixing args and kwargs: < %s > %s' %(e.__class__.__name__, str(e)))
|
||||||
|
|
||||||
|
assert result == expectedResult , 'Got wrong result when mixing args and kwargs'
|
||||||
|
|
||||||
|
def test_retry(self):
|
||||||
|
sleepFunction = getSleepLambda(.5)
|
||||||
|
|
||||||
|
expectedResult = 5 + 19
|
||||||
|
|
||||||
|
gotException = False
|
||||||
|
functionTimedOut = None
|
||||||
|
|
||||||
|
startTime = time.time()
|
||||||
|
try:
|
||||||
|
result = func_timeout(.3, sleepFunction, args=(5, 19))
|
||||||
|
except FunctionTimedOut as fte:
|
||||||
|
functionTimedOut = fte
|
||||||
|
gotException = True
|
||||||
|
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))
|
||||||
|
|
||||||
|
gotException = False
|
||||||
|
startTime = time.time()
|
||||||
|
try:
|
||||||
|
result = functionTimedOut.retry()
|
||||||
|
except FunctionTimedOut:
|
||||||
|
gotException = True
|
||||||
|
endTime = time.time()
|
||||||
|
|
||||||
|
assert gotException , 'Expected to get exception on retry.'
|
||||||
|
assert compareTimes(endTime, startTime, .3, 3, .1, None) == 0 , 'Expected retry with no arguments to use same timeout of .3'
|
||||||
|
|
||||||
|
gotException = False
|
||||||
|
startTime = time.time()
|
||||||
|
try:
|
||||||
|
result = functionTimedOut.retry(None)
|
||||||
|
except FunctionTimedOut:
|
||||||
|
gotException = True
|
||||||
|
endTime = time.time()
|
||||||
|
|
||||||
|
assert not gotException , 'Did NOT to get exception with no timeout'
|
||||||
|
assert compareTimes(endTime, startTime, .5, 3, .1, None) == 0 , 'Expected retry with None as timeout to last full length of function'
|
||||||
|
|
||||||
|
gotException = False
|
||||||
|
startTime = time.time()
|
||||||
|
try:
|
||||||
|
result = functionTimedOut.retry(.4)
|
||||||
|
except FunctionTimedOut:
|
||||||
|
gotException = True
|
||||||
|
finally:
|
||||||
|
endTime = time.time()
|
||||||
|
|
||||||
|
assert gotException , 'Expected to time out after .5 seconds when providing .5'
|
||||||
|
assert compareTimes(endTime, startTime, .5, 3, .1, None) == 0 , 'Expected providing .5 would allow timeout of up to .5 seconds'
|
||||||
|
|
||||||
|
threadsCleanedUp = False
|
||||||
|
|
||||||
|
for i in range(5):
|
||||||
|
time.sleep(1)
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
if threading.active_count() == 1:
|
||||||
|
threadsCleanedUp = True
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
assert threadsCleanedUp , 'Expected other threads to get cleaned up after gc collection'
|
||||||
|
|
||||||
|
def test_exception(self):
|
||||||
|
sleepFunction = getSleepLambda(.5)
|
||||||
|
|
||||||
|
expectedResult = 5 + 19
|
||||||
|
|
||||||
|
gotException = False
|
||||||
|
functionTimedOut = None
|
||||||
|
|
||||||
|
startTime = time.time()
|
||||||
|
try:
|
||||||
|
result = func_timeout(.3, sleepFunction, args=(5, 19))
|
||||||
|
except FunctionTimedOut as fte:
|
||||||
|
functionTimedOut = fte
|
||||||
|
gotException = True
|
||||||
|
endTime = time.time()
|
||||||
|
|
||||||
|
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 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 {}'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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())
|
||||||
|
|
||||||
|
# vim: set ts=4 sw=4 expandtab :
|
||||||
@ -35,7 +35,7 @@ class TestBasicSleepWithArgs(object):
|
|||||||
raise AssertionError('Expected to have 1 default arg and 2 standard. Tried 3 args')
|
raise AssertionError('Expected to have 1 default arg and 2 standard. Tried 3 args')
|
||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert compareTimes(endTime, startTime, 2, 2, deltaFixed=.1, deltaPct=None) == 0 , 'Expected getSleepLambdaWithArgs(2) to take 2 seconds.'
|
assert compareTimes(endTime, startTime, 2, 2, deltaFixed=.15, deltaPct=None) == 0 , 'Expected getSleepLambdaWithArgs(2) to take 2 seconds.'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sleepLambda(4, 7, 12)
|
sleepLambda(4, 7, 12)
|
||||||
@ -54,7 +54,7 @@ class TestBasicSleepWithArgs(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert result == expectedResult , 'Got wrong result'
|
assert result == expectedResult , 'Got wrong result'
|
||||||
assert compareTimes(endTime, startTime, 1.75, 2, deltaFixed=.1, deltaPct=None) == 0 , 'Expected getSleepLambdaWithArgs(1.75) to take 1.75 seconds.'
|
assert compareTimes(endTime, startTime, 1.75, 2, deltaFixed=.15, deltaPct=None) == 0 , 'Expected getSleepLambdaWithArgs(1.75) to take 1.75 seconds.'
|
||||||
|
|
||||||
expectedResult = 5 + 13
|
expectedResult = 5 + 13
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ class TestBasicSleepWithArgs(object):
|
|||||||
endTime = time.time()
|
endTime = time.time()
|
||||||
|
|
||||||
assert result == expectedResult , 'Did not get return from sleepFunction'
|
assert result == expectedResult , 'Did not get return from sleepFunction'
|
||||||
assert compareTimes(endTime, startTime, 1.75, 2, deltaFixed=.1, deltaPct=None) == 0 , 'Expected getSleepLambda(1.75) to take 1.75 seconds.'
|
assert compareTimes(endTime, startTime, 1.75, 2, deltaFixed=.15, deltaPct=None) == 0 , 'Expected getSleepLambda(1.75) to take 1.75 seconds.'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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())
|
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())
|
||||||
|
|||||||
@ -12,7 +12,62 @@
|
|||||||
# NOTE: Since version 1.2.3, you can also import this (like from a graphical application) and call the "main()" function.
|
# NOTE: Since version 1.2.3, you can also import this (like from a graphical application) and call the "main()" function.
|
||||||
# All of the following globals are the defaults, but can be overridden when calling main() (params have the same name as the globals).
|
# All of the following globals are the defaults, but can be overridden when calling main() (params have the same name as the globals).
|
||||||
|
|
||||||
import imp
|
# Assign a local function, "find_mod" to the interface to search
|
||||||
|
# PYTHONPATH for importable module
|
||||||
|
try:
|
||||||
|
# imp.find_module has been deprecated as of python 3.7, so
|
||||||
|
# prefer some alternate/newer interfaces first.
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
try:
|
||||||
|
# If we have the newest and therefore least-deprecated
|
||||||
|
# way, use it.
|
||||||
|
_findModSpec = importlib.util.find_spec
|
||||||
|
def find_mod(modName):
|
||||||
|
'''
|
||||||
|
find_mod - Find a module by name.
|
||||||
|
|
||||||
|
Similar to import #modName but only finds importable module,
|
||||||
|
does not actually import.
|
||||||
|
|
||||||
|
@raises ImportError on failure
|
||||||
|
'''
|
||||||
|
modSpec = _findModSpec(modName)
|
||||||
|
if not modSpec:
|
||||||
|
# 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, ))
|
||||||
|
|
||||||
|
return modSpec
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
# We have importlib, but don't have importlib.util.find_spec
|
||||||
|
|
||||||
|
# We could use importlib.import_module which is present in
|
||||||
|
# python 2.7, but that changes behaviour by actually
|
||||||
|
# importing (and thus additionally checking syntax/other).
|
||||||
|
#
|
||||||
|
# So just fall back to the old imp.find_module in this case
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Clean up namespace
|
||||||
|
del importlib
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
# Fall back to imp.find_module implementation below
|
||||||
|
raise ImportError('importlib but no importlib.util')
|
||||||
|
#find_mod = lambda modName : importlib.import_module(modName)
|
||||||
|
|
||||||
|
except:
|
||||||
|
# importlib is not present or has an unknown/dated interface,
|
||||||
|
# so fallback to the deprecated but oldest form
|
||||||
|
import imp
|
||||||
|
|
||||||
|
# Use a lambda to ensure only one arg is passed as that is
|
||||||
|
# our standard interface
|
||||||
|
find_mod = lambda modName : imp.find_module(modName)
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -32,8 +87,8 @@ ALLOW_SITE_INSTALL = False
|
|||||||
# This is the test directory that should contain all your tests. This should be a directory in your "tests" folder
|
# This is the test directory that should contain all your tests. This should be a directory in your "tests" folder
|
||||||
MY_TEST_DIRECTORY = 'FuncTimeoutTests'
|
MY_TEST_DIRECTORY = 'FuncTimeoutTests'
|
||||||
|
|
||||||
__version__ = '2.1.1'
|
__version__ = '3.0.5'
|
||||||
__version_tuple__ = (2, 1, 1)
|
__version_tuple__ = (3, 0, 5)
|
||||||
|
|
||||||
def findGoodTests():
|
def findGoodTests():
|
||||||
'''
|
'''
|
||||||
@ -65,8 +120,83 @@ def findGoodTests():
|
|||||||
"success" : success
|
"success" : success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def findExecutable(execName):
|
||||||
|
'''
|
||||||
|
findExecutable - Search PATH for an executable
|
||||||
|
|
||||||
|
@return <dict> {
|
||||||
|
'path' <str> -> Path to executable (if found, see "success")
|
||||||
|
'success' <bool> -> True/False if we successfully found requested executable
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
pathSplit = os.environ['PATH'].split(':')
|
||||||
|
if '.' not in pathSplit:
|
||||||
|
pathSplit = ['.'] + pathSplit
|
||||||
|
os.environ['PATH'] = ':'.join(pathSplit)
|
||||||
|
|
||||||
|
result = ''
|
||||||
|
success = False
|
||||||
|
for path in pathSplit:
|
||||||
|
if path.endswith(os.sep):
|
||||||
|
path = path[:-1]
|
||||||
|
guess = path + os.sep + execName
|
||||||
|
if os.path.exists(guess):
|
||||||
|
success = True
|
||||||
|
result = guess
|
||||||
|
break
|
||||||
|
|
||||||
|
return {
|
||||||
|
"path" : result,
|
||||||
|
"success" : success
|
||||||
|
}
|
||||||
|
|
||||||
|
def findGoodTests():
|
||||||
|
return findExecutable('GoodTests.py')
|
||||||
|
|
||||||
|
|
||||||
def try_pip_install():
|
def try_pip_install():
|
||||||
pipe = subprocess.Popen('pip install GoodTests', shell=True)
|
'''
|
||||||
|
try to pip install GoodTests.py
|
||||||
|
|
||||||
|
First, try via pip module.
|
||||||
|
|
||||||
|
If that fails, try to locate pip by dirname(current python executable) + os.sep + pip
|
||||||
|
If that does not exist, scan PATH for pip
|
||||||
|
|
||||||
|
If found a valid pip executable, invoke it to install GoodTests
|
||||||
|
otherwise, fail.
|
||||||
|
'''
|
||||||
|
|
||||||
|
didImport = False
|
||||||
|
try:
|
||||||
|
import pip
|
||||||
|
didImport = True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if didImport is True:
|
||||||
|
print ( "Found pip as module=pip")
|
||||||
|
res = pip.main(['install', 'GoodTests'])
|
||||||
|
if res == 0:
|
||||||
|
return 0
|
||||||
|
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, ) )
|
||||||
|
if not os.path.exists(pipPath):
|
||||||
|
print ( '"%s" does not exist. Scanning PATH to locate a usable pip executable' %(pipPath, ))
|
||||||
|
pipPath = None
|
||||||
|
searchResults = findExecutable('pip')
|
||||||
|
if not searchResults['success']:
|
||||||
|
sys.stderr.write('Failed to find a usable pip executable in PATH.\n')
|
||||||
|
return 1 # Failed to locate a usable pip
|
||||||
|
|
||||||
|
pipPath = searchResults['path']
|
||||||
|
|
||||||
|
print ( 'Found pip executable at "%s"' %(pipPath, ) )
|
||||||
|
print ( "Executing: %s %s 'install' 'GoodTests'" %(sys.executable, pipPath) )
|
||||||
|
pipe = subprocess.Popen([sys.executable, pipPath, 'install', 'GoodTests'], shell=False, env=os.environ)
|
||||||
res = pipe.wait()
|
res = pipe.wait()
|
||||||
|
|
||||||
return res
|
return res
|
||||||
@ -195,7 +325,7 @@ def main(thisDir=None, additionalArgs=[], MY_PACKAGE_MODULE=None, ALLOW_SITE_INS
|
|||||||
elif dirName == '':
|
elif dirName == '':
|
||||||
inCurrentDir = False
|
inCurrentDir = False
|
||||||
try:
|
try:
|
||||||
imp.find_module(MY_PACKAGE_MODULE)
|
find_mod(MY_PACKAGE_MODULE)
|
||||||
inCurrentDir = True
|
inCurrentDir = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# COMPAT WITH PREVIOUS runTests.py: Try plain module in parent directory
|
# COMPAT WITH PREVIOUS runTests.py: Try plain module in parent directory
|
||||||
@ -203,7 +333,7 @@ def main(thisDir=None, additionalArgs=[], MY_PACKAGE_MODULE=None, ALLOW_SITE_INS
|
|||||||
oldSysPath = sys.path[:]
|
oldSysPath = sys.path[:]
|
||||||
sys.path = [os.path.realpath(os.getcwd() + os.sep + '..' + os.sep)]
|
sys.path = [os.path.realpath(os.getcwd() + os.sep + '..' + os.sep)]
|
||||||
try:
|
try:
|
||||||
imp.find_module(MY_PACKAGE_MODULE)
|
find_mod(MY_PACKAGE_MODULE)
|
||||||
foundIt = True
|
foundIt = True
|
||||||
sys.path = oldSysPath
|
sys.path = oldSysPath
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
@ -234,8 +364,15 @@ def main(thisDir=None, additionalArgs=[], MY_PACKAGE_MODULE=None, ALLOW_SITE_INS
|
|||||||
if baseName.endswith(('.py', '.pyc', '.pyo')):
|
if baseName.endswith(('.py', '.pyc', '.pyo')):
|
||||||
MY_PACKAGE_MODULE = baseName[ : baseName.rindex('.')]
|
MY_PACKAGE_MODULE = baseName[ : baseName.rindex('.')]
|
||||||
|
|
||||||
if e.name != MY_PACKAGE_MODULE:
|
try:
|
||||||
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' %(e.name, str(e), e.name))
|
eName = e.name
|
||||||
|
except AttributeError as noNameE:
|
||||||
|
# Some platforms python2 does not have this attribute
|
||||||
|
# so pull it from the message
|
||||||
|
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))
|
||||||
return 1
|
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('Could not import %s. Either install it or otherwise add to PYTHONPATH\n%s\n' %(MY_PACKAGE_MODULE, str(e)))
|
||||||
return 1
|
return 1
|
||||||
@ -253,8 +390,7 @@ def main(thisDir=None, additionalArgs=[], MY_PACKAGE_MODULE=None, ALLOW_SITE_INS
|
|||||||
|
|
||||||
|
|
||||||
didTerminate = False
|
didTerminate = False
|
||||||
|
pipe = subprocess.Popen([sys.executable, goodTestsInfo['path']] + additionalArgs + [MY_TEST_DIRECTORY], env=os.environ, shell=False)
|
||||||
pipe = subprocess.Popen([goodTestsInfo['path']] + additionalArgs + [MY_TEST_DIRECTORY], env=os.environ, shell=False)
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
pipe.wait()
|
pipe.wait()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user