Compare commits
92 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 | ||
|
|
515988d9fc | ||
|
|
ac5eb257b0 | ||
|
|
45244ce98e | ||
|
|
549251b2a3 | ||
|
|
cdd2d6d34f | ||
|
|
5ab59fd709 | ||
|
|
c52d7181d6 | ||
|
|
874bc9b576 | ||
|
|
92dc986332 | ||
|
|
dc5fcfe507 | ||
|
|
f7f761789c | ||
|
|
f793e36dc6 | ||
|
|
089172fe0c | ||
|
|
b427da2517 | ||
|
|
00685ec546 | ||
|
|
2bd33c12fd | ||
|
|
5e15db92b6 | ||
|
|
e99d1d8957 | ||
|
|
0e43f0b161 | ||
|
|
f21d4b4b49 | ||
|
|
6bee825db7 | ||
|
|
19497ca229 | ||
|
|
4190710ec4 | ||
|
|
57aed88c7a | ||
|
|
3f6d300aa0 | ||
|
|
0e2b0e9c3e | ||
|
|
5e44de7210 | ||
|
|
9e1ac2fb8c | ||
|
|
76cf4f6456 | ||
|
|
fd0260fc05 | ||
|
|
3275780482 | ||
|
|
5a6187122d | ||
|
|
3dcf6f921e | ||
|
|
38ec3de098 | ||
|
|
0c1f6a7de6 | ||
|
|
b6c02653b8 | ||
|
|
cfa466a302 | ||
|
|
ca8a3a3534 | ||
|
|
8af967169d | ||
|
|
f20e8e727c | ||
|
|
525c291aef | ||
|
|
d6ff5e1932 | ||
|
|
e554cd0252 | ||
|
|
d10476b734 | ||
|
|
63454c7178 | ||
|
|
9d3eec0475 | ||
|
|
e4cf7fa765 | ||
|
|
ad944d3c2c | ||
|
|
b729265855 | ||
|
|
75d90251f6 | ||
|
|
2079932b4e | ||
|
|
0ccca87e2c | ||
|
|
36b7d6f266 | ||
|
|
5b93fe5d7c | ||
|
|
c3a114e1df | ||
|
|
b1a05fe92f | ||
|
|
32078a8065 | ||
|
|
84a3abd11d | ||
|
|
036c1dfa6f | ||
|
|
b778c644c3 | ||
|
|
1a22f0f55b | ||
|
|
906f2b9028 | ||
|
|
1f4e536784 | ||
|
|
f7baa18e64 | ||
|
|
f8ec7f492c | ||
|
|
3f4bae15c1 |
90
ChangeLog
90
ChangeLog
@ -1,3 +1,93 @@
|
||||
|
||||
* 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
|
||||
|
||||
- Update docs, update README, note that this still works with Python 3.7
|
||||
|
||||
- Update link to pydocs to be on githubpreview, as python hosted is broke right now.
|
||||
|
||||
|
||||
* 4.3.0 - Aug 10 2017
|
||||
|
||||
- Ensure that functions wrapped with @func_set_timeout do not lose meta
|
||||
information (such as name, docstrings, etc.). Special thanks to belongwqz on
|
||||
github for noting the issue and suggesting functools.wraps
|
||||
|
||||
|
||||
* 4.2.0 - Jun 4 2017
|
||||
- Add "stop" method to StoppableThread (same as previous _stopThread method -
|
||||
but with a 'public' name)
|
||||
- Document StoppableThread a lot more
|
||||
- Add "raiseEvery" to StoppableThread.stop to control the "repeatEvery" arg to
|
||||
JoinThread (how often after the first raise the exception is retried)
|
||||
|
||||
* 4.1.0 - May 24 2017
|
||||
|
||||
- If func_timeout completes the function call in the alloted time, explicitly
|
||||
clean up the thread (previously we were only cleaning it up in the time-out
|
||||
path). On python3 it would get cleaned up anyway on the next garbage
|
||||
collection cycle, on python2 it's a bit more finnikey (but will also,
|
||||
eventually, get automatically joined)
|
||||
|
||||
- Be better at the traceback. Exclude the funcwrap from the call, and ensure
|
||||
the raised Exception follows the actual path of execution (can be confusing to
|
||||
read at first glance, but your function is at the bottom, then goes around a
|
||||
bit.)
|
||||
|
||||
This uses PEP409/3134 in Python3 (available in 3.3+, we support 3.4+
|
||||
officially).
|
||||
|
||||
Python2 doesn't have this, but has it's own syntax that doesn't work in
|
||||
python3 to allow throwing a custom __traceback__.
|
||||
|
||||
You'd think both would support both, or at least python3 support both...
|
||||
|
||||
Anyway, this causes a kinda silly import-hack and adds an extra frame to the
|
||||
exception handling part, but works to prevent default handlers from picking it
|
||||
up.
|
||||
|
||||
This solves the multiple prints of exceptions on python3 in certain
|
||||
circumstances(you catching and then raising an exception)
|
||||
|
||||
|
||||
* 4.0.0 - May 20 2017
|
||||
|
||||
- Fixup short delay after a thread times out
|
||||
|
||||
- Fix where in some cases (depending on what was execution when abort was
|
||||
|
||||
called), a default handler could print the tracback to stderr outside of the
|
||||
scope of the normal raise (it would still be raised, just handled twice).
|
||||
|
||||
- Add a function decorator which provides a default timeout, allows that
|
||||
timeout to be overriden by adding a "forceTimeout" kwarg to the function
|
||||
(configurable), or even running a function to calculate the timeout based on
|
||||
args for each call
|
||||
|
||||
- Some improvements to python2 support
|
||||
|
||||
- Add GoodTests (Unit tests)
|
||||
|
||||
* 3.1.0 - Jan 4 2017
|
||||
- Make windows compatible
|
||||
- Add simple testcase, "testit.py"
|
||||
|
||||
@ -7,3 +7,6 @@ include ChangeLog
|
||||
include testit.py
|
||||
recursive-include func_timeout *.py
|
||||
recursive-include doc *.html
|
||||
include tests/runTests.py
|
||||
include tests/README
|
||||
recursive-include tests/FuncTimeoutTests *.py
|
||||
|
||||
188
README.md
188
README.md
@ -2,10 +2,12 @@
|
||||
Python module to support running any existing function with a given timeout.
|
||||
|
||||
|
||||
Package Includes
|
||||
----------------
|
||||
Function Timeout
|
||||
================
|
||||
|
||||
**func\_timeout**
|
||||
|
||||
func\_timeout
|
||||
-------------
|
||||
|
||||
This is the function wherein you pass the timeout, the function you want to call, and any arguments, and it runs it for up to #timeout# seconds, and will return/raise anything the passed function would otherwise return or raise.
|
||||
|
||||
@ -25,13 +27,10 @@ This is the function wherein you pass the timeout, the function you want to call
|
||||
@return - The return value that #func# gives
|
||||
'''
|
||||
|
||||
**FunctionTimedOut**
|
||||
|
||||
Exception raised if the function times out
|
||||
**Example**
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
So, for esxample, if you have a function "doit('arg1', 'arg2')" that you want to limit to running for 5 seconds, with func\_timeout you can call it like this:
|
||||
|
||||
|
||||
@ -49,6 +48,41 @@ So, for esxample, if you have a function "doit('arg1', 'arg2')" that you want to
|
||||
# Handle any exceptions that doit might raise here
|
||||
|
||||
|
||||
|
||||
func\_set\_timeout
|
||||
------------------
|
||||
|
||||
|
||||
This is a decorator you can use on functions to apply func\_timeout.
|
||||
|
||||
Takes two arguments, "timeout" and "allowOverride"
|
||||
|
||||
If "allowOverride" is present, an optional keyword argument is added to the wrapped function, 'forceTimeout'. When provided, this will override the timeout used on this function.
|
||||
|
||||
|
||||
The "timeout" parameter can be either a number (for a fixed timeout), or a function/lambda. If a function/lambda is used, it will be passed the same arguments as the called function was passed. It should return a number which will be used as the timeout for that paticular run. For example, if you have a method that calculates data, you'll want a higher timeout for 1 million records than 50 records.
|
||||
|
||||
|
||||
**Example:**
|
||||
|
||||
@func_set_timeout(2.5)
|
||||
def myFunction(self, arg1, arg2):
|
||||
...
|
||||
|
||||
|
||||
FunctionTimedOut
|
||||
----------------
|
||||
|
||||
Exception raised if the function times out.
|
||||
|
||||
|
||||
Has a "retry" method which takes the following arguments:
|
||||
|
||||
* No argument - Retry same args, same function, same timeout
|
||||
* Number argument - Retry same args, same function, provided timeout
|
||||
* None - Retry same args, same function, no timeout
|
||||
|
||||
|
||||
How it works
|
||||
------------
|
||||
|
||||
@ -57,10 +91,144 @@ If there is a return or an exception raised, it will be returned/raised as norma
|
||||
|
||||
If the timeout has exceeded, the "FunctionTimedOut" exception will be raised in the context of the function being called, as well as from the context of "func\_timeout". You should have your function catch the "FunctionTimedOut" exception and exit cleanly if possible. Every 2 seconds until your function is terminated, it will continue to raise FunctionTimedOut. The terminating of the timed-out function happens in the context of the thread and will not block main execution.
|
||||
|
||||
Support
|
||||
-------
|
||||
|
||||
I've tested func\_timeout with python 2.7, 3.4, and 3.5. It should work on other versions as well.
|
||||
StoppableThread
|
||||
===============
|
||||
|
||||
StoppableThread is a subclass of threading.Thread, which supports stopping the thread (supports both python2 and python3). It will work to stop even in C code.
|
||||
|
||||
The way it works is that you pass it an exception, and it raises it via the cpython api (So the next time a "python" function is called from C api, or the next line is processed in python code, the exception is raised).
|
||||
|
||||
|
||||
Using StoppableThread
|
||||
---------------------
|
||||
|
||||
You can use StoppableThread one of two ways:
|
||||
|
||||
**As a Parent Class**
|
||||
|
||||
|
||||
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
|
||||
-----------------
|
||||
|
||||
|
||||
The *StoppableThread* class (you must extend this for your thread) adds a function, *stop*, which can be called to stop the thread.
|
||||
|
||||
|
||||
def stop(self, exception, raiseEvery=2.0):
|
||||
'''
|
||||
Stops the thread by raising a given exception.
|
||||
|
||||
@param exception <Exception type> - Exception to throw. Likely, you want to use something
|
||||
|
||||
that inherits from BaseException (so except Exception as e: continue; isn't a problem)
|
||||
|
||||
This should be a class/type, NOT an instance, i.e. MyExceptionType not MyExceptionType()
|
||||
|
||||
|
||||
@param raiseEvery <float> Default 2.0 - We will keep raising this exception every #raiseEvery seconds,
|
||||
|
||||
until the thread terminates.
|
||||
|
||||
If your code traps a specific exception type, this will allow you #raiseEvery seconds to cleanup before exit.
|
||||
|
||||
If you're calling third-party code you can't control, which catches BaseException, set this to a low number
|
||||
|
||||
to break out of their exception handler.
|
||||
|
||||
|
||||
@return <None>
|
||||
'''
|
||||
|
||||
|
||||
The "exception" param must be a type, and it must be instantiable with no arguments (i.e. MyExceptionType() must create the object).
|
||||
|
||||
Consider using a custom exception type which extends BaseException, which you can then use to do basic cleanup ( flush any open files, etc. ).
|
||||
|
||||
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.
|
||||
|
||||
For example:
|
||||
|
||||
class ServerShutdownExceptionType(BaseException):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
BaseException.__init__(self, 'Server is shutting down')
|
||||
|
||||
|
||||
This will force 'Server is shutting down' as the message held by this exception.
|
||||
|
||||
|
||||
|
||||
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.5 .
|
||||
|
||||
|
||||
Support
|
||||
=======
|
||||
|
||||
I've tested func\_timeout with python 2.7, 3.4, 3.5, 3.6, 3.7. It should work on other versions as well.
|
||||
|
||||
Works on windows, linux/unix, cygwin, mac
|
||||
|
||||
|
||||
239
README.rst
239
README.rst
@ -1,87 +1,262 @@
|
||||
func_timeout
|
||||
func\_timeout
|
||||
=============
|
||||
|
||||
Python module to support running any existing function with a given timeout.
|
||||
|
||||
|
||||
Package Includes
|
||||
----------------
|
||||
Function Timeout
|
||||
================
|
||||
|
||||
**func_timeout**
|
||||
|
||||
func\_timeout
|
||||
-------------
|
||||
|
||||
This is the function wherein you pass the timeout, the function you want to call, and any arguments, and it runs it for up to #timeout# seconds, and will return/raise anything the passed function would otherwise return or raise.
|
||||
|
||||
def func_timeout(timeout, func, args=(), kwargs=None):
|
||||
def func\_timeout(timeout, func, args=(), kwargs=None):
|
||||
|
||||
'''
|
||||
|
||||
func_timeout - Runs the given function for up to #timeout# seconds.
|
||||
|
||||
func\_timeout \- Runs the given function for up to #timeout# seconds.
|
||||
|
||||
Raises any exceptions #func# would raise, returns what #func# would return (unless timeout is exceeded), in which case it raises FunctionTimedOut
|
||||
|
||||
@param timeout <float> \- Maximum number of seconds to run #func# before terminating
|
||||
|
||||
@param timeout <float> - Maximum number of seconds to run #func# before terminating
|
||||
@param func <function> \- The function to call
|
||||
|
||||
@param func <function> - The function to call
|
||||
@param args <tuple> \- Any ordered arguments to pass to the function
|
||||
|
||||
@param args <tuple> - Any ordered arguments to pass to the function
|
||||
@param kwargs <dict/None> \- Keyword arguments to pass to the function.
|
||||
|
||||
@param kwargs <dict/None> - Keyword arguments to pass to the function.
|
||||
@raises \- FunctionTimedOut if #timeout# is exceeded, otherwise anything #func# could raise will be raised
|
||||
|
||||
|
||||
@raises - FunctionTimedOut if #timeout# is exceeded, otherwise anything #func# could raise will be raised
|
||||
|
||||
|
||||
@return - The return value that #func# gives
|
||||
@return \- The return value that #func# gives
|
||||
|
||||
'''
|
||||
|
||||
**FunctionTimedOut**
|
||||
|
||||
Exception raised if the function times out
|
||||
**Example**
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
So, for esxample, if you have a function "doit('arg1', 'arg2')" that you want to limit to running for 5 seconds, with func_timeout you can call it like this:
|
||||
So, for esxample, if you have a function "doit('arg1', 'arg2')" that you want to limit to running for 5 seconds, with func\_timeout you can call it like this:
|
||||
|
||||
|
||||
from func_timeout import func_timeout, FunctionTimedOut
|
||||
|
||||
from func\_timeout import func\_timeout, FunctionTimedOut
|
||||
|
||||
...
|
||||
|
||||
|
||||
try:
|
||||
|
||||
|
||||
doitReturnValue = func_timeout(5, doit, args=('arg1', 'arg2'))
|
||||
|
||||
doitReturnValue = func\_timeout(5, doit, args=('arg1', 'arg2'))
|
||||
|
||||
except FunctionTimedOut:
|
||||
|
||||
print ( "doit('arg1', 'arg2') could not complete within 5 seconds and was terminated.\n")
|
||||
print ( "doit('arg1', 'arg2') could not complete within 5 seconds and was terminated.\\n")
|
||||
|
||||
except Exception as e:
|
||||
|
||||
# Handle any exceptions that doit might raise here
|
||||
|
||||
|
||||
|
||||
func\_set\_timeout
|
||||
------------------
|
||||
|
||||
|
||||
This is a decorator you can use on functions to apply func\_timeout.
|
||||
|
||||
Takes two arguments, "timeout" and "allowOverride"
|
||||
|
||||
If "allowOverride" is present, an optional keyword argument is added to the wrapped function, 'forceTimeout'. When provided, this will override the timeout used on this function.
|
||||
|
||||
|
||||
The "timeout" parameter can be either a number (for a fixed timeout), or a function/lambda. If a function/lambda is used, it will be passed the same arguments as the called function was passed. It should return a number which will be used as the timeout for that paticular run. For example, if you have a method that calculates data, you'll want a higher timeout for 1 million records than 50 records.
|
||||
|
||||
|
||||
**Example:**
|
||||
|
||||
@func\_set\_timeout(2.5)
|
||||
|
||||
def myFunction(self, arg1, arg2):
|
||||
|
||||
...
|
||||
|
||||
|
||||
FunctionTimedOut
|
||||
----------------
|
||||
|
||||
Exception raised if the function times out.
|
||||
|
||||
|
||||
Has a "retry" method which takes the following arguments:
|
||||
|
||||
\* No argument \- Retry same args, same function, same timeout
|
||||
|
||||
\* Number argument \- Retry same args, same function, provided timeout
|
||||
|
||||
\* None \- Retry same args, same function, no timeout
|
||||
|
||||
|
||||
How it works
|
||||
------------
|
||||
|
||||
func_timeout will run the specified function in a thread with the specified arguments until it returns, raises an exception, or the timeout is exceeded.
|
||||
func\_timeout will run the specified function in a thread with the specified arguments until it returns, raises an exception, or the timeout is exceeded.
|
||||
|
||||
If there is a return or an exception raised, it will be returned/raised as normal.
|
||||
|
||||
If the timeout has exceeded, the "FunctionTimedOut" exception will be raised in the context of the function being called, as well as from the context of "func_timeout". You should have your function catch the "FunctionTimedOut" exception and exit cleanly if possible. Every 2 seconds until your function is terminated, it will continue to raise FunctionTimedOut. The terminating of the timed-out function happens in the context of the thread and will not block main execution.
|
||||
If the timeout has exceeded, the "FunctionTimedOut" exception will be raised in the context of the function being called, as well as from the context of "func\_timeout". You should have your function catch the "FunctionTimedOut" exception and exit cleanly if possible. Every 2 seconds until your function is terminated, it will continue to raise FunctionTimedOut. The terminating of the timed-out function happens in the context of the thread and will not block main execution.
|
||||
|
||||
|
||||
StoppableThread
|
||||
===============
|
||||
|
||||
StoppableThread is a subclass of threading.Thread, which supports stopping the thread (supports both python2 and python3). It will work to stop even in C code.
|
||||
|
||||
The way it works is that you pass it an exception, and it raises it via the cpython api (So the next time a "python" function is called from C api, or the next line is processed in python code, the exception is raised).
|
||||
|
||||
|
||||
Using StoppableThread
|
||||
---------------------
|
||||
|
||||
You can use StoppableThread one of two ways:
|
||||
|
||||
**As a Parent Class**
|
||||
|
||||
|
||||
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
|
||||
-----------------
|
||||
|
||||
|
||||
The *StoppableThread* class (you must extend this for your thread) adds a function, *stop*, which can be called to stop the thread.
|
||||
|
||||
|
||||
def stop(self, exception, raiseEvery=2.0):
|
||||
|
||||
'''
|
||||
|
||||
Stops the thread by raising a given exception.
|
||||
|
||||
@param exception <Exception type> \- Exception to throw. Likely, you want to use something
|
||||
|
||||
that inherits from BaseException (so except Exception as e: continue; isn't a problem)
|
||||
|
||||
This should be a class/type, NOT an instance, i.e. MyExceptionType not MyExceptionType()
|
||||
|
||||
|
||||
@param raiseEvery <float> Default 2.0 \- We will keep raising this exception every #raiseEvery seconds,
|
||||
|
||||
until the thread terminates.
|
||||
|
||||
If your code traps a specific exception type, this will allow you #raiseEvery seconds to cleanup before exit.
|
||||
|
||||
If you're calling third\-party code you can't control, which catches BaseException, set this to a low number
|
||||
|
||||
to break out of their exception handler.
|
||||
|
||||
|
||||
@return <None>
|
||||
|
||||
'''
|
||||
|
||||
|
||||
The "exception" param must be a type, and it must be instantiable with no arguments (i.e. MyExceptionType() must create the object).
|
||||
|
||||
Consider using a custom exception type which extends BaseException, which you can then use to do basic cleanup ( flush any open files, etc. ).
|
||||
|
||||
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.
|
||||
|
||||
For example:
|
||||
|
||||
class ServerShutdownExceptionType(BaseException):
|
||||
|
||||
def \_\_init\_\_(self, \*args, \*\*kwargs):
|
||||
|
||||
BaseException.\_\_init\_\_(self, 'Server is shutting down')
|
||||
|
||||
|
||||
This will force 'Server is shutting down' as the message held by this exception.
|
||||
|
||||
|
||||
|
||||
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.5 .
|
||||
|
||||
|
||||
Support
|
||||
-------
|
||||
=======
|
||||
|
||||
I've tested func\_timeout with python 2.7, 3.4, and 3.5. It should work on other versions as well.
|
||||
I've tested func\_timeout with python 2.7, 3.4, 3.5, 3.6, 3.7. It should work on other versions as well.
|
||||
|
||||
Works on windows, linux/unix, cygwin, mac
|
||||
|
||||
ChangeLog can be found at https://raw.githubusercontent.com/kata198/func_timeout/master/ChangeLog
|
||||
|
||||
Pydoc can be found at: http://htmlpreview.github.io/?https://github.com/kata198/func_timeout/blob/master/doc/func_timeout.html?vers=1
|
||||
|
||||
|
||||
@ -1,184 +1,64 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><title>Python: module func_timeout.StoppableThread</title>
|
||||
<html><head><title>Python: class StoppableThread</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
</head><body bgcolor="#f0f0f8">
|
||||
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
||||
<tr bgcolor="#7799ee">
|
||||
<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>.StoppableThread</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>
|
||||
<p></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="ctypes.html">ctypes</a><br>
|
||||
</td><td width="25%" valign=top><a href="threading.html">threading</a><br>
|
||||
</td><td width="25%" valign=top><a href="time.html">time</a><br>
|
||||
</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#ee77aa">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><dl>
|
||||
<dt><font face="helvetica, arial"><a href="threading.html#Thread">threading.Thread</a>(<a href="builtins.html#object">builtins.object</a>)
|
||||
</font></dt><dd>
|
||||
<dl>
|
||||
<dt><font face="helvetica, arial"><a href="func_timeout.StoppableThread.html#JoinThread">JoinThread</a>
|
||||
</font></dt><dt><font face="helvetica, arial"><a href="func_timeout.StoppableThread.html#StoppableThread">StoppableThread</a>
|
||||
</font></dt></dl>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#ffc8d8">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#000000" face="helvetica, arial"><a name="JoinThread">class <strong>JoinThread</strong></a>(<a href="threading.html#Thread">threading.Thread</a>)</font></td></tr>
|
||||
<font color="#000000" face="helvetica, arial"><strong>func_timeout.StoppableThread</strong> = <a name="func_timeout.StoppableThread">class StoppableThread</a>(<a href="threading.html#Thread">threading.Thread</a>)</font></td></tr>
|
||||
|
||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
||||
<td colspan=2><tt><a href="#JoinThread">JoinThread</a> - The workhouse that stops the <a href="#StoppableThread">StoppableThread</a><br> </tt></td></tr>
|
||||
<tr><td> </td>
|
||||
<td width="100%"><dl><dt>Method resolution order:</dt>
|
||||
<dd><a href="func_timeout.StoppableThread.html#JoinThread">JoinThread</a></dd>
|
||||
<dd><a href="threading.html#Thread">threading.Thread</a></dd>
|
||||
<dd><a href="builtins.html#object">builtins.object</a></dd>
|
||||
</dl>
|
||||
<hr>
|
||||
Methods defined here:<br>
|
||||
<dl><dt><a name="JoinThread-__init__"><strong>__init__</strong></a>(self, otherThread, exception)</dt><dd><tt>This constructor should always be called with keyword arguments. Arguments are:<br>
|
||||
<td colspan=2><tt>func_timeout.StoppableThread(group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)<br>
|
||||
<br>
|
||||
*group* should be None; reserved for future extension when a ThreadGroup<br>
|
||||
class is implemented.<br>
|
||||
StoppableThread - A thread that can be stopped by forcing an exception in the execution context.<br>
|
||||
<br>
|
||||
*target* is the callable object to be invoked by the <a href="#JoinThread-run">run</a>()<br>
|
||||
method. Defaults to None, meaning nothing is called.<br>
|
||||
This works both to interrupt code that is in C or in python code, at either the next call to a python function,<br>
|
||||
or the next line in python code.<br>
|
||||
<br>
|
||||
*name* is the thread name. By default, a unique name is constructed of<br>
|
||||
the form "<a href="threading.html#Thread">Thread</a>-N" where N is a small decimal number.<br>
|
||||
It is recommended that if you call stop ( @see StoppableThread.stop ) that you use an exception that inherits BaseException, to ensure it likely isn't caught.<br>
|
||||
<br>
|
||||
*args* is the argument tuple for the target invocation. Defaults to ().<br>
|
||||
Also, beware unmarked exception handlers in your code. Code like this:<br>
|
||||
<br>
|
||||
*kwargs* is a dictionary of keyword arguments for the target<br>
|
||||
invocation. Defaults to {}.<br>
|
||||
while True:<br>
|
||||
try:<br>
|
||||
doSomething()<br>
|
||||
except:<br>
|
||||
continue<br>
|
||||
<br>
|
||||
If a subclass overrides the constructor, it must make sure to invoke<br>
|
||||
the base class constructor (<a href="threading.html#Thread">Thread</a>.<a href="#JoinThread-__init__">__init__</a>()) before doing anything<br>
|
||||
else to the thread.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="JoinThread-run"><strong>run</strong></a>(self)</dt><dd><tt>Method representing the thread's activity.<br>
|
||||
will never be able to abort, because the exception you raise is immediately caught.<br>
|
||||
<br>
|
||||
You may override this method in a subclass. The standard <a href="#JoinThread-run">run</a>() method<br>
|
||||
invokes the callable object passed to the object's constructor as the<br>
|
||||
target argument, if any, with sequential and keyword arguments taken<br>
|
||||
from the args and kwargs arguments, respectively.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Methods inherited from <a href="threading.html#Thread">threading.Thread</a>:<br>
|
||||
<dl><dt><a name="JoinThread-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return repr(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="JoinThread-getName"><strong>getName</strong></a>(self)</dt></dl>
|
||||
|
||||
<dl><dt><a name="JoinThread-isAlive"><strong>isAlive</strong></a> = is_alive(self)</dt><dd><tt>Return whether the thread is alive.<br>
|
||||
<br>
|
||||
This method returns True just before the <a href="#JoinThread-run">run</a>() method starts until just<br>
|
||||
after the <a href="#JoinThread-run">run</a>() method terminates. The module function enumerate()<br>
|
||||
returns a list of all alive threads.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="JoinThread-isDaemon"><strong>isDaemon</strong></a>(self)</dt></dl>
|
||||
|
||||
<dl><dt><a name="JoinThread-is_alive"><strong>is_alive</strong></a>(self)</dt><dd><tt>Return whether the thread is alive.<br>
|
||||
<br>
|
||||
This method returns True just before the <a href="#JoinThread-run">run</a>() method starts until just<br>
|
||||
after the <a href="#JoinThread-run">run</a>() method terminates. The module function enumerate()<br>
|
||||
returns a list of all alive threads.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="JoinThread-join"><strong>join</strong></a>(self, timeout=None)</dt><dd><tt>Wait until the thread terminates.<br>
|
||||
<br>
|
||||
This blocks the calling thread until the thread whose <a href="#JoinThread-join">join</a>() method is<br>
|
||||
called terminates -- either normally or through an unhandled exception<br>
|
||||
or until the optional timeout occurs.<br>
|
||||
<br>
|
||||
When the timeout argument is present and not None, it should be a<br>
|
||||
floating point number specifying a timeout for the operation in seconds<br>
|
||||
(or fractions thereof). As <a href="#JoinThread-join">join</a>() always returns None, you must call<br>
|
||||
<a href="#JoinThread-isAlive">isAlive</a>() after <a href="#JoinThread-join">join</a>() to decide whether a timeout happened -- if the<br>
|
||||
thread is still alive, the <a href="#JoinThread-join">join</a>() call timed out.<br>
|
||||
<br>
|
||||
When the timeout argument is not present or None, the operation will<br>
|
||||
block until the thread terminates.<br>
|
||||
<br>
|
||||
A thread can be <a href="#JoinThread-join">join</a>()ed many times.<br>
|
||||
<br>
|
||||
<a href="#JoinThread-join">join</a>() raises a RuntimeError if an attempt is made to join the current<br>
|
||||
thread as that would cause a deadlock. It is also an error to <a href="#JoinThread-join">join</a>() a<br>
|
||||
thread before it has been started and attempts to do so raises the same<br>
|
||||
exception.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="JoinThread-setDaemon"><strong>setDaemon</strong></a>(self, daemonic)</dt></dl>
|
||||
|
||||
<dl><dt><a name="JoinThread-setName"><strong>setName</strong></a>(self, name)</dt></dl>
|
||||
|
||||
<dl><dt><a name="JoinThread-start"><strong>start</strong></a>(self)</dt><dd><tt>Start the thread's activity.<br>
|
||||
<br>
|
||||
It must be called at most once per thread object. It arranges for the<br>
|
||||
object's <a href="#JoinThread-run">run</a>() method to be invoked in a separate thread of control.<br>
|
||||
<br>
|
||||
This method will raise a RuntimeError if called more than once on the<br>
|
||||
same thread object.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors inherited from <a href="threading.html#Thread">threading.Thread</a>:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>daemon</strong></dt>
|
||||
<dd><tt>A boolean value indicating whether this thread is a daemon thread.<br>
|
||||
<br>
|
||||
This must be set before start() is called, otherwise RuntimeError is<br>
|
||||
raised. Its initial value is inherited from the creating thread; the<br>
|
||||
main thread is not a daemon thread and therefore all threads created in<br>
|
||||
the main thread default to daemon = False.<br>
|
||||
<br>
|
||||
The entire Python program exits when no alive non-daemon threads are<br>
|
||||
left.</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>ident</strong></dt>
|
||||
<dd><tt>Thread identifier of this thread or None if it has not been started.<br>
|
||||
<br>
|
||||
This is a nonzero integer. See the thread.get_ident() function. Thread<br>
|
||||
identifiers may be recycled when a thread exits and another thread is<br>
|
||||
created. The identifier is available even after the thread has exited.</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>name</strong></dt>
|
||||
<dd><tt>A string used for identification purposes only.<br>
|
||||
<br>
|
||||
It has no semantics. Multiple threads may be given the same name. The<br>
|
||||
initial name is set by the constructor.</tt></dd>
|
||||
</dl>
|
||||
</td></tr></table> <p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#ffc8d8">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#000000" face="helvetica, arial"><a name="StoppableThread">class <strong>StoppableThread</strong></a>(<a href="threading.html#Thread">threading.Thread</a>)</font></td></tr>
|
||||
|
||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
||||
<td colspan=2><tt><a href="#StoppableThread">StoppableThread</a> - A thread that can be stopped by forcing an exception in the execution context.<br> </tt></td></tr>
|
||||
The exception is raised over and over, with a specifed delay (default 2.0 seconds)<br> </tt></td></tr>
|
||||
<tr><td> </td>
|
||||
<td width="100%"><dl><dt>Method resolution order:</dt>
|
||||
<dd><a href="func_timeout.StoppableThread.html#StoppableThread">StoppableThread</a></dd>
|
||||
<dd><a href="threading.html#Thread">threading.Thread</a></dd>
|
||||
<dd><a href="builtins.html#object">builtins.object</a></dd>
|
||||
</dl>
|
||||
<hr>
|
||||
Methods defined here:<br>
|
||||
<dl><dt><a name="StoppableThread-stop"><strong>stop</strong></a>(self, exception, raiseEvery=2.0)</dt><dd><tt>Stops the thread by raising a given exception.<br>
|
||||
<br>
|
||||
@param exception <Exception type> - Exception to throw. Likely, you want to use something<br>
|
||||
<br>
|
||||
that inherits from BaseException (so except Exception as e: continue; isn't a problem)<br>
|
||||
<br>
|
||||
This should be a class/type, NOT an instance, i.e. MyExceptionType not MyExceptionType()<br>
|
||||
<br>
|
||||
<br>
|
||||
@param raiseEvery <float> Default 2.0 - We will keep raising this exception every #raiseEvery seconds,<br>
|
||||
<br>
|
||||
until the thread terminates.<br>
|
||||
<br>
|
||||
If your code traps a specific exception type, this will allow you #raiseEvery seconds to cleanup before exit.<br>
|
||||
<br>
|
||||
If you're calling third-party code you can't control, which catches BaseException, set this to a low number<br>
|
||||
<br>
|
||||
to break out of their exception handler.<br>
|
||||
<br>
|
||||
<br>
|
||||
@return <None></tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Methods inherited from <a href="threading.html#Thread">threading.Thread</a>:<br>
|
||||
<dl><dt><a name="StoppableThread-__init__"><strong>__init__</strong></a>(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)</dt><dd><tt>This constructor should always be called with keyword arguments. Arguments are:<br>
|
||||
@ -186,11 +66,11 @@ Methods inherited from <a href="threading.html#Thread">threading.Thread</a>:<br>
|
||||
*group* should be None; reserved for future extension when a ThreadGroup<br>
|
||||
class is implemented.<br>
|
||||
<br>
|
||||
*target* is the callable object to be invoked by the <a href="#StoppableThread-run">run</a>()<br>
|
||||
*target* is the callable object to be invoked by the <a href="#func_timeout.StoppableThread-run">run</a>()<br>
|
||||
method. Defaults to None, meaning nothing is called.<br>
|
||||
<br>
|
||||
*name* is the thread name. By default, a unique name is constructed of<br>
|
||||
the form "<a href="threading.html#Thread">Thread</a>-N" where N is a small decimal number.<br>
|
||||
the form "Thread-N" where N is a small decimal number.<br>
|
||||
<br>
|
||||
*args* is the argument tuple for the target invocation. Defaults to ().<br>
|
||||
<br>
|
||||
@ -198,52 +78,50 @@ the form "<a href="threading.html#Thread">Thread</a>-N" where&nbs
|
||||
invocation. Defaults to {}.<br>
|
||||
<br>
|
||||
If a subclass overrides the constructor, it must make sure to invoke<br>
|
||||
the base class constructor (<a href="threading.html#Thread">Thread</a>.<a href="#StoppableThread-__init__">__init__</a>()) before doing anything<br>
|
||||
the base class constructor (Thread.<a href="#func_timeout.StoppableThread-__init__">__init__</a>()) before doing anything<br>
|
||||
else to the thread.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="StoppableThread-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return repr(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="StoppableThread-getName"><strong>getName</strong></a>(self)</dt></dl>
|
||||
|
||||
<dl><dt><a name="StoppableThread-isAlive"><strong>isAlive</strong></a> = is_alive(self)</dt><dd><tt>Return whether the thread is alive.<br>
|
||||
<dl><dt><a name="StoppableThread-isAlive"><strong>isAlive</strong></a>(self)</dt><dd><tt>Return whether the thread is alive.<br>
|
||||
<br>
|
||||
This method returns True just before the <a href="#StoppableThread-run">run</a>() method starts until just<br>
|
||||
after the <a href="#StoppableThread-run">run</a>() method terminates. The module function enumerate()<br>
|
||||
returns a list of all alive threads.</tt></dd></dl>
|
||||
This method is deprecated, use <a href="#func_timeout.StoppableThread-is_alive">is_alive</a>() instead.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="StoppableThread-isDaemon"><strong>isDaemon</strong></a>(self)</dt></dl>
|
||||
|
||||
<dl><dt><a name="StoppableThread-is_alive"><strong>is_alive</strong></a>(self)</dt><dd><tt>Return whether the thread is alive.<br>
|
||||
<br>
|
||||
This method returns True just before the <a href="#StoppableThread-run">run</a>() method starts until just<br>
|
||||
after the <a href="#StoppableThread-run">run</a>() method terminates. The module function enumerate()<br>
|
||||
This method returns True just before the <a href="#func_timeout.StoppableThread-run">run</a>() method starts until just<br>
|
||||
after the <a href="#func_timeout.StoppableThread-run">run</a>() method terminates. The module function enumerate()<br>
|
||||
returns a list of all alive threads.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="StoppableThread-join"><strong>join</strong></a>(self, timeout=None)</dt><dd><tt>Wait until the thread terminates.<br>
|
||||
<br>
|
||||
This blocks the calling thread until the thread whose <a href="#StoppableThread-join">join</a>() method is<br>
|
||||
This blocks the calling thread until the thread whose <a href="#func_timeout.StoppableThread-join">join</a>() method is<br>
|
||||
called terminates -- either normally or through an unhandled exception<br>
|
||||
or until the optional timeout occurs.<br>
|
||||
<br>
|
||||
When the timeout argument is present and not None, it should be a<br>
|
||||
floating point number specifying a timeout for the operation in seconds<br>
|
||||
(or fractions thereof). As <a href="#StoppableThread-join">join</a>() always returns None, you must call<br>
|
||||
<a href="#StoppableThread-isAlive">isAlive</a>() after <a href="#StoppableThread-join">join</a>() to decide whether a timeout happened -- if the<br>
|
||||
thread is still alive, the <a href="#StoppableThread-join">join</a>() call timed out.<br>
|
||||
(or fractions thereof). As <a href="#func_timeout.StoppableThread-join">join</a>() always returns None, you must call<br>
|
||||
<a href="#func_timeout.StoppableThread-is_alive">is_alive</a>() after <a href="#func_timeout.StoppableThread-join">join</a>() to decide whether a timeout happened -- if the<br>
|
||||
thread is still alive, the <a href="#func_timeout.StoppableThread-join">join</a>() call timed out.<br>
|
||||
<br>
|
||||
When the timeout argument is not present or None, the operation will<br>
|
||||
block until the thread terminates.<br>
|
||||
<br>
|
||||
A thread can be <a href="#StoppableThread-join">join</a>()ed many times.<br>
|
||||
A thread can be <a href="#func_timeout.StoppableThread-join">join</a>()ed many times.<br>
|
||||
<br>
|
||||
<a href="#StoppableThread-join">join</a>() raises a RuntimeError if an attempt is made to join the current<br>
|
||||
thread as that would cause a deadlock. It is also an error to <a href="#StoppableThread-join">join</a>() a<br>
|
||||
<a href="#func_timeout.StoppableThread-join">join</a>() raises a RuntimeError if an attempt is made to join the current<br>
|
||||
thread as that would cause a deadlock. It is also an error to <a href="#func_timeout.StoppableThread-join">join</a>() a<br>
|
||||
thread before it has been started and attempts to do so raises the same<br>
|
||||
exception.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="StoppableThread-run"><strong>run</strong></a>(self)</dt><dd><tt>Method representing the thread's activity.<br>
|
||||
<br>
|
||||
You may override this method in a subclass. The standard <a href="#StoppableThread-run">run</a>() method<br>
|
||||
You may override this method in a subclass. The standard <a href="#func_timeout.StoppableThread-run">run</a>() method<br>
|
||||
invokes the callable object passed to the object's constructor as the<br>
|
||||
target argument, if any, with sequential and keyword arguments taken<br>
|
||||
from the args and kwargs arguments, respectively.</tt></dd></dl>
|
||||
@ -255,7 +133,7 @@ from the args and kwargs arguments, respectively.<
|
||||
<dl><dt><a name="StoppableThread-start"><strong>start</strong></a>(self)</dt><dd><tt>Start the thread's activity.<br>
|
||||
<br>
|
||||
It must be called at most once per thread object. It arranges for the<br>
|
||||
object's <a href="#StoppableThread-run">run</a>() method to be invoked in a separate thread of control.<br>
|
||||
object's <a href="#func_timeout.StoppableThread-run">run</a>() method to be invoked in a separate thread of control.<br>
|
||||
<br>
|
||||
This method will raise a RuntimeError if called more than once on the<br>
|
||||
same thread object.</tt></dd></dl>
|
||||
@ -282,7 +160,7 @@ left.</tt></dd>
|
||||
<dl><dt><strong>ident</strong></dt>
|
||||
<dd><tt>Thread identifier of this thread or None if it has not been started.<br>
|
||||
<br>
|
||||
This is a nonzero integer. See the thread.get_ident() function. Thread<br>
|
||||
This is a nonzero integer. See the get_ident() function. Thread<br>
|
||||
identifiers may be recycled when a thread exits and another thread is<br>
|
||||
created. The identifier is available even after the thread has exited.</tt></dd>
|
||||
</dl>
|
||||
@ -292,5 +170,5 @@ created. The identifier is available even after&nb
|
||||
It has no semantics. Multiple threads may be given the same name. The<br>
|
||||
initial name is set by the constructor.</tt></dd>
|
||||
</dl>
|
||||
</td></tr></table></td></tr></table>
|
||||
</body></html>
|
||||
</td></tr></table>
|
||||
</body></html>
|
||||
@ -1,59 +1,111 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><title>Python: module func_timeout.dafunc</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
</head><body bgcolor="#f0f0f8">
|
||||
<html ><head ><title >Python: module func_timeout.dafunc</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head><body bgcolor="#f0f0f8" >
|
||||
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
||||
<tr bgcolor="#7799ee">
|
||||
<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>.dafunc</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>
|
||||
<p><tt>Copyright (c) 2016 Tim Savannah All Rights Reserved.<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>
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="heading" >
|
||||
<tr bgcolor="#7799ee" >
|
||||
<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>.dafunc</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>
|
||||
<p ><tt >Copyright (c) 2016, 2017 Tim Savannah All Rights Reserved.<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 />
|
||||
LICENSE, otherwise it is available at https://github.com/kata198/func_timeout/LICENSE</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
<p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#aa55cc" >
|
||||
<td colspan="3" valign="bottom" > <br />
|
||||
<font color="#ffffff" face="helvetica, arial" ><big ><strong >Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="threading.html">threading</a><br>
|
||||
</td><td width="25%" valign=top><a href="time.html">time</a><br>
|
||||
</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#eeaa77">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
|
||||
<tr ><td bgcolor="#aa55cc" ><tt > </tt></td><td > </td>
|
||||
<td width="100%" ><table width="100%" summary="list" ><tr ><td width="25%" valign="top" ><a href="copy.html" >copy</a><br />
|
||||
<a href="inspect.html" >inspect</a><br />
|
||||
</td><td width="25%" valign="top" ><a href="sys.html" >sys</a><br />
|
||||
<a href="threading.html" >threading</a><br />
|
||||
</td><td width="25%" valign="top" ><a href="time.html" >time</a><br />
|
||||
<a href="types.html" >types</a><br />
|
||||
</td><td width="25%" valign="top" ></td></tr></table></td></tr></table><p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#eeaa77" >
|
||||
<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>
|
||||
<td width="100%"><dl><dt><a name="-func_timeout"><strong>func_timeout</strong></a>(timeout, func, args=(), kwargs=None)</dt><dd><tt>func_timeout - Runs the given function for up to #timeout# seconds.<br>
|
||||
<br>
|
||||
Raises any exceptions #func# would raise, returns what #func# would return (unless timeout is exceeded), in which case it raises FunctionTimedOut<br>
|
||||
<br>
|
||||
@param timeout <float> - Maximum number of seconds to run #func# before terminating<br>
|
||||
@param func <function> - The function to call<br>
|
||||
@param args <tuple> - Any ordered arguments to pass to the function<br>
|
||||
@param kwargs <dict/None> - Keyword arguments to pass to the function.<br>
|
||||
<br>
|
||||
@raises - FunctionTimedOut if #timeout# is exceeded, otherwise anything #func# could raise will be raised<br>
|
||||
<br>
|
||||
If the timeout is exceeded, FunctionTimedOut will be raised within the context of the called function every two seconds until it terminates,<br>
|
||||
but will not block the calling thread (a new thread will be created to perform the join). If possible, you should try/except FunctionTimedOut<br>
|
||||
to return cleanly, but in most cases it will 'just work'.<br>
|
||||
<br>
|
||||
Be careful of code like:<br>
|
||||
def myfunc():<br>
|
||||
while True:<br>
|
||||
try:<br>
|
||||
dosomething()<br>
|
||||
except Exception:<br>
|
||||
continue<br>
|
||||
<br>
|
||||
because it will never terminate.<br>
|
||||
<br>
|
||||
<tr ><td bgcolor="#eeaa77" ><tt > </tt></td><td > </td>
|
||||
<td width="100%" ><dl ><dt ><a name="-func_set_timeout" ><strong >func_set_timeout</strong></a>(timeout, allowOverride=False)</dt><dd ><tt >func_set_timeout - Decorator to run a function with a given/calculated timeout (max execution time).<br />
|
||||
Optionally (if #allowOverride is True), adds a paramater, "forceTimeout", to the<br />
|
||||
function which, if provided, will override the default timeout for that invocation.<br />
|
||||
<br />
|
||||
If #timeout is provided as a lambda/function, it will be called<br />
|
||||
prior to each invocation of the decorated function to calculate the timeout to be used<br />
|
||||
for that call, based on the arguments passed to the decorated function.<br />
|
||||
<br />
|
||||
For example, you may have a "processData" function whose execution time<br />
|
||||
depends on the number of "data" elements, so you may want a million elements to have a<br />
|
||||
much higher timeout than seven elements.)<br />
|
||||
<br />
|
||||
If #allowOverride is True AND a kwarg of "forceTimeout" is passed to the wrapped function, that timeout<br />
|
||||
will be used for that single call.<br />
|
||||
<br />
|
||||
@param timeout <float OR lambda/function> -<br />
|
||||
<br />
|
||||
**If float:**<br />
|
||||
Default number of seconds max to allow function to execute<br />
|
||||
before throwing FunctionTimedOut<br />
|
||||
<br />
|
||||
**If lambda/function:<br />
|
||||
<br />
|
||||
If a function/lambda is provided, it will be called for every<br />
|
||||
invocation of the decorated function (unless #allowOverride=True and "forceTimeout" was passed)<br />
|
||||
to determine the timeout to use based on the arguments to the decorated function.<br />
|
||||
<br />
|
||||
The arguments as passed into the decorated function will be passed to this function.<br />
|
||||
They either must match exactly to what the decorated function has, OR<br />
|
||||
if you prefer to get the *args (list of ordered args) and **kwargs ( key : value keyword args form),<br />
|
||||
define your calculate function like:<br />
|
||||
<br />
|
||||
def calculateTimeout(*args, **kwargs):<br />
|
||||
...<br />
|
||||
<br />
|
||||
or lambda like:<br />
|
||||
<br />
|
||||
calculateTimeout = lambda *args, **kwargs : ...<br />
|
||||
<br />
|
||||
otherwise the args to your calculate function should match exactly the decorated function.<br />
|
||||
<br />
|
||||
<br />
|
||||
@param allowOverride <bool> Default False, if True adds a keyword argument to the decorated function,<br />
|
||||
"forceTimeout" which, if provided, will override the #timeout. If #timeout was provided as a lambda / function, it<br />
|
||||
will not be called.<br />
|
||||
<br />
|
||||
@throws FunctionTimedOut If time alloted passes without function returning naturally<br />
|
||||
<br />
|
||||
@see func_timeout</tt></dd></dl>
|
||||
<dl ><dt ><a name="-func_timeout" ><strong >func_timeout</strong></a>(timeout, func, args=(), kwargs=None)</dt><dd ><tt >func_timeout - Runs the given function for up to #timeout# seconds.<br />
|
||||
<br />
|
||||
Raises any exceptions #func# would raise, returns what #func# would return (unless timeout is exceeded), in which case it raises FunctionTimedOut<br />
|
||||
<br />
|
||||
@param timeout <float> - Maximum number of seconds to run #func# before terminating<br />
|
||||
<br />
|
||||
@param func <function> - The function to call<br />
|
||||
<br />
|
||||
@param args <tuple> - Any ordered arguments to pass to the function<br />
|
||||
<br />
|
||||
@param kwargs <dict/None> - Keyword arguments to pass to the function.<br />
|
||||
<br />
|
||||
<br />
|
||||
@raises - FunctionTimedOut if #timeout# is exceeded, otherwise anything #func# could raise will be raised<br />
|
||||
<br />
|
||||
If the timeout is exceeded, FunctionTimedOut will be raised within the context of the called function every two seconds until it terminates,<br />
|
||||
but will not block the calling thread (a new thread will be created to perform the join). If possible, you should try/except FunctionTimedOut<br />
|
||||
to return cleanly, but in most cases it will 'just work'.<br />
|
||||
<br />
|
||||
@return - The return value that #func# gives</tt></dd></dl>
|
||||
</td></tr></table>
|
||||
</body></html>
|
||||
</td></tr></table><p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#55aa55" >
|
||||
<td colspan="3" valign="bottom" > <br />
|
||||
<font color="#ffffff" face="helvetica, arial" ><big ><strong >Data</strong></big></font></td></tr>
|
||||
|
||||
<tr ><td bgcolor="#55aa55" ><tt > </tt></td><td > </td>
|
||||
<td width="100%" ><strong >__all__</strong> = ('func_timeout', 'func_set_timeout')</td></tr></table>
|
||||
</p></p></p></body></html>
|
||||
@ -1,87 +1,137 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><title>Python: module func_timeout.exceptions</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
</head><body bgcolor="#f0f0f8">
|
||||
<html ><head ><title >Python: module func_timeout.exceptions</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head><body bgcolor="#f0f0f8" >
|
||||
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
||||
<tr bgcolor="#7799ee">
|
||||
<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>.exceptions</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>
|
||||
<p></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#ee77aa">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="heading" >
|
||||
<tr bgcolor="#7799ee" >
|
||||
<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>.exceptions</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>
|
||||
<p ><tt >Copyright (c) 2016 Tim Savannah All Rights Reserved.<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 />
|
||||
LICENSE, otherwise it is available at https://github.com/kata198/func_timeout/LICENSE</tt></p>
|
||||
<p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#ee77aa" >
|
||||
<td colspan="3" valign="bottom" > <br />
|
||||
<font color="#ffffff" face="helvetica, arial" ><big ><strong >Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><dl>
|
||||
<dt><font face="helvetica, arial"><a href="builtins.html#BaseException">builtins.BaseException</a>(<a href="builtins.html#object">builtins.object</a>)
|
||||
</font></dt><dd>
|
||||
<dl>
|
||||
<dt><font face="helvetica, arial"><a href="func_timeout.exceptions.html#FunctionTimedOut">FunctionTimedOut</a>
|
||||
<tr ><td bgcolor="#ee77aa" ><tt > </tt></td><td > </td>
|
||||
<td width="100%" ><dl >
|
||||
<dt ><font face="helvetica, arial" ><a href="builtins.html#BaseException" >builtins.BaseException</a>(<a href="builtins.html#object" >builtins.object</a>)
|
||||
</font></dt><dd >
|
||||
<dl >
|
||||
<dt ><font face="helvetica, arial" ><a href="func_timeout.exceptions.html#FunctionTimedOut" >FunctionTimedOut</a>
|
||||
</font></dt></dl>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#ffc8d8">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#000000" face="helvetica, arial"><a name="FunctionTimedOut">class <strong>FunctionTimedOut</strong></a>(<a href="builtins.html#BaseException">builtins.BaseException</a>)</font></td></tr>
|
||||
<p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#ffc8d8" >
|
||||
<td colspan="3" valign="bottom" > <br />
|
||||
<font color="#000000" face="helvetica, arial" ><a name="FunctionTimedOut" >class <strong >FunctionTimedOut</strong></a>(<a href="builtins.html#BaseException" >builtins.BaseException</a>)</font></td></tr>
|
||||
|
||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
||||
<td colspan=2><tt>Common base class for all exceptions<br> </tt></td></tr>
|
||||
<tr><td> </td>
|
||||
<td width="100%"><dl><dt>Method resolution order:</dt>
|
||||
<dd><a href="func_timeout.exceptions.html#FunctionTimedOut">FunctionTimedOut</a></dd>
|
||||
<dd><a href="builtins.html#BaseException">builtins.BaseException</a></dd>
|
||||
<dd><a href="builtins.html#object">builtins.object</a></dd>
|
||||
<tr bgcolor="#ffc8d8" ><td rowspan="2" ><tt > </tt></td>
|
||||
<td colspan="2" ><tt ><a href="#FunctionTimedOut" >FunctionTimedOut</a>(msg='', timedOutAfter=None, timedOutFunction=None, timedOutArgs=None, timedOutKwargs=None)<br />
|
||||
<br />
|
||||
<a href="#FunctionTimedOut" >FunctionTimedOut</a> - Exception raised when a function times out<br />
|
||||
<br />
|
||||
@property timedOutAfter - Number of seconds before timeout was triggered<br />
|
||||
<br />
|
||||
@property timedOutFunction - Function called which timed out<br />
|
||||
@property timedOutArgs - Ordered args to function<br />
|
||||
@property timedOutKwargs - Keyword args to function<br />
|
||||
<br />
|
||||
@method retry - Retries the function with same arguments, with option to run with original timeout, no timeout, or a different<br />
|
||||
explicit timeout. @see <a href="#FunctionTimedOut" >FunctionTimedOut</a>.retry<br /> </tt></td></tr>
|
||||
<tr ><td > </td>
|
||||
<td width="100%" ><dl ><dt >Method resolution order:</dt>
|
||||
<dd ><a href="func_timeout.exceptions.html#FunctionTimedOut" >FunctionTimedOut</a></dd>
|
||||
<dd ><a href="builtins.html#BaseException" >builtins.BaseException</a></dd>
|
||||
<dd ><a href="builtins.html#object" >builtins.object</a></dd>
|
||||
</dl>
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if defined)</tt></dd>
|
||||
<hr />
|
||||
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 >__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 />
|
||||
<br />
|
||||
@return <str> - Message</tt></dd></dl>
|
||||
|
||||
<dl ><dt ><a name="FunctionTimedOut-retry" ><strong >retry</strong></a>(self, timeout='RETRY_SAME_TIMEOUT')</dt><dd ><tt >retry - Retry the timed-out function with same arguments.<br />
|
||||
<br />
|
||||
@param timeout <float/RETRY_SAME_TIMEOUT/None> Default RETRY_SAME_TIMEOUT<br />
|
||||
<br />
|
||||
If RETRY_SAME_TIMEOUT : Will retry the function same args, same timeout<br />
|
||||
If a float/int : Will retry the function same args with provided timeout<br />
|
||||
If None : Will retry function same args no timeout<br />
|
||||
<br />
|
||||
@return - Returnval from function</tt></dd></dl>
|
||||
|
||||
<hr />
|
||||
Data descriptors defined here:<br />
|
||||
<dl ><dt ><strong >__weakref__</strong></dt>
|
||||
<dd ><tt >list of weak references to the object (if defined)</tt></dd>
|
||||
</dl>
|
||||
<hr>
|
||||
Methods inherited from <a href="builtins.html#BaseException">builtins.BaseException</a>:<br>
|
||||
<dl><dt><a name="FunctionTimedOut-__delattr__"><strong>__delattr__</strong></a>(self, name, /)</dt><dd><tt>Implement delattr(self, name).</tt></dd></dl>
|
||||
<hr />
|
||||
Methods inherited from <a href="builtins.html#BaseException" >builtins.BaseException</a>:<br />
|
||||
<dl ><dt ><a name="FunctionTimedOut-__delattr__" ><strong >__delattr__</strong></a>(self, name, /)</dt><dd ><tt >Implement delattr(self, name).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__getattribute__"><strong>__getattribute__</strong></a>(self, name, /)</dt><dd><tt>Return getattr(self, name).</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__getattribute__" ><strong >__getattribute__</strong></a>(self, name, /)</dt><dd ><tt >Return getattr(self, name).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__init__"><strong>__init__</strong></a>(self, /, *args, **kwargs)</dt><dd><tt>Initialize self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__reduce__" ><strong >__reduce__</strong></a>(...)</dt><dd ><tt >Helper for pickle.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__new__"><strong>__new__</strong></a>(*args, **kwargs)<font color="#909090"><font face="helvetica, arial"> from <a href="builtins.html#type">builtins.type</a></font></font></dt><dd><tt>Create and return a new object. See help(type) for accurate signature.</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__repr__" ><strong >__repr__</strong></a>(self, /)</dt><dd ><tt >Return repr(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__reduce__"><strong>__reduce__</strong></a>(...)</dt><dd><tt>helper for pickle</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__setattr__" ><strong >__setattr__</strong></a>(self, name, value, /)</dt><dd ><tt >Implement setattr(self, name, value).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__repr__"><strong>__repr__</strong></a>(self, /)</dt><dd><tt>Return repr(self).</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__setstate__" ><strong >__setstate__</strong></a>(...)</dt></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__setattr__"><strong>__setattr__</strong></a>(self, name, value, /)</dt><dd><tt>Implement setattr(self, name, value).</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__str__" ><strong >__str__</strong></a>(self, /)</dt><dd ><tt >Return str(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-with_traceback" ><strong >with_traceback</strong></a>(...)</dt><dd ><tt >Exception.<a href="#FunctionTimedOut-with_traceback" >with_traceback</a>(tb) --<br />
|
||||
set self.<strong >__traceback__</strong> to tb and return self.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__str__"><strong>__str__</strong></a>(self, /)</dt><dd><tt>Return str(self).</tt></dd></dl>
|
||||
<hr />
|
||||
Static methods inherited from <a href="builtins.html#BaseException" >builtins.BaseException</a>:<br />
|
||||
<dl ><dt ><a name="FunctionTimedOut-__new__" ><strong >__new__</strong></a>(*args, **kwargs)<font color="#909090" ><font face="helvetica, arial" > from <a href="builtins.html#type" >builtins.type</a></font></font></dt><dd ><tt >Create and return a new object. See help(type) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-with_traceback"><strong>with_traceback</strong></a>(...)</dt><dd><tt>Exception.<a href="#FunctionTimedOut-with_traceback">with_traceback</a>(tb) --<br>
|
||||
set self.<strong>__traceback__</strong> to tb and return self.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors inherited from <a href="builtins.html#BaseException">builtins.BaseException</a>:<br>
|
||||
<dl><dt><strong>__cause__</strong></dt>
|
||||
<dd><tt>exception cause</tt></dd>
|
||||
<hr />
|
||||
Data descriptors inherited from <a href="builtins.html#BaseException" >builtins.BaseException</a>:<br />
|
||||
<dl ><dt ><strong >__cause__</strong></dt>
|
||||
<dd ><tt >exception cause</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__context__</strong></dt>
|
||||
<dd><tt>exception context</tt></dd>
|
||||
<dl ><dt ><strong >__context__</strong></dt>
|
||||
<dd ><tt >exception context</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dl ><dt ><strong >__dict__</strong></dt>
|
||||
</dl>
|
||||
<dl><dt><strong>__suppress_context__</strong></dt>
|
||||
<dl ><dt ><strong >__suppress_context__</strong></dt>
|
||||
</dl>
|
||||
<dl><dt><strong>__traceback__</strong></dt>
|
||||
<dl ><dt ><strong >__traceback__</strong></dt>
|
||||
</dl>
|
||||
<dl><dt><strong>args</strong></dt>
|
||||
<dl ><dt ><strong >args</strong></dt>
|
||||
</dl>
|
||||
</td></tr></table></td></tr></table>
|
||||
</body></html>
|
||||
</td></tr></table></p></td></tr></table><p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#55aa55" >
|
||||
<td colspan="3" valign="bottom" > <br />
|
||||
<font color="#ffffff" face="helvetica, arial" ><big ><strong >Data</strong></big></font></td></tr>
|
||||
|
||||
<tr ><td bgcolor="#55aa55" ><tt > </tt></td><td > </td>
|
||||
<td width="100%" ><strong >RETRY_SAME_TIMEOUT</strong> = 'RETRY_SAME_TIMEOUT'<br />
|
||||
<strong >__all__</strong> = ('FunctionTimedOut', 'RETRY_SAME_TIMEOUT')</td></tr></table>
|
||||
</p></p></body></html>
|
||||
@ -1,141 +1,399 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><title>Python: package func_timeout</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
</head><body bgcolor="#f0f0f8">
|
||||
<html ><head ><title >Python: package func_timeout</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head><body bgcolor="#f0f0f8" >
|
||||
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
||||
<tr bgcolor="#7799ee">
|
||||
<td valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong>func_timeout</strong></big></big> (version 3.1.0)</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 Tim Savannah All Rights Reserved.<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>
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="heading" >
|
||||
<tr bgcolor="#7799ee" >
|
||||
<td valign="bottom" > <br />
|
||||
<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 />
|
||||
<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 />
|
||||
LICENSE, otherwise it is available at https://github.com/kata198/func_timeout/LICENSE</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
|
||||
<p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#aa55cc" >
|
||||
<td colspan="3" valign="bottom" > <br />
|
||||
<font color="#ffffff" face="helvetica, arial" ><big ><strong >Package Contents</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="func_timeout.StoppableThread.html">StoppableThread</a><br>
|
||||
</td><td width="25%" valign=top><a href="func_timeout.dafunc.html">dafunc</a><br>
|
||||
</td><td width="25%" valign=top><a href="func_timeout.exceptions.html">exceptions</a><br>
|
||||
</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#ee77aa">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
<tr ><td bgcolor="#aa55cc" ><tt > </tt></td><td > </td>
|
||||
<td width="100%" ><table width="100%" summary="list" ><tr ><td width="25%" valign="top" ><a href="func_timeout.StoppableThread.html" >StoppableThread</a><br />
|
||||
<a href="func_timeout.dafunc.html" >dafunc</a><br />
|
||||
</td><td width="25%" valign="top" ><a href="func_timeout.exceptions.html" >exceptions</a><br />
|
||||
<a href="func_timeout.py2_raise.html" >py2_raise</a><br />
|
||||
</td><td width="25%" valign="top" ><a href="func_timeout.py3_raise.html" >py3_raise</a><br />
|
||||
</td><td width="25%" valign="top" ></td></tr></table></td></tr></table><p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#ee77aa" >
|
||||
<td colspan="3" valign="bottom" > <br />
|
||||
<font color="#ffffff" face="helvetica, arial" ><big ><strong >Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><dl>
|
||||
<dt><font face="helvetica, arial"><a href="builtins.html#BaseException">builtins.BaseException</a>(<a href="builtins.html#object">builtins.object</a>)
|
||||
</font></dt><dd>
|
||||
<dl>
|
||||
<dt><font face="helvetica, arial"><a href="func_timeout.exceptions.html#FunctionTimedOut">func_timeout.exceptions.FunctionTimedOut</a>
|
||||
<tr ><td bgcolor="#ee77aa" ><tt > </tt></td><td > </td>
|
||||
<td width="100%" ><dl >
|
||||
<dt ><font face="helvetica, arial" ><a href="builtins.html#BaseException" >builtins.BaseException</a>(<a href="builtins.html#object" >builtins.object</a>)
|
||||
</font></dt><dd >
|
||||
<dl >
|
||||
<dt ><font face="helvetica, arial" ><a href="func_timeout.exceptions.html#FunctionTimedOut" >func_timeout.exceptions.FunctionTimedOut</a>
|
||||
</font></dt></dl>
|
||||
</dd>
|
||||
<dt ><font face="helvetica, arial" ><a href="threading.html#Thread" >threading.Thread</a>(<a href="builtins.html#object" >builtins.object</a>)
|
||||
</font></dt><dd >
|
||||
<dl >
|
||||
<dt ><font face="helvetica, arial" ><a href="func_timeout.StoppableThread.html#StoppableThread" >func_timeout.StoppableThread.StoppableThread</a>
|
||||
</font></dt></dl>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#ffc8d8">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#000000" face="helvetica, arial"><a name="FunctionTimedOut">class <strong>FunctionTimedOut</strong></a>(<a href="builtins.html#BaseException">builtins.BaseException</a>)</font></td></tr>
|
||||
<p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#ffc8d8" >
|
||||
<td colspan="3" valign="bottom" > <br />
|
||||
<font color="#000000" face="helvetica, arial" ><a name="FunctionTimedOut" >class <strong >FunctionTimedOut</strong></a>(<a href="builtins.html#BaseException" >builtins.BaseException</a>)</font></td></tr>
|
||||
|
||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
||||
<td colspan=2><tt><a href="#FunctionTimedOut">FunctionTimedOut</a> - Exception raised when a function times out<br> </tt></td></tr>
|
||||
<tr><td> </td>
|
||||
<td width="100%"><dl><dt>Method resolution order:</dt>
|
||||
<dd><a href="func_timeout.exceptions.html#FunctionTimedOut">FunctionTimedOut</a></dd>
|
||||
<dd><a href="builtins.html#BaseException">builtins.BaseException</a></dd>
|
||||
<dd><a href="builtins.html#object">builtins.object</a></dd>
|
||||
<tr bgcolor="#ffc8d8" ><td rowspan="2" ><tt > </tt></td>
|
||||
<td colspan="2" ><tt ><a href="#FunctionTimedOut" >FunctionTimedOut</a>(msg='', timedOutAfter=None, timedOutFunction=None, timedOutArgs=None, timedOutKwargs=None)<br />
|
||||
<br />
|
||||
<a href="#FunctionTimedOut" >FunctionTimedOut</a> - Exception raised when a function times out<br />
|
||||
<br />
|
||||
@property timedOutAfter - Number of seconds before timeout was triggered<br />
|
||||
<br />
|
||||
@property timedOutFunction - Function called which timed out<br />
|
||||
@property timedOutArgs - Ordered args to function<br />
|
||||
@property timedOutKwargs - Keyword args to function<br />
|
||||
<br />
|
||||
@method retry - Retries the function with same arguments, with option to run with original timeout, no timeout, or a different<br />
|
||||
explicit timeout. @see <a href="#FunctionTimedOut" >FunctionTimedOut</a>.retry<br /> </tt></td></tr>
|
||||
<tr ><td > </td>
|
||||
<td width="100%" ><dl ><dt >Method resolution order:</dt>
|
||||
<dd ><a href="func_timeout.exceptions.html#FunctionTimedOut" >FunctionTimedOut</a></dd>
|
||||
<dd ><a href="builtins.html#BaseException" >builtins.BaseException</a></dd>
|
||||
<dd ><a href="builtins.html#object" >builtins.object</a></dd>
|
||||
</dl>
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if defined)</tt></dd>
|
||||
<hr />
|
||||
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 >__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 />
|
||||
<br />
|
||||
@return <str> - Message</tt></dd></dl>
|
||||
|
||||
<dl ><dt ><a name="FunctionTimedOut-retry" ><strong >retry</strong></a>(self, timeout='RETRY_SAME_TIMEOUT')</dt><dd ><tt >retry - Retry the timed-out function with same arguments.<br />
|
||||
<br />
|
||||
@param timeout <float/RETRY_SAME_TIMEOUT/None> Default RETRY_SAME_TIMEOUT<br />
|
||||
<br />
|
||||
If RETRY_SAME_TIMEOUT : Will retry the function same args, same timeout<br />
|
||||
If a float/int : Will retry the function same args with provided timeout<br />
|
||||
If None : Will retry function same args no timeout<br />
|
||||
<br />
|
||||
@return - Returnval from function</tt></dd></dl>
|
||||
|
||||
<hr />
|
||||
Data descriptors defined here:<br />
|
||||
<dl ><dt ><strong >__weakref__</strong></dt>
|
||||
<dd ><tt >list of weak references to the object (if defined)</tt></dd>
|
||||
</dl>
|
||||
<hr>
|
||||
Methods inherited from <a href="builtins.html#BaseException">builtins.BaseException</a>:<br>
|
||||
<dl><dt><a name="FunctionTimedOut-__delattr__"><strong>__delattr__</strong></a>(self, name, /)</dt><dd><tt>Implement delattr(self, name).</tt></dd></dl>
|
||||
<hr />
|
||||
Methods inherited from <a href="builtins.html#BaseException" >builtins.BaseException</a>:<br />
|
||||
<dl ><dt ><a name="FunctionTimedOut-__delattr__" ><strong >__delattr__</strong></a>(self, name, /)</dt><dd ><tt >Implement delattr(self, name).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__getattribute__"><strong>__getattribute__</strong></a>(self, name, /)</dt><dd><tt>Return getattr(self, name).</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__getattribute__" ><strong >__getattribute__</strong></a>(self, name, /)</dt><dd ><tt >Return getattr(self, name).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__init__"><strong>__init__</strong></a>(self, /, *args, **kwargs)</dt><dd><tt>Initialize self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__reduce__" ><strong >__reduce__</strong></a>(...)</dt><dd ><tt >Helper for pickle.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__new__"><strong>__new__</strong></a>(*args, **kwargs)<font color="#909090"><font face="helvetica, arial"> from <a href="builtins.html#type">builtins.type</a></font></font></dt><dd><tt>Create and return a new object. See help(type) for accurate signature.</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__repr__" ><strong >__repr__</strong></a>(self, /)</dt><dd ><tt >Return repr(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__reduce__"><strong>__reduce__</strong></a>(...)</dt><dd><tt>helper for pickle</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__setattr__" ><strong >__setattr__</strong></a>(self, name, value, /)</dt><dd ><tt >Implement setattr(self, name, value).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__repr__"><strong>__repr__</strong></a>(self, /)</dt><dd><tt>Return repr(self).</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__setstate__" ><strong >__setstate__</strong></a>(...)</dt></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__setattr__"><strong>__setattr__</strong></a>(self, name, value, /)</dt><dd><tt>Implement setattr(self, name, value).</tt></dd></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-__str__" ><strong >__str__</strong></a>(self, /)</dt><dd ><tt >Return str(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
|
||||
<dl ><dt ><a name="FunctionTimedOut-with_traceback" ><strong >with_traceback</strong></a>(...)</dt><dd ><tt >Exception.<a href="#FunctionTimedOut-with_traceback" >with_traceback</a>(tb) --<br />
|
||||
set self.<strong >__traceback__</strong> to tb and return self.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-__str__"><strong>__str__</strong></a>(self, /)</dt><dd><tt>Return str(self).</tt></dd></dl>
|
||||
<hr />
|
||||
Static methods inherited from <a href="builtins.html#BaseException" >builtins.BaseException</a>:<br />
|
||||
<dl ><dt ><a name="FunctionTimedOut-__new__" ><strong >__new__</strong></a>(*args, **kwargs)<font color="#909090" ><font face="helvetica, arial" > from <a href="builtins.html#type" >builtins.type</a></font></font></dt><dd ><tt >Create and return a new object. See help(type) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FunctionTimedOut-with_traceback"><strong>with_traceback</strong></a>(...)</dt><dd><tt>Exception.<a href="#FunctionTimedOut-with_traceback">with_traceback</a>(tb) --<br>
|
||||
set self.<strong>__traceback__</strong> to tb and return self.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors inherited from <a href="builtins.html#BaseException">builtins.BaseException</a>:<br>
|
||||
<dl><dt><strong>__cause__</strong></dt>
|
||||
<dd><tt>exception cause</tt></dd>
|
||||
<hr />
|
||||
Data descriptors inherited from <a href="builtins.html#BaseException" >builtins.BaseException</a>:<br />
|
||||
<dl ><dt ><strong >__cause__</strong></dt>
|
||||
<dd ><tt >exception cause</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__context__</strong></dt>
|
||||
<dd><tt>exception context</tt></dd>
|
||||
<dl ><dt ><strong >__context__</strong></dt>
|
||||
<dd ><tt >exception context</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dl ><dt ><strong >__dict__</strong></dt>
|
||||
</dl>
|
||||
<dl><dt><strong>__suppress_context__</strong></dt>
|
||||
<dl ><dt ><strong >__suppress_context__</strong></dt>
|
||||
</dl>
|
||||
<dl><dt><strong>__traceback__</strong></dt>
|
||||
<dl ><dt ><strong >__traceback__</strong></dt>
|
||||
</dl>
|
||||
<dl><dt><strong>args</strong></dt>
|
||||
<dl ><dt ><strong >args</strong></dt>
|
||||
</dl>
|
||||
</td></tr></table></td></tr></table><p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#eeaa77">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
|
||||
</td></tr></table> <p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#ffc8d8" >
|
||||
<td colspan="3" valign="bottom" > <br />
|
||||
<font color="#000000" face="helvetica, arial" ><a name="StoppableThread" >class <strong >StoppableThread</strong></a>(<a href="threading.html#Thread" >threading.Thread</a>)</font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><dl><dt><a name="-func_timeout"><strong>func_timeout</strong></a>(timeout, func, args=(), kwargs=None)</dt><dd><tt>func_timeout - Runs the given function for up to #timeout# seconds.<br>
|
||||
<br>
|
||||
Raises any exceptions #func# would raise, returns what #func# would return (unless timeout is exceeded), in which case it raises <a href="#FunctionTimedOut">FunctionTimedOut</a><br>
|
||||
<br>
|
||||
@param timeout <float> - Maximum number of seconds to run #func# before terminating<br>
|
||||
@param func <function> - The function to call<br>
|
||||
@param args <tuple> - Any ordered arguments to pass to the function<br>
|
||||
@param kwargs <dict/None> - Keyword arguments to pass to the function.<br>
|
||||
<br>
|
||||
@raises - <a href="#FunctionTimedOut">FunctionTimedOut</a> if #timeout# is exceeded, otherwise anything #func# could raise will be raised<br>
|
||||
<br>
|
||||
If the timeout is exceeded, <a href="#FunctionTimedOut">FunctionTimedOut</a> will be raised within the context of the called function every two seconds until it terminates,<br>
|
||||
but will not block the calling thread (a new thread will be created to perform the join). If possible, you should try/except <a href="#FunctionTimedOut">FunctionTimedOut</a><br>
|
||||
to return cleanly, but in most cases it will 'just work'.<br>
|
||||
<br>
|
||||
Be careful of code like:<br>
|
||||
def myfunc():<br>
|
||||
while True:<br>
|
||||
try:<br>
|
||||
dosomething()<br>
|
||||
except Exception:<br>
|
||||
continue<br>
|
||||
<br>
|
||||
because it will never terminate.<br>
|
||||
<br>
|
||||
<tr bgcolor="#ffc8d8" ><td rowspan="2" ><tt > </tt></td>
|
||||
<td colspan="2" ><tt ><a href="#StoppableThread" >StoppableThread</a>(group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)<br />
|
||||
<br />
|
||||
<a href="#StoppableThread" >StoppableThread</a> - A thread that can be stopped by forcing an exception in the execution context.<br />
|
||||
<br />
|
||||
This works both to interrupt code that is in C or in python code, at either the next call to a python function,<br />
|
||||
or the next line in python code.<br />
|
||||
<br />
|
||||
It is recommended that if you call stop ( @see <a href="#StoppableThread" >StoppableThread</a>.stop ) that you use an exception that inherits <a href="builtins.html#BaseException" >BaseException</a>, to ensure it likely isn't caught.<br />
|
||||
<br />
|
||||
Also, beware unmarked exception handlers in your code. Code like this:<br />
|
||||
<br />
|
||||
while True:<br />
|
||||
try:<br />
|
||||
doSomething()<br />
|
||||
except:<br />
|
||||
continue<br />
|
||||
<br />
|
||||
will never be able to abort, because the exception you raise is immediately caught.<br />
|
||||
<br />
|
||||
The exception is raised over and over, with a specifed delay (default 2.0 seconds)<br /> </tt></td></tr>
|
||||
<tr ><td > </td>
|
||||
<td width="100%" ><dl ><dt >Method resolution order:</dt>
|
||||
<dd ><a href="func_timeout.StoppableThread.html#StoppableThread" >StoppableThread</a></dd>
|
||||
<dd ><a href="threading.html#Thread" >threading.Thread</a></dd>
|
||||
<dd ><a href="builtins.html#object" >builtins.object</a></dd>
|
||||
</dl>
|
||||
<hr />
|
||||
Methods defined here:<br />
|
||||
<dl ><dt ><a name="StoppableThread-stop" ><strong >stop</strong></a>(self, exception, raiseEvery=2.0)</dt><dd ><tt >Stops the thread by raising a given exception.<br />
|
||||
<br />
|
||||
@param exception <Exception type> - Exception to throw. Likely, you want to use something<br />
|
||||
<br />
|
||||
that inherits from <a href="builtins.html#BaseException" >BaseException</a> (so except Exception as e: continue; isn't a problem)<br />
|
||||
<br />
|
||||
This should be a class/type, NOT an instance, i.e. MyExceptionType not MyExceptionType()<br />
|
||||
<br />
|
||||
<br />
|
||||
@param raiseEvery <float> Default 2.0 - We will keep raising this exception every #raiseEvery seconds,<br />
|
||||
<br />
|
||||
until the thread terminates.<br />
|
||||
<br />
|
||||
If your code traps a specific exception type, this will allow you #raiseEvery seconds to cleanup before exit.<br />
|
||||
<br />
|
||||
If you're calling third-party code you can't control, which catches <a href="builtins.html#BaseException" >BaseException</a>, set this to a low number<br />
|
||||
<br />
|
||||
to break out of their exception handler.<br />
|
||||
<br />
|
||||
<br />
|
||||
@return <None></tt></dd></dl>
|
||||
|
||||
<hr />
|
||||
Methods inherited from <a href="threading.html#Thread" >threading.Thread</a>:<br />
|
||||
<dl ><dt ><a name="StoppableThread-__init__" ><strong >__init__</strong></a>(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)</dt><dd ><tt >This constructor should always be called with keyword arguments. Arguments are:<br />
|
||||
<br />
|
||||
*group* should be None; reserved for future extension when a ThreadGroup<br />
|
||||
class is implemented.<br />
|
||||
<br />
|
||||
*target* is the callable object to be invoked by the <a href="#StoppableThread-run" >run</a>()<br />
|
||||
method. Defaults to None, meaning nothing is called.<br />
|
||||
<br />
|
||||
*name* is the thread name. By default, a unique name is constructed of<br />
|
||||
the form "<a href="threading.html#Thread" >Thread</a>-N" where N is a small decimal number.<br />
|
||||
<br />
|
||||
*args* is the argument tuple for the target invocation. Defaults to ().<br />
|
||||
<br />
|
||||
*kwargs* is a dictionary of keyword arguments for the target<br />
|
||||
invocation. Defaults to {}.<br />
|
||||
<br />
|
||||
If a subclass overrides the constructor, it must make sure to invoke<br />
|
||||
the base class constructor (<a href="threading.html#Thread" >Thread</a>.<a href="#StoppableThread-__init__" >__init__</a>()) before doing anything<br />
|
||||
else to the thread.</tt></dd></dl>
|
||||
|
||||
<dl ><dt ><a name="StoppableThread-__repr__" ><strong >__repr__</strong></a>(self)</dt><dd ><tt >Return repr(self).</tt></dd></dl>
|
||||
|
||||
<dl ><dt ><a name="StoppableThread-getName" ><strong >getName</strong></a>(self)</dt></dl>
|
||||
|
||||
<dl ><dt ><a name="StoppableThread-isAlive" ><strong >isAlive</strong></a>(self)</dt><dd ><tt >Return whether the thread is alive.<br />
|
||||
<br />
|
||||
This method is deprecated, use <a href="#StoppableThread-is_alive" >is_alive</a>() instead.</tt></dd></dl>
|
||||
|
||||
<dl ><dt ><a name="StoppableThread-isDaemon" ><strong >isDaemon</strong></a>(self)</dt></dl>
|
||||
|
||||
<dl ><dt ><a name="StoppableThread-is_alive" ><strong >is_alive</strong></a>(self)</dt><dd ><tt >Return whether the thread is alive.<br />
|
||||
<br />
|
||||
This method returns True just before the <a href="#StoppableThread-run" >run</a>() method starts until just<br />
|
||||
after the <a href="#StoppableThread-run" >run</a>() method terminates. The module function enumerate()<br />
|
||||
returns a list of all alive threads.</tt></dd></dl>
|
||||
|
||||
<dl ><dt ><a name="StoppableThread-join" ><strong >join</strong></a>(self, timeout=None)</dt><dd ><tt >Wait until the thread terminates.<br />
|
||||
<br />
|
||||
This blocks the calling thread until the thread whose <a href="#StoppableThread-join" >join</a>() method is<br />
|
||||
called terminates -- either normally or through an unhandled exception<br />
|
||||
or until the optional timeout occurs.<br />
|
||||
<br />
|
||||
When the timeout argument is present and not None, it should be a<br />
|
||||
floating point number specifying a timeout for the operation in seconds<br />
|
||||
(or fractions thereof). As <a href="#StoppableThread-join" >join</a>() always returns None, you must call<br />
|
||||
<a href="#StoppableThread-is_alive" >is_alive</a>() after <a href="#StoppableThread-join" >join</a>() to decide whether a timeout happened -- if the<br />
|
||||
thread is still alive, the <a href="#StoppableThread-join" >join</a>() call timed out.<br />
|
||||
<br />
|
||||
When the timeout argument is not present or None, the operation will<br />
|
||||
block until the thread terminates.<br />
|
||||
<br />
|
||||
A thread can be <a href="#StoppableThread-join" >join</a>()ed many times.<br />
|
||||
<br />
|
||||
<a href="#StoppableThread-join" >join</a>() raises a RuntimeError if an attempt is made to join the current<br />
|
||||
thread as that would cause a deadlock. It is also an error to <a href="#StoppableThread-join" >join</a>() a<br />
|
||||
thread before it has been started and attempts to do so raises the same<br />
|
||||
exception.</tt></dd></dl>
|
||||
|
||||
<dl ><dt ><a name="StoppableThread-run" ><strong >run</strong></a>(self)</dt><dd ><tt >Method representing the thread's activity.<br />
|
||||
<br />
|
||||
You may override this method in a subclass. The standard <a href="#StoppableThread-run" >run</a>() method<br />
|
||||
invokes the callable object passed to the object's constructor as the<br />
|
||||
target argument, if any, with sequential and keyword arguments taken<br />
|
||||
from the args and kwargs arguments, respectively.</tt></dd></dl>
|
||||
|
||||
<dl ><dt ><a name="StoppableThread-setDaemon" ><strong >setDaemon</strong></a>(self, daemonic)</dt></dl>
|
||||
|
||||
<dl ><dt ><a name="StoppableThread-setName" ><strong >setName</strong></a>(self, name)</dt></dl>
|
||||
|
||||
<dl ><dt ><a name="StoppableThread-start" ><strong >start</strong></a>(self)</dt><dd ><tt >Start the thread's activity.<br />
|
||||
<br />
|
||||
It must be called at most once per thread object. It arranges for the<br />
|
||||
object's <a href="#StoppableThread-run" >run</a>() method to be invoked in a separate thread of control.<br />
|
||||
<br />
|
||||
This method will raise a RuntimeError if called more than once on the<br />
|
||||
same thread object.</tt></dd></dl>
|
||||
|
||||
<hr />
|
||||
Data descriptors inherited from <a href="threading.html#Thread" >threading.Thread</a>:<br />
|
||||
<dl ><dt ><strong >__dict__</strong></dt>
|
||||
<dd ><tt >dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl ><dt ><strong >__weakref__</strong></dt>
|
||||
<dd ><tt >list of weak references to the object (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl ><dt ><strong >daemon</strong></dt>
|
||||
<dd ><tt >A boolean value indicating whether this thread is a daemon thread.<br />
|
||||
<br />
|
||||
This must be set before start() is called, otherwise RuntimeError is<br />
|
||||
raised. Its initial value is inherited from the creating thread; the<br />
|
||||
main thread is not a daemon thread and therefore all threads created in<br />
|
||||
the main thread default to daemon = False.<br />
|
||||
<br />
|
||||
The entire Python program exits when no alive non-daemon threads are<br />
|
||||
left.</tt></dd>
|
||||
</dl>
|
||||
<dl ><dt ><strong >ident</strong></dt>
|
||||
<dd ><tt >Thread identifier of this thread or None if it has not been started.<br />
|
||||
<br />
|
||||
This is a nonzero integer. See the get_ident() function. Thread<br />
|
||||
identifiers may be recycled when a thread exits and another thread is<br />
|
||||
created. The identifier is available even after the thread has exited.</tt></dd>
|
||||
</dl>
|
||||
<dl ><dt ><strong >name</strong></dt>
|
||||
<dd ><tt >A string used for identification purposes only.<br />
|
||||
<br />
|
||||
It has no semantics. Multiple threads may be given the same name. The<br />
|
||||
initial name is set by the constructor.</tt></dd>
|
||||
</dl>
|
||||
</td></tr></table></p></p></td></tr></table><p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#eeaa77" >
|
||||
<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>
|
||||
<td width="100%" ><dl ><dt ><a name="-func_set_timeout" ><strong >func_set_timeout</strong></a>(timeout, allowOverride=False)</dt><dd ><tt >func_set_timeout - Decorator to run a function with a given/calculated timeout (max execution time).<br />
|
||||
Optionally (if #allowOverride is True), adds a paramater, "forceTimeout", to the<br />
|
||||
function which, if provided, will override the default timeout for that invocation.<br />
|
||||
<br />
|
||||
If #timeout is provided as a lambda/function, it will be called<br />
|
||||
prior to each invocation of the decorated function to calculate the timeout to be used<br />
|
||||
for that call, based on the arguments passed to the decorated function.<br />
|
||||
<br />
|
||||
For example, you may have a "processData" function whose execution time<br />
|
||||
depends on the number of "data" elements, so you may want a million elements to have a<br />
|
||||
much higher timeout than seven elements.)<br />
|
||||
<br />
|
||||
If #allowOverride is True AND a kwarg of "forceTimeout" is passed to the wrapped function, that timeout<br />
|
||||
will be used for that single call.<br />
|
||||
<br />
|
||||
@param timeout <float OR lambda/function> -<br />
|
||||
<br />
|
||||
**If float:**<br />
|
||||
Default number of seconds max to allow function to execute<br />
|
||||
before throwing <a href="#FunctionTimedOut" >FunctionTimedOut</a><br />
|
||||
<br />
|
||||
**If lambda/function:<br />
|
||||
<br />
|
||||
If a function/lambda is provided, it will be called for every<br />
|
||||
invocation of the decorated function (unless #allowOverride=True and "forceTimeout" was passed)<br />
|
||||
to determine the timeout to use based on the arguments to the decorated function.<br />
|
||||
<br />
|
||||
The arguments as passed into the decorated function will be passed to this function.<br />
|
||||
They either must match exactly to what the decorated function has, OR<br />
|
||||
if you prefer to get the *args (list of ordered args) and **kwargs ( key : value keyword args form),<br />
|
||||
define your calculate function like:<br />
|
||||
<br />
|
||||
def calculateTimeout(*args, **kwargs):<br />
|
||||
...<br />
|
||||
<br />
|
||||
or lambda like:<br />
|
||||
<br />
|
||||
calculateTimeout = lambda *args, **kwargs : ...<br />
|
||||
<br />
|
||||
otherwise the args to your calculate function should match exactly the decorated function.<br />
|
||||
<br />
|
||||
<br />
|
||||
@param allowOverride <bool> Default False, if True adds a keyword argument to the decorated function,<br />
|
||||
"forceTimeout" which, if provided, will override the #timeout. If #timeout was provided as a lambda / function, it<br />
|
||||
will not be called.<br />
|
||||
<br />
|
||||
@throws <a href="#FunctionTimedOut" >FunctionTimedOut</a> If time alloted passes without function returning naturally<br />
|
||||
<br />
|
||||
@see func_timeout</tt></dd></dl>
|
||||
<dl ><dt ><a name="-func_timeout" ><strong >func_timeout</strong></a>(timeout, func, args=(), kwargs=None)</dt><dd ><tt >func_timeout - Runs the given function for up to #timeout# seconds.<br />
|
||||
<br />
|
||||
Raises any exceptions #func# would raise, returns what #func# would return (unless timeout is exceeded), in which case it raises <a href="#FunctionTimedOut" >FunctionTimedOut</a><br />
|
||||
<br />
|
||||
@param timeout <float> - Maximum number of seconds to run #func# before terminating<br />
|
||||
<br />
|
||||
@param func <function> - The function to call<br />
|
||||
<br />
|
||||
@param args <tuple> - Any ordered arguments to pass to the function<br />
|
||||
<br />
|
||||
@param kwargs <dict/None> - Keyword arguments to pass to the function.<br />
|
||||
<br />
|
||||
<br />
|
||||
@raises - <a href="#FunctionTimedOut" >FunctionTimedOut</a> if #timeout# is exceeded, otherwise anything #func# could raise will be raised<br />
|
||||
<br />
|
||||
If the timeout is exceeded, <a href="#FunctionTimedOut" >FunctionTimedOut</a> will be raised within the context of the called function every two seconds until it terminates,<br />
|
||||
but will not block the calling thread (a new thread will be created to perform the join). If possible, you should try/except <a href="#FunctionTimedOut" >FunctionTimedOut</a><br />
|
||||
to return cleanly, but in most cases it will 'just work'.<br />
|
||||
<br />
|
||||
@return - The return value that #func# gives</tt></dd></dl>
|
||||
</td></tr></table><p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#55aa55">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
</td></tr></table><p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#55aa55" >
|
||||
<td colspan="3" valign="bottom" > <br />
|
||||
<font color="#ffffff" face="helvetica, arial" ><big ><strong >Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><strong>__all__</strong> = ('func_timeout', 'FunctionTimedOut')<br>
|
||||
<strong>__version_tuple__</strong> = (3, 0, 1)</td></tr></table>
|
||||
</body></html>
|
||||
<tr ><td bgcolor="#55aa55" ><tt > </tt></td><td > </td>
|
||||
<td width="100%" ><strong >__all__</strong> = ('func_timeout', 'func_set_timeout', 'FunctionTimedOut', 'StoppableThread')<br />
|
||||
<strong >__version_tuple__</strong> = (4, 3, 5)</td></tr></table>
|
||||
</p></p></p></p></body></html>
|
||||
20
doc/func_timeout.py2_raise.html
Normal file
20
doc/func_timeout.py2_raise.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html ><head ><title >Python: module func_timeout.py2_raise</title>
|
||||
<meta charset="utf-8" />
|
||||
</head><body bgcolor="#f0f0f8" >
|
||||
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="heading" >
|
||||
<tr bgcolor="#7799ee" >
|
||||
<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><td align="right" valign="bottom" ><font color="#ffffff" face="helvetica, arial" ><a href="func_timeout.html" >index</a></font></td></tr></table>
|
||||
<p ><tt ># Python2 allows specifying an alternate traceback.</tt></p>
|
||||
<p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#eeaa77" >
|
||||
<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>
|
||||
<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>
|
||||
</p></body></html>
|
||||
26
doc/func_timeout.py3_raise.html
Normal file
26
doc/func_timeout.py3_raise.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html ><head ><title >Python: module func_timeout.py3_raise</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head><body bgcolor="#f0f0f8" >
|
||||
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="heading" >
|
||||
<tr bgcolor="#7799ee" >
|
||||
<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>.py3_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>
|
||||
<p ><tt ># <a href="http://www.python.org/dev/peps/pep-0409/" >PEP 409</a> - Raise with the chained exception context disabled<br />
|
||||
# This, in effect, prevents the "funcwrap" wrapper ( chained<br />
|
||||
# in context to an exception raised here, due to scope )<br />
|
||||
# Only available in python3.3+</tt></p>
|
||||
<p >
|
||||
<table width="100%" cellspacing="0" cellpadding="2" border="0" summary="section" >
|
||||
<tr bgcolor="#eeaa77" >
|
||||
<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>
|
||||
<td width="100%" ><dl ><dt ><a name="-raise_exception" ><strong >raise_exception</strong></a>(exception)</dt><dd ><tt ># <a href="http://www.python.org/dev/peps/pep-0409/" >PEP 409</a> - Raise with the chained exception context disabled<br />
|
||||
# This, in effect, prevents the "funcwrap" wrapper ( chained<br />
|
||||
# in context to an exception raised here, due to scope )<br />
|
||||
# Only available in python3.3+</tt></dd></dl>
|
||||
</td></tr></table>
|
||||
</p></body></html>
|
||||
1
doc/index.html
Symbolic link
1
doc/index.html
Symbolic link
@ -0,0 +1 @@
|
||||
func_timeout.html
|
||||
@ -1,5 +1,5 @@
|
||||
'''
|
||||
Copyright (c) 2016, 2017 Tim Savannah All Rights Reserved.
|
||||
Copyright (c) 2016, 2017, 2019 Timothy 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
|
||||
@ -8,39 +8,126 @@
|
||||
import os
|
||||
import ctypes
|
||||
import threading
|
||||
import time
|
||||
|
||||
__all__ = ('StoppableThread', 'JoinThread')
|
||||
|
||||
class StoppableThread(threading.Thread):
|
||||
'''
|
||||
StoppableThread - A thread that can be stopped by forcing an exception in the execution context.
|
||||
|
||||
This works both to interrupt code that is in C or in python code, at either the next call to a python function,
|
||||
or the next line in python code.
|
||||
|
||||
It is recommended that if you call stop ( @see StoppableThread.stop ) that you use an exception that inherits BaseException, to ensure it likely isn't caught.
|
||||
|
||||
Also, beware unmarked exception handlers in your code. Code like this:
|
||||
|
||||
while True:
|
||||
try:
|
||||
doSomething()
|
||||
except:
|
||||
continue
|
||||
|
||||
will never be able to abort, because the exception you raise is immediately caught.
|
||||
|
||||
The exception is raised over and over, with a specifed delay (default 2.0 seconds)
|
||||
'''
|
||||
|
||||
|
||||
def _stopThread(self, exception):
|
||||
if self.isAlive() is False:
|
||||
def _stopThread(self, exception, raiseEvery=2.0):
|
||||
'''
|
||||
_stopThread - @see StoppableThread.stop
|
||||
'''
|
||||
if self.is_alive() is False:
|
||||
return True
|
||||
|
||||
self._stderr = open(os.devnull, 'w')
|
||||
joinThread = JoinThread(self, exception)
|
||||
|
||||
# Create "joining" thread which will raise the provided exception
|
||||
# on a repeat, until the thread stops.
|
||||
joinThread = JoinThread(self, exception, repeatEvery=raiseEvery)
|
||||
|
||||
# Try to prevent spurrious prints
|
||||
joinThread._stderr = self._stderr
|
||||
joinThread.start()
|
||||
joinThread._stderr = self._stderr
|
||||
|
||||
|
||||
def stop(self, exception, raiseEvery=2.0):
|
||||
'''
|
||||
Stops the thread by raising a given exception.
|
||||
|
||||
@param exception <Exception type> - Exception to throw. Likely, you want to use something
|
||||
|
||||
that inherits from BaseException (so except Exception as e: continue; isn't a problem)
|
||||
|
||||
This should be a class/type, NOT an instance, i.e. MyExceptionType not MyExceptionType()
|
||||
|
||||
|
||||
@param raiseEvery <float> Default 2.0 - We will keep raising this exception every #raiseEvery seconds,
|
||||
|
||||
until the thread terminates.
|
||||
|
||||
If your code traps a specific exception type, this will allow you #raiseEvery seconds to cleanup before exit.
|
||||
|
||||
If you're calling third-party code you can't control, which catches BaseException, set this to a low number
|
||||
|
||||
to break out of their exception handler.
|
||||
|
||||
|
||||
@return <None>
|
||||
'''
|
||||
return self._stopThread(exception, raiseEvery)
|
||||
|
||||
|
||||
class JoinThread(threading.Thread):
|
||||
'''
|
||||
JoinThread - The workhouse that stops the StoppableThread
|
||||
JoinThread - The workhouse that stops the StoppableThread.
|
||||
|
||||
Takes an exception, and upon being started immediately raises that exception in the current context
|
||||
of the thread's execution (so next line of python gets it, or next call to a python api function in C code ).
|
||||
|
||||
@see StoppableThread for more details
|
||||
'''
|
||||
|
||||
def __init__(self, otherThread, exception):
|
||||
def __init__(self, otherThread, exception, repeatEvery=2.0):
|
||||
'''
|
||||
__init__ - Create a JoinThread (don't forget to call .start() ! )
|
||||
|
||||
@param otherThread <threading.Thread> - A thread
|
||||
|
||||
@param exception <BaseException> - An exception. Should be a BaseException, to prevent "catch Exception as e: continue" type code
|
||||
from never being terminated. If such code is unavoidable, you can try setting #repeatEvery to a very low number, like .00001,
|
||||
and it will hopefully raise within the context of the catch, and be able to break free.
|
||||
|
||||
@param repeatEvery <float> Default 2.0 - After starting, the given exception is immediately raised. Then, every #repeatEvery seconds,
|
||||
it is raised again, until the thread terminates.
|
||||
'''
|
||||
threading.Thread.__init__(self)
|
||||
self.otherThread = otherThread
|
||||
self.exception = exception
|
||||
self.repeatEvery = repeatEvery
|
||||
self.daemon = True
|
||||
|
||||
def run(self):
|
||||
while self.otherThread.isAlive():
|
||||
'''
|
||||
run - The thread main. Will attempt to stop and join the attached thread.
|
||||
'''
|
||||
|
||||
# Try to silence default exception printing.
|
||||
self.otherThread._Thread__stderr = self._stderr
|
||||
if hasattr(self.otherThread, '_Thread__stop'):
|
||||
# If py2, call this first to start thread termination cleanly.
|
||||
# Python3 does not need such ( nor does it provide.. )
|
||||
self.otherThread._Thread__stop()
|
||||
while self.otherThread.is_alive():
|
||||
# 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))
|
||||
self.otherThread.join(2)
|
||||
self.otherThread.join(self.repeatEvery)
|
||||
|
||||
try:
|
||||
self._stderr.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
# vim: set ts=4 sw=4 expandtab :
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
'''
|
||||
Copyright (c) 2016, 2017 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
|
||||
LICENSE, otherwise it is available at https://github.com/kata198/func_timeout/LICENSE
|
||||
'''
|
||||
|
||||
|
||||
__version__ = '3.1.0'
|
||||
__version_tuple__ = (3, 1, 0)
|
||||
__version__ = '4.3.5'
|
||||
__version_tuple__ = (4, 3, 5)
|
||||
|
||||
__all__ = ('func_timeout', 'FunctionTimedOut')
|
||||
__all__ = ('func_timeout', 'func_set_timeout', 'FunctionTimedOut', 'StoppableThread')
|
||||
|
||||
from .exceptions import FunctionTimedOut
|
||||
from .dafunc import func_timeout
|
||||
from .dafunc import func_timeout, func_set_timeout
|
||||
from .StoppableThread import StoppableThread
|
||||
|
||||
@ -1,15 +1,35 @@
|
||||
|
||||
# vim: set ts=4 sw=4 expandtab :
|
||||
|
||||
'''
|
||||
Copyright (c) 2016 Tim Savannah All Rights Reserved.
|
||||
Copyright (c) 2016, 2017 Tim Savannah All Rights Reserved.
|
||||
|
||||
Licensed under the Lesser GNU Public License Version 3, LGPLv3. You should have recieved a copy of this with the source distribution as
|
||||
LICENSE, otherwise it is available at https://github.com/kata198/func_timeout/LICENSE
|
||||
'''
|
||||
|
||||
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')
|
||||
|
||||
|
||||
def func_timeout(timeout, func, args=(), kwargs=None):
|
||||
'''
|
||||
func_timeout - Runs the given function for up to #timeout# seconds.
|
||||
@ -17,26 +37,20 @@ def func_timeout(timeout, func, args=(), kwargs=None):
|
||||
Raises any exceptions #func# would raise, returns what #func# would return (unless timeout is exceeded), in which case it raises FunctionTimedOut
|
||||
|
||||
@param timeout <float> - Maximum number of seconds to run #func# before terminating
|
||||
|
||||
@param func <function> - The function to call
|
||||
|
||||
@param args <tuple> - Any ordered arguments to pass to the function
|
||||
|
||||
@param kwargs <dict/None> - Keyword arguments to pass to the function.
|
||||
|
||||
|
||||
@raises - FunctionTimedOut if #timeout# is exceeded, otherwise anything #func# could raise will be raised
|
||||
|
||||
If the timeout is exceeded, FunctionTimedOut will be raised within the context of the called function every two seconds until it terminates,
|
||||
but will not block the calling thread (a new thread will be created to perform the join). If possible, you should try/except FunctionTimedOut
|
||||
to return cleanly, but in most cases it will 'just work'.
|
||||
|
||||
Be careful of code like:
|
||||
def myfunc():
|
||||
while True:
|
||||
try:
|
||||
dosomething()
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
because it will never terminate.
|
||||
|
||||
@return - The return value that #func# gives
|
||||
'''
|
||||
|
||||
@ -52,10 +66,18 @@ def func_timeout(timeout, func, args=(), kwargs=None):
|
||||
def funcwrap(args2, kwargs2):
|
||||
try:
|
||||
ret.append( func(*args2, **kwargs2) )
|
||||
except FunctionTimedOut:
|
||||
# Don't print traceback to stderr if we time out
|
||||
pass
|
||||
except Exception as e:
|
||||
exc_info = sys.exc_info()
|
||||
if isStopped is False:
|
||||
# Don't capture stopping exception
|
||||
exception.append(e)
|
||||
# Assemble the alternate traceback, excluding this function
|
||||
# from the trace (by going to next frame)
|
||||
# Pytohn3 reads native from __traceback__,
|
||||
# python2 has a different form for "raise"
|
||||
e.__traceback__ = exc_info[2].tb_next
|
||||
exception.append( e )
|
||||
|
||||
thread = StoppableThread(target=funcwrap, args=(args, kwargs))
|
||||
thread.daemon = True
|
||||
@ -64,17 +86,151 @@ def func_timeout(timeout, func, args=(), kwargs=None):
|
||||
thread.join(timeout)
|
||||
|
||||
stopException = None
|
||||
if thread.isAlive():
|
||||
if thread.is_alive():
|
||||
isStopped = True
|
||||
stopException = FunctionTimedOut
|
||||
|
||||
class FunctionTimedOutTempType(FunctionTimedOut):
|
||||
def __init__(self):
|
||||
return FunctionTimedOut.__init__(self, '', timeout, func, args, kwargs)
|
||||
|
||||
FunctionTimedOutTemp = type('FunctionTimedOut' + str( hash( "%d_%d_%d_%d" %(id(timeout), id(func), id(args), id(kwargs))) ), FunctionTimedOutTempType.__bases__, dict(FunctionTimedOutTempType.__dict__))
|
||||
|
||||
stopException = FunctionTimedOutTemp
|
||||
thread._stopThread(stopException)
|
||||
thread.join(.1)
|
||||
raise FunctionTimedOut('Function %s (args=%s) (kwargs=%s) timed out after %f seconds.\n' %(func.__name__, str(args), str(kwargs), timeout))
|
||||
thread.join(min(.1, timeout / 50.0))
|
||||
raise FunctionTimedOut('', timeout, func, args, kwargs)
|
||||
else:
|
||||
# We can still cleanup the thread here..
|
||||
# Still give a timeout... just... cuz..
|
||||
thread.join(.5)
|
||||
|
||||
if exception:
|
||||
raise exception[0]
|
||||
raise_exception(exception)
|
||||
|
||||
if ret:
|
||||
return ret[0]
|
||||
|
||||
|
||||
def func_set_timeout(timeout, allowOverride=False):
|
||||
'''
|
||||
func_set_timeout - Decorator to run a function with a given/calculated timeout (max execution time).
|
||||
Optionally (if #allowOverride is True), adds a paramater, "forceTimeout", to the
|
||||
function which, if provided, will override the default timeout for that invocation.
|
||||
|
||||
If #timeout is provided as a lambda/function, it will be called
|
||||
prior to each invocation of the decorated function to calculate the timeout to be used
|
||||
for that call, based on the arguments passed to the decorated function.
|
||||
|
||||
For example, you may have a "processData" function whose execution time
|
||||
depends on the number of "data" elements, so you may want a million elements to have a
|
||||
much higher timeout than seven elements.)
|
||||
|
||||
If #allowOverride is True AND a kwarg of "forceTimeout" is passed to the wrapped function, that timeout
|
||||
will be used for that single call.
|
||||
|
||||
@param timeout <float OR lambda/function> -
|
||||
|
||||
**If float:**
|
||||
Default number of seconds max to allow function to execute
|
||||
before throwing FunctionTimedOut
|
||||
|
||||
**If lambda/function:
|
||||
|
||||
If a function/lambda is provided, it will be called for every
|
||||
invocation of the decorated function (unless #allowOverride=True and "forceTimeout" was passed)
|
||||
to determine the timeout to use based on the arguments to the decorated function.
|
||||
|
||||
The arguments as passed into the decorated function will be passed to this function.
|
||||
They either must match exactly to what the decorated function has, OR
|
||||
if you prefer to get the *args (list of ordered args) and **kwargs ( key : value keyword args form),
|
||||
define your calculate function like:
|
||||
|
||||
def calculateTimeout(*args, **kwargs):
|
||||
...
|
||||
|
||||
or lambda like:
|
||||
|
||||
calculateTimeout = lambda *args, **kwargs : ...
|
||||
|
||||
otherwise the args to your calculate function should match exactly the decorated function.
|
||||
|
||||
|
||||
@param allowOverride <bool> Default False, if True adds a keyword argument to the decorated function,
|
||||
"forceTimeout" which, if provided, will override the #timeout. If #timeout was provided as a lambda / function, it
|
||||
will not be called.
|
||||
|
||||
@throws FunctionTimedOut If time alloted passes without function returning naturally
|
||||
|
||||
@see func_timeout
|
||||
'''
|
||||
# Try to be as efficent as possible... don't compare the args more than once
|
||||
|
||||
# Helps closure issue on some versions of python
|
||||
defaultTimeout = copy.copy(timeout)
|
||||
|
||||
isTimeoutAFunction = bool( issubclass(timeout.__class__, (types.FunctionType, types.MethodType, types.LambdaType, types.BuiltinFunctionType, types.BuiltinMethodType) ) )
|
||||
|
||||
if not isTimeoutAFunction:
|
||||
if not issubclass(timeout.__class__, (float, int)):
|
||||
try:
|
||||
timeout = float(timeout)
|
||||
except:
|
||||
raise ValueError('timeout argument must be a float/int for number of seconds, or a function/lambda which gets passed the function arguments and returns a calculated timeout (as float or int). Passed type: < %s > is not of any of these, and cannot be converted to a float.' %( timeout.__class__.__name__, ))
|
||||
|
||||
|
||||
if not allowOverride and not isTimeoutAFunction:
|
||||
# Only defaultTimeout provided. Simple function wrapper
|
||||
def _function_decorator(func):
|
||||
|
||||
return wraps(func)(lambda *args, **kwargs : func_timeout(defaultTimeout, func, args=args, kwargs=kwargs))
|
||||
|
||||
# def _function_wrapper(*args, **kwargs):
|
||||
# return func_timeout(defaultTimeout, func, args=args, kwargs=kwargs)
|
||||
# return _function_wrapper
|
||||
return _function_decorator
|
||||
|
||||
if not isTimeoutAFunction:
|
||||
# allowOverride is True and timeout is not a function. Simple conditional on every call
|
||||
def _function_decorator(func):
|
||||
def _function_wrapper(*args, **kwargs):
|
||||
if 'forceTimeout' in kwargs:
|
||||
useTimeout = kwargs.pop('forceTimeout')
|
||||
else:
|
||||
useTimeout = defaultTimeout
|
||||
|
||||
return func_timeout(useTimeout, func, args=args, kwargs=kwargs)
|
||||
|
||||
return wraps(func)(_function_wrapper)
|
||||
return _function_decorator
|
||||
|
||||
|
||||
# At this point, timeout IS known to be a function.
|
||||
timeoutFunction = timeout
|
||||
|
||||
if allowOverride:
|
||||
# Could use a lambda here... but want traceback to highlight the calculate function,
|
||||
# and not the invoked function
|
||||
def _function_decorator(func):
|
||||
def _function_wrapper(*args, **kwargs):
|
||||
if 'forceTimeout' in kwargs:
|
||||
useTimeout = kwargs.pop('forceTimeout')
|
||||
else:
|
||||
useTimeout = timeoutFunction(*args, **kwargs)
|
||||
|
||||
return func_timeout(useTimeout, func, args=args, kwargs=kwargs)
|
||||
|
||||
return wraps(func)(_function_wrapper)
|
||||
return _function_decorator
|
||||
|
||||
# Cannot override, and calculate timeout function
|
||||
def _function_decorator(func):
|
||||
def _function_wrapper(*args, **kwargs):
|
||||
useTimeout = timeoutFunction(*args, **kwargs)
|
||||
|
||||
return func_timeout(useTimeout, func, args=args, kwargs=kwargs)
|
||||
|
||||
return wraps(func)(_function_wrapper)
|
||||
return _function_decorator
|
||||
|
||||
|
||||
# vim: set ts=4 sw=4 expandtab :
|
||||
|
||||
@ -1,13 +1,98 @@
|
||||
'''
|
||||
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
|
||||
LICENSE, otherwise it is available at https://github.com/kata198/func_timeout/LICENSE
|
||||
'''
|
||||
|
||||
__all__ = ('FunctionTimedOut', 'RETRY_SAME_TIMEOUT')
|
||||
|
||||
RETRY_SAME_TIMEOUT = 'RETRY_SAME_TIMEOUT'
|
||||
|
||||
class FunctionTimedOut(BaseException):
|
||||
'''
|
||||
FunctionTimedOut - Exception raised when a function times out
|
||||
|
||||
@property timedOutAfter - Number of seconds before timeout was triggered
|
||||
|
||||
@property timedOutFunction - Function called which timed out
|
||||
@property timedOutArgs - Ordered args to function
|
||||
@property timedOutKwargs - Keyword args to function
|
||||
|
||||
@method retry - Retries the function with same arguments, with option to run with original timeout, no timeout, or a different
|
||||
explicit timeout. @see FunctionTimedOut.retry
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
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.timedOutFunction = timedOutFunction
|
||||
self.timedOutArgs = timedOutArgs
|
||||
self.timedOutKwargs = timedOutKwargs
|
||||
|
||||
if not msg:
|
||||
msg = self.getMsg()
|
||||
|
||||
BaseException.__init__(self, msg)
|
||||
|
||||
self.msg = msg
|
||||
|
||||
|
||||
def getMsg(self):
|
||||
'''
|
||||
getMsg - Generate a default message based on parameters to FunctionTimedOut exception'
|
||||
|
||||
@return <str> - Message
|
||||
'''
|
||||
# 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):
|
||||
'''
|
||||
retry - Retry the timed-out function with same arguments.
|
||||
|
||||
@param timeout <float/RETRY_SAME_TIMEOUT/None> Default RETRY_SAME_TIMEOUT
|
||||
|
||||
If RETRY_SAME_TIMEOUT : Will retry the function same args, same timeout
|
||||
If a float/int : Will retry the function same args with provided timeout
|
||||
If None : Will retry function same args no timeout
|
||||
|
||||
@return - Returnval from function
|
||||
'''
|
||||
if timeout is None:
|
||||
return self.timedOutFunction(*(self.timedOutArgs), **self.timedOutKwargs)
|
||||
|
||||
from .dafunc import func_timeout
|
||||
|
||||
if timeout == RETRY_SAME_TIMEOUT:
|
||||
timeout = self.timedOutAfter
|
||||
|
||||
return func_timeout(timeout, self.timedOutFunction, args=self.timedOutArgs, kwargs=self.timedOutKwargs)
|
||||
|
||||
5
func_timeout/py2_raise.py
Normal file
5
func_timeout/py2_raise.py
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
# Python2 allows specifying an alternate traceback.
|
||||
def raise_exception(exception):
|
||||
raise exception[0] , None , exception[0].__traceback__
|
||||
7
func_timeout/py3_raise.py
Normal file
7
func_timeout/py3_raise.py
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
# 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
|
||||
59
mkdoc.sh
Executable file
59
mkdoc.sh
Executable file
@ -0,0 +1,59 @@
|
||||
#!/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' ' ')"
|
||||
|
||||
pydoc -w ${ALL_MODS}
|
||||
mv func_timeout*.html doc/
|
||||
pushd doc >/dev/null 2>&1
|
||||
rm -f index.html
|
||||
|
||||
for fname in `echo *.html`;
|
||||
do
|
||||
python <<EOT
|
||||
|
||||
import AdvancedHTMLParser
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
filename = "${fname}"
|
||||
|
||||
parser = AdvancedHTMLParser.AdvancedHTMLParser()
|
||||
parser.parseFile(filename)
|
||||
|
||||
em = parser.filter(tagName='a', href='.')
|
||||
|
||||
if len(em) == 0:
|
||||
sys.exit(0)
|
||||
|
||||
em = em[0]
|
||||
|
||||
em.href = 'func_timeout.html'
|
||||
|
||||
parentNode = em.parentNode
|
||||
|
||||
emIndex = parentNode.children.index(em)
|
||||
|
||||
i = len(parentNode.children) - 1
|
||||
|
||||
while i > emIndex:
|
||||
parentNode.removeChild( parentNode.children[i] )
|
||||
i -= 1
|
||||
|
||||
|
||||
with open(filename, 'wt') as f:
|
||||
f.write(parser.getHTML())
|
||||
|
||||
|
||||
EOT
|
||||
|
||||
|
||||
done
|
||||
|
||||
ln -s func_timeout.html index.html
|
||||
|
||||
popd >/dev/null 2>&1
|
||||
9
setup.py
9
setup.py
@ -20,7 +20,7 @@ if __name__ == '__main__':
|
||||
if dirName and os.getcwd() != dirName:
|
||||
os.chdir(dirName)
|
||||
|
||||
summary = 'Python module which allows you to specify timeouts when calling any existing function'
|
||||
summary = 'Python module which allows you to specify timeouts when calling any existing function. Also provides support for stoppable-threads'
|
||||
|
||||
try:
|
||||
with open('README.rst', 'rt') as f:
|
||||
@ -30,7 +30,7 @@ if __name__ == '__main__':
|
||||
log_description = summary
|
||||
|
||||
setup(name='func_timeout',
|
||||
version='3.1.0',
|
||||
version='4.3.7',
|
||||
packages=['func_timeout'],
|
||||
author='Tim Savannah',
|
||||
author_email='kata198@gmail.com',
|
||||
@ -48,6 +48,11 @@ if __name__ == '__main__':
|
||||
'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',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
||||
]
|
||||
)
|
||||
|
||||
13
testit.py
13
testit.py
@ -1,4 +1,9 @@
|
||||
#!/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
|
||||
import time
|
||||
@ -15,8 +20,16 @@ if __name__ == '__main__':
|
||||
print ( "\tGot Return: %s\n" %(str(func_timeout(4, doit, args=(6,))),) )
|
||||
|
||||
print ( "\nShould time out (exception):" )
|
||||
myException = None
|
||||
try:
|
||||
print ("\tGot Return: %s\n" %(str(func_timeout(1, doit, kwargs={'howmany' : 16})),))
|
||||
except FunctionTimedOut as e:
|
||||
sys.stderr.write('\tGot Exception: %s\n' %(str(e),))
|
||||
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)), ) )
|
||||
else:
|
||||
sys.stderr.write('Did not get exception before?\n')
|
||||
|
||||
190
tests/FuncTimeoutTests/TestUtils.py
Normal file
190
tests/FuncTimeoutTests/TestUtils.py
Normal file
@ -0,0 +1,190 @@
|
||||
|
||||
# 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
|
||||
|
||||
TestUtils.py - Common functions and types used across unit tests
|
||||
'''
|
||||
|
||||
import copy
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
|
||||
__all__ = ('ARG_NO_DEFAULT', 'getSleepLambda', 'getSleepLambdaWithArgs', 'compareTimes')
|
||||
|
||||
class ARG_NO_DEFAULT_TYPE(object):
|
||||
|
||||
def __eq__(self, other):
|
||||
'''
|
||||
__eq__ - Equal operator ( == ). Returns True if both are instances of ARG_NO_DEFAULT_TYPE,
|
||||
or either is the type itself.
|
||||
|
||||
@param other <anything, preferably an ARG_NO_DEFAULT_TYPE instance> - The other item to compare
|
||||
against this item.
|
||||
|
||||
@return <bool> - True if both objects are instances of ARG_NO_DEFAULT_TYPE,
|
||||
or either are the type itself.
|
||||
'''
|
||||
|
||||
# Is self == ARG_NO_DEFAULT_TYPE ever going to be True? Just in case...
|
||||
if issubclass(other.__class__, ARG_NO_DEFAULT_TYPE) or (other == ARG_NO_DEFAULT_TYPE or self == ARG_NO_DEFAULT_TYPE):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
'''
|
||||
__ne__ - Not-equal operator ( != ). Equivilant to not ==.
|
||||
|
||||
@see ARG_NO_DEFAULT_TYPE.__eq__
|
||||
'''
|
||||
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __cmp__(self, other):
|
||||
'''
|
||||
__cmp__ - Perform a "cmp" operation between self and other.
|
||||
|
||||
Added for completeness, like python2 sorting etc.
|
||||
|
||||
@param other <anything> - Another object, preferably one of ARG_NO_DEFAULT_TYPE.
|
||||
|
||||
@return <int> - Returns 0 if the objects are both
|
||||
equal (both instances of ARG_NO_DEFAULT_TYPE),
|
||||
otherwise to prevent recursion in sorting etc,
|
||||
the id (location in memory) is compared.
|
||||
|
||||
'''
|
||||
if self.__eq__(other):
|
||||
return 0
|
||||
|
||||
if id(self) > id(other):
|
||||
return 1
|
||||
|
||||
return -1
|
||||
|
||||
ARG_NO_DEFAULT = ARG_NO_DEFAULT_TYPE()
|
||||
|
||||
def getUniqueID(prefix):
|
||||
uniqueName = prefix + '_' + str(uuid.uuid4().hex)
|
||||
return uniqueName
|
||||
|
||||
def getSleepLambda(sleepTime):
|
||||
'''
|
||||
getSleepLambda - Get a lambda that takes two integer arguments (a, b)
|
||||
and sleeps for a given number of seconds before returning the sum
|
||||
|
||||
@param sleepTime <float> - The number of seconds to sleep
|
||||
|
||||
@return lambda takes two integer argumennts, "a" and "b".
|
||||
|
||||
NOTE: Lambda's are usually to functions, as functions may get their scope/closure overridden
|
||||
|
||||
@see getSleepLambdaWithArgs
|
||||
'''
|
||||
|
||||
# Ensure we don't get a strange reference override on somne versions of python
|
||||
_sleepTime = copy.copy(sleepTime)
|
||||
|
||||
|
||||
|
||||
return eval('''lambda a, b : int(bool(time.sleep(%f))) + a + b''' %(_sleepTime,))
|
||||
|
||||
|
||||
def getSleepLambdaWithArgs(sleepTime, args):
|
||||
'''
|
||||
getSleepLambdaWithArgs - Get a lambda that takes a variable collection of arguments
|
||||
and sleeps for a given number of seconds before returning the sum of arguments
|
||||
|
||||
@param sleepTime <float> - The number of seconds to sleep
|
||||
|
||||
@param args list <tuple < str, (int) > - A list that represents the arguments to
|
||||
the returned lambda. Should be a list of tuples.
|
||||
|
||||
The first tuple element is the name of the parameter. If a second paramater is present,
|
||||
it will be used as the default value for that argument.
|
||||
|
||||
Keep in mind order is important ( i.e. no args with default following args without default),
|
||||
as they will be used in the order provided.
|
||||
|
||||
All arguments should expect integer values.
|
||||
|
||||
@return lambda with the provided arguments
|
||||
|
||||
NOTE: Lambda's are usually to functions, as functions may get their scope/closure overridden
|
||||
|
||||
@see getSleepLambda
|
||||
'''
|
||||
|
||||
# Ensure we don't get a strange reference override on somne versions of python
|
||||
_sleepTime = copy.copy(sleepTime)
|
||||
|
||||
if not args:
|
||||
raise ValueError('Empty "args" param. See docstring for usage details. Got: ' + repr(args))
|
||||
|
||||
_args = copy.deepcopy(args)
|
||||
|
||||
argStrs = []
|
||||
argNames = []
|
||||
for arg in _args:
|
||||
argNames.append(arg[0])
|
||||
|
||||
if len(arg) == 1:
|
||||
argStrs.append(arg[0])
|
||||
else:
|
||||
argStrs.append('%s=%d' %( arg[0], arg[1] ) )
|
||||
|
||||
argStr = ', '.join(argStrs)
|
||||
|
||||
sumStr = ' + '.join(argNames)
|
||||
|
||||
|
||||
# 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, ) )
|
||||
|
||||
|
||||
def compareTimes(timeEnd, timeStart, cmpTime, roundTo=None, deltaFixed=.05, deltaPct=None):
|
||||
'''
|
||||
compareTimes - Compare two times, with support for max error
|
||||
|
||||
@param timeEnd <float> - End time
|
||||
@param timeStart<float> - Start time
|
||||
|
||||
@param cmpTime <float> - Time to compare against
|
||||
|
||||
@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 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,
|
||||
|
||||
if compareTimes( timeEnd, timeStart, 2, deltaFixed=.05, deltaPct=None) == 0
|
||||
|
||||
@return <int> cmp style, < 0 if time delta is less than #cmpTime
|
||||
= 0 if time delta is equal (taking into account #deltaFixed and #deltaPct)
|
||||
> 0 if time delta is greater than #cmpTime
|
||||
'''
|
||||
|
||||
timeDiff = timeEnd - timeStart
|
||||
|
||||
delta = timeDiff - cmpTime
|
||||
if roundTo is not None:
|
||||
delta = round(delta, roundTo)
|
||||
absDelta = abs(delta)
|
||||
|
||||
if deltaFixed and absDelta <= deltaFixed:
|
||||
return 0
|
||||
|
||||
if deltaPct and absDelta <= (cmpTime * float(deltaPct)):
|
||||
return 0
|
||||
|
||||
return delta
|
||||
|
||||
# vim: set ts=4 sw=4 expandtab :
|
||||
188
tests/FuncTimeoutTests/test_Basic.py
Executable file
188
tests/FuncTimeoutTests/test_Basic.py
Executable file
@ -0,0 +1,188 @@
|
||||
#!/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(2.00)
|
||||
|
||||
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(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),))
|
||||
|
||||
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(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), ))
|
||||
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(1.2)
|
||||
|
||||
expectedResult = 5 + 19
|
||||
|
||||
gotException = False
|
||||
functionTimedOut = None
|
||||
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = func_timeout(.8, 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, .8, 3, deltaFixed=.15) == 0 , 'Expected to wait .8 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, .8, 3, deltaFixed=.15) == 0 , 'Expected retry with no arguments to use same timeout of .8'
|
||||
|
||||
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, 1.2, 3, deltaFixed=.15) == 0 , 'Expected retry with None as timeout to last full length of function'
|
||||
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = functionTimedOut.retry(.55)
|
||||
except FunctionTimedOut:
|
||||
gotException = True
|
||||
finally:
|
||||
endTime = time.time()
|
||||
|
||||
assert gotException , 'Expected to time out after .55 seconds when providing .55'
|
||||
assert compareTimes(endTime, startTime, .55, 3, deltaFixed=.15) == 0 , 'Expected providing .55 would allow timeout of up to .55 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(.85)
|
||||
|
||||
expectedResult = 5 + 19
|
||||
|
||||
gotException = False
|
||||
functionTimedOut = None
|
||||
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = func_timeout(.5, 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) == .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.timedOutArgs == (5, 19) , 'Expected args to equal (5, 19)'
|
||||
assert functionTimedOut.timedOutKwargs == {} , 'Expected timedOutKwargs to equal {}'
|
||||
|
||||
|
||||
def test_instantiateExceptionNoArgs(self):
|
||||
|
||||
gotException = False
|
||||
|
||||
try:
|
||||
exc = FunctionTimedOut()
|
||||
msg = str(exc)
|
||||
msg2 = exc.getMsg()
|
||||
|
||||
except Exception as _e:
|
||||
sys.stderr.write('Got unexpected exception in test_instantiateExceptionNoArgs with no arguments. %s %s\n\n' %(str(type(_e)), str(_e)))
|
||||
gotException = True
|
||||
|
||||
assert gotException is False, 'Expected to be able to create FunctionTimedOut exception without arguments.'
|
||||
|
||||
gotException = False
|
||||
|
||||
try:
|
||||
exc = FunctionTimedOut('test')
|
||||
msg = str(exc)
|
||||
msg2 = str(exc.getMsg())
|
||||
|
||||
except Exception as _e:
|
||||
sys.stderr.write('Got unexpected exception in test_instantiateExceptionNoArgs with fixed message string. %s %s\n\n' %(str(type(_e)), str(_e)))
|
||||
gotException = True
|
||||
|
||||
assert gotException is False , 'Expected to be able to create a FunctionTimedOut exception with a fixed message.'
|
||||
|
||||
# Other forms (providing the function name) are tested elsewhere.
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(subprocess.Popen('GoodTests.py -n1 "%s" %s' %(sys.argv[0], ' '.join(['"%s"' %(arg.replace('"', '\\"'), ) for arg in sys.argv[1:]]) ), shell=True).wait())
|
||||
|
||||
# vim: set ts=4 sw=4 expandtab :
|
||||
441
tests/FuncTimeoutTests/test_Decorator.py
Executable file
441
tests/FuncTimeoutTests/test_Decorator.py
Executable file
@ -0,0 +1,441 @@
|
||||
#!/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
|
||||
|
||||
SLEEP_TIME = 1.25
|
||||
|
||||
def doSleep(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
def calculateSleep(a, b):
|
||||
return a * 1.15
|
||||
|
||||
class TestDecorator(object):
|
||||
'''
|
||||
TestDecorator - Perform tests using the func_set_timeout wrapper
|
||||
'''
|
||||
|
||||
|
||||
|
||||
def test_funcSetTimeout(self):
|
||||
|
||||
@func_set_timeout(SLEEP_TIME)
|
||||
def doSleepFunc(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
expected = SLEEP_TIME * 1.3 + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFunc(SLEEP_TIME * 1.3, 4)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
assert gotException , 'Expected to get exception at sleep time'
|
||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep up to sleep time'
|
||||
|
||||
expected = SLEEP_TIME * .8 + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFunc(SLEEP_TIME * .8, 4)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
assert not gotException , 'Expected not to get exception at 80% 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'
|
||||
|
||||
def test_funcSetTimeoutOverride(self):
|
||||
@func_set_timeout(SLEEP_TIME, allowOverride=True)
|
||||
def doSleepFunc(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
expected = SLEEP_TIME * 1.3 + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFunc(SLEEP_TIME * 1.3, 4, forceTimeout=SLEEP_TIME * 1.2)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
except Exception as e:
|
||||
raise AssertionError('Expected to be able to override forceTimeout when allowOverride=True')
|
||||
endTime = time.time()
|
||||
|
||||
assert gotException , 'Expected to get exception at 130% 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)
|
||||
def doSleepFuncNoOverride(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
gotException = False
|
||||
try:
|
||||
doSleepFuncNoOverride(SLEEP_TIME * 1.3, 4, forceTimeout=SLEEP_TIME * 1.2)
|
||||
except Exception as e:
|
||||
gotException = True
|
||||
except FunctionTimedOut as fte:
|
||||
raise AssertionError('Expected to NOT be able to pass forceTimeout when allowOverride=False, but got FunctionTimedOut exception')
|
||||
|
||||
assert gotException , 'Expected to NOT be able to pass forceTimeout when allowOverride=False, but did not get exception'
|
||||
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFunc(SLEEP_TIME, 4, forceTimeout=SLEEP_TIME * 1.15)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
assert not gotException , 'Expected not to get exception with forced 115% sleep time'
|
||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME'
|
||||
assert result == expected , 'Got wrong result'
|
||||
|
||||
def test_funcSetTimeCalculate(self):
|
||||
|
||||
def calculateSleepOver(a, b):
|
||||
return a * 1.2
|
||||
|
||||
def calculateSleepUnder(a, b):
|
||||
return a * .8
|
||||
|
||||
def calculateSleepOverArgs(*args, **kwargs):
|
||||
return args[0] * 1.2
|
||||
|
||||
def calculateSleepUnderArgs(*args, **kwargs):
|
||||
return args[0] * .8
|
||||
|
||||
@func_set_timeout(calculateSleepOver, allowOverride=False)
|
||||
def doSleepFuncOver(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFuncOver(SLEEP_TIME, 4)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
assert not gotException , 'Expected not to get exception with calculated 120% timeout on sleep time'
|
||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME'
|
||||
assert result == expected , 'Got wrong result'
|
||||
|
||||
|
||||
@func_set_timeout(calculateSleepUnder, allowOverride=False)
|
||||
def doSleepFuncUnder(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFuncUnder(SLEEP_TIME, 4)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
assert gotException , 'Expected to get exception with calculated 80% timeout on 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)
|
||||
def doSleepFuncOverArgs(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFuncOverArgs(SLEEP_TIME, 4)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
assert not gotException , 'Expected not to get exception with calculated 120% timeout on 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'
|
||||
|
||||
@func_set_timeout(calculateSleepUnderArgs, allowOverride=False)
|
||||
def doSleepFuncUnderArgs(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFuncUnderArgs(SLEEP_TIME, 4)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
assert gotException , 'Expected to get exception with calculated 80% timeout on 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 calculateSleepOver(a, b):
|
||||
return a * 1.2
|
||||
|
||||
def calculateSleepUnder(a, b):
|
||||
return a * .8
|
||||
|
||||
def calculateSleepOverArgs(*args, **kwargs):
|
||||
return args[0] * 1.2
|
||||
|
||||
def calculateSleepUnderArgs(*args, **kwargs):
|
||||
return args[0] * .8
|
||||
|
||||
@func_set_timeout(calculateSleepOver, allowOverride=True)
|
||||
def doSleepFuncOver(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFuncOver(SLEEP_TIME, 4)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
assert not gotException , 'Expected not to get exception with calculated 120% timeout on sleep time'
|
||||
assert compareTimes(endTime, startTime, SLEEP_TIME, None, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME'
|
||||
assert result == expected , 'Got wrong result'
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFuncOver(SLEEP_TIME, 4, forceTimeout= SLEEP_TIME * 1.5)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
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, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME with 150% timeout on override'
|
||||
assert result == expected , 'Got wrong result'
|
||||
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFuncOver(SLEEP_TIME, 4, forceTimeout=SLEEP_TIME * .7)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
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, deltaFixed=.15) == 0 , 'Expected to sleep for 70% SLEEP_TIME with 70% timeout on override'
|
||||
|
||||
|
||||
@func_set_timeout(calculateSleepUnder, allowOverride=True)
|
||||
def doSleepFuncUnder(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFuncUnder(SLEEP_TIME, 4)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
assert gotException , 'Expected to get exception with calculated 80% timeout on sleep time'
|
||||
assert compareTimes(endTime, startTime, SLEEP_TIME * .8, None, deltaFixed=.15) == 0 , 'Expected to sleep for 80% SLEEP_TIME'
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFuncUnder(SLEEP_TIME, 4, forceTimeout=SLEEP_TIME * 1.2)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
endTime = time.time()
|
||||
|
||||
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, deltaFixed=.15) == 0 , 'Expected to sleep for SLEEP_TIME with 120% timeout on override'
|
||||
|
||||
|
||||
def test_setFuncTimeoutetry(self):
|
||||
def calculateSleepOver(a, b):
|
||||
return a * 1.2
|
||||
|
||||
def calculateSleepUnder(a, b):
|
||||
return a * .8
|
||||
|
||||
def calculateSleepOverArgs(*args, **kwargs):
|
||||
return args[0] * 1.2
|
||||
|
||||
def calculateSleepUnderArgs(*args, **kwargs):
|
||||
return args[0] * .8
|
||||
|
||||
@func_set_timeout(calculateSleepUnder, allowOverride=True)
|
||||
def doSleepFuncUnder(a, b):
|
||||
time.sleep(a)
|
||||
return a + b
|
||||
|
||||
|
||||
expected = SLEEP_TIME + 4
|
||||
gotException = False
|
||||
functionTimedOut = None
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = doSleepFuncUnder(SLEEP_TIME, 4)
|
||||
except FunctionTimedOut as fte:
|
||||
gotException = True
|
||||
functionTimedOut = fte
|
||||
endTime = time.time()
|
||||
|
||||
assert gotException , 'Expected to get exception with calculated 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
|
||||
startTime = time.time()
|
||||
try:
|
||||
functionTimedOut.retry()
|
||||
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)))
|
||||
endTime = time.time()
|
||||
|
||||
assert gotException , 'Expected to get exception with calculated 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
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = functionTimedOut.retry(None)
|
||||
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)))
|
||||
endTime = time.time()
|
||||
|
||||
assert not gotException , 'Expected to get exception with calculated 80% timeout 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'
|
||||
|
||||
|
||||
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
functionTimedOut.retry(SLEEP_TIME * .6)
|
||||
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)))
|
||||
endTime = time.time()
|
||||
|
||||
assert gotException , 'Expected to get exception with calculated 80% timeout overriden by 60% timeout on retry'
|
||||
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
|
||||
gotException = False
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = functionTimedOut.retry(SLEEP_TIME * 1.5)
|
||||
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)))
|
||||
endTime = time.time()
|
||||
|
||||
assert not gotException , 'Expected to get exception with calculated 80% timeout overriden by 150% timeout on retry'
|
||||
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
|
||||
|
||||
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_nameRetained(self):
|
||||
|
||||
# Case of just timeout
|
||||
@func_set_timeout(2, allowOverride=False)
|
||||
def hello():
|
||||
pass
|
||||
|
||||
assert hello.__name__ == 'hello'
|
||||
|
||||
del hello
|
||||
|
||||
def getTimeoutFunc():
|
||||
return 2
|
||||
|
||||
# Timeout is function
|
||||
@func_set_timeout(getTimeoutFunc, allowOverride=False)
|
||||
def hello2():
|
||||
pass
|
||||
|
||||
assert hello2.__name__ == 'hello2'
|
||||
|
||||
del hello2
|
||||
|
||||
# Now the same with allowOverride=True
|
||||
|
||||
@func_set_timeout(2, allowOverride=True)
|
||||
def hello3():
|
||||
pass
|
||||
|
||||
assert hello3.__name__ == 'hello3'
|
||||
|
||||
del hello3
|
||||
|
||||
@func_set_timeout(getTimeoutFunc, allowOverride=True)
|
||||
def hello4():
|
||||
pass
|
||||
|
||||
assert hello4.__name__ == 'hello4'
|
||||
|
||||
|
||||
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 :
|
||||
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 :
|
||||
58
tests/FuncTimeoutTests/test_TestUtils.py
Executable file
58
tests/FuncTimeoutTests/test_TestUtils.py
Executable file
@ -0,0 +1,58 @@
|
||||
#!/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 sys
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from func_timeout import func_timeout, FunctionTimedOut, func_set_timeout
|
||||
|
||||
from TestUtils import ARG_NO_DEFAULT, ARG_NO_DEFAULT_TYPE, getSleepLambda, getSleepLambdaWithArgs, compareTimes
|
||||
|
||||
class TestTestUtils(object):
|
||||
'''
|
||||
TestTestUtils - Perform tests using the basic func_timeout function
|
||||
'''
|
||||
|
||||
|
||||
def test_ArgNoDefault(self):
|
||||
|
||||
assert ARG_NO_DEFAULT == ARG_NO_DEFAULT , 'Expected ARG_NO_DEFAULT to equal itself'
|
||||
assert (ARG_NO_DEFAULT != ARG_NO_DEFAULT) is False , 'Expected ARG_NO_DEFAULT to not not equal itself'
|
||||
|
||||
|
||||
assert ARG_NO_DEFAULT == ARG_NO_DEFAULT_TYPE , 'Expected ARG_NO_DEFAULT to equal ARG_NO_DEFAULT_TYPE'
|
||||
assert ARG_NO_DEFAULT_TYPE == ARG_NO_DEFAULT , '2Expected ARG_NO_DEFAULT to equal ARG_NO_DEFAULT_TYPE'
|
||||
|
||||
otherInstance = ARG_NO_DEFAULT_TYPE()
|
||||
|
||||
assert otherInstance == ARG_NO_DEFAULT , 'Assert ARG_NO_DEFAULT_TYPE instances equal eachother'
|
||||
assert not (otherInstance != ARG_NO_DEFAULT) , 'Assert ARG_NO_DEFAULT_TYPE instances not not-equal eachother'
|
||||
|
||||
|
||||
def test_compareTimes(self):
|
||||
|
||||
startTime = 50.00
|
||||
endTime = 52.03
|
||||
|
||||
assert compareTimes(endTime, startTime, 2, roundTo=2, deltaFixed=.05, deltaPct=None) == 0 , 'Expected deltaFixed to be > abs(delta) to show times equal'
|
||||
|
||||
assert compareTimes(endTime, startTime, 2, roundTo=2, deltaFixed=.01, deltaPct=None) == .03 , 'Expected when deltaFixed is less than the abs delta, actual diff to be returned.'
|
||||
|
||||
assert compareTimes(endTime, startTime, 2, roundTo=2, deltaFixed=None, deltaPct=.2) == 0 , 'Expected deltaPct * cmpTime when greater than abs delta to be equal'
|
||||
|
||||
assert compareTimes(endTime, startTime, 2, roundTo=2, deltaFixed=None, deltaPct=.0002) == .03 , 'Expected deltaPct * cmpTime when less than abs delta to be actual diff'
|
||||
|
||||
|
||||
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 :
|
||||
59
tests/FuncTimeoutTests/test_TestUtilsSleep.py
Executable file
59
tests/FuncTimeoutTests/test_TestUtilsSleep.py
Executable file
@ -0,0 +1,59 @@
|
||||
#!/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 sys
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from func_timeout import func_timeout, FunctionTimedOut, func_set_timeout
|
||||
|
||||
from TestUtils import ARG_NO_DEFAULT, ARG_NO_DEFAULT_TYPE, getSleepLambda, getSleepLambdaWithArgs, compareTimes
|
||||
|
||||
class TestBasicSleep(object):
|
||||
'''
|
||||
TestBasicSleep - Perform test on the sleep generator function.
|
||||
|
||||
Seperate file so runs in separate GoodTests process ( for performance reasons )
|
||||
'''
|
||||
|
||||
|
||||
def test_getSleepLambda(self):
|
||||
|
||||
sleepLambda = getSleepLambda(2)
|
||||
startTime = time.time()
|
||||
sleepLambda(2, 3)
|
||||
endTime = time.time()
|
||||
|
||||
assert compareTimes(endTime, startTime, 2, 2, deltaFixed=.1, deltaPct=None) == 0 , 'Expected getSleepLambda(2) to take 2 seconds.'
|
||||
|
||||
sleepLambda = getSleepLambda(1.75)
|
||||
|
||||
expectedResult = 2 + 3
|
||||
startTime = time.time()
|
||||
result = sleepLambda(2, 3)
|
||||
endTime = time.time()
|
||||
|
||||
assert result == expectedResult , 'Got wrong result'
|
||||
assert compareTimes(endTime, startTime, 1.75, 2, deltaFixed=.1, deltaPct=None) == 0 , 'Expected getSleepLambda(1.75) to take 1.75 seconds.'
|
||||
|
||||
expectedResult = 5 + 13
|
||||
|
||||
startTime = time.time()
|
||||
result = sleepLambda(5, 13)
|
||||
endTime = time.time()
|
||||
|
||||
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.'
|
||||
|
||||
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 :
|
||||
71
tests/FuncTimeoutTests/test_TestUtilsSleepWithArgs.py
Executable file
71
tests/FuncTimeoutTests/test_TestUtilsSleepWithArgs.py
Executable file
@ -0,0 +1,71 @@
|
||||
#!/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 sys
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from func_timeout import func_timeout, FunctionTimedOut, func_set_timeout
|
||||
|
||||
from TestUtils import ARG_NO_DEFAULT, ARG_NO_DEFAULT_TYPE, getSleepLambda, getSleepLambdaWithArgs, compareTimes
|
||||
|
||||
class TestBasicSleepWithArgs(object):
|
||||
'''
|
||||
TestBasicSleepWithArgs - Perform test on the sleep generator with args function.
|
||||
|
||||
Seperate file so runs in separate GoodTests process ( for performance reasons )
|
||||
'''
|
||||
|
||||
|
||||
def test_getSleepLambdaWithArgs(self):
|
||||
|
||||
sleepLambda = getSleepLambdaWithArgs(2, [ ('a', ), ('b', ), ('c', 4) ] )
|
||||
startTime = time.time()
|
||||
try:
|
||||
sleepLambda(1, 2)
|
||||
except:
|
||||
raise AssertionError('Expected to have 1 default arg and 2 standard. Tried 3 args')
|
||||
endTime = time.time()
|
||||
|
||||
assert compareTimes(endTime, startTime, 2, 2, deltaFixed=.15, deltaPct=None) == 0 , 'Expected getSleepLambdaWithArgs(2) to take 2 seconds.'
|
||||
|
||||
try:
|
||||
sleepLambda(4, 7, 12)
|
||||
except:
|
||||
raise AssertionError('Expected to have 1 default arg and 2 standard. Tried 3 args.')
|
||||
|
||||
|
||||
|
||||
expectedResult = 2 + 3
|
||||
sleepLambda = getSleepLambdaWithArgs(1.75, [ ('a', ), ('xxx', )] )
|
||||
startTime = time.time()
|
||||
try:
|
||||
result = sleepLambda(xxx=2, a=3)
|
||||
except:
|
||||
raise AssertionError('Expected to be able to use provided field names when calling function')
|
||||
endTime = time.time()
|
||||
|
||||
assert result == expectedResult , 'Got wrong result'
|
||||
assert compareTimes(endTime, startTime, 1.75, 2, deltaFixed=.15, deltaPct=None) == 0 , 'Expected getSleepLambdaWithArgs(1.75) to take 1.75 seconds.'
|
||||
|
||||
expectedResult = 5 + 13
|
||||
|
||||
startTime = time.time()
|
||||
result = sleepLambda(5, 13)
|
||||
endTime = time.time()
|
||||
|
||||
assert result == expectedResult , 'Did not get return from sleepFunction'
|
||||
assert compareTimes(endTime, startTime, 1.75, 2, deltaFixed=.15, deltaPct=None) == 0 , 'Expected getSleepLambda(1.75) to take 1.75 seconds.'
|
||||
|
||||
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 :
|
||||
3
tests/README
Normal file
3
tests/README
Normal file
@ -0,0 +1,3 @@
|
||||
Run ./runTests.py to execute all unit tests against parent directory "func_timeout" (will not run against site install.)
|
||||
|
||||
This will download GoodTests.py if not already present in PATH, and use it to execute the unit tests in parallel, forcing resolution to the parent func_timeout dir
|
||||
411
tests/runTests.py
Executable file
411
tests/runTests.py
Executable file
@ -0,0 +1,411 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2015, 2016, 2017 Tim Savannah under following terms:
|
||||
# You may modify and redistribe this script with your project
|
||||
#
|
||||
# It will download the latest GoodTests.py and use it to execute the tests.
|
||||
#
|
||||
# This should be placed in a directory, "tests", at the root of your project. It assumes that ../$MY_PACKAGE_MODULE is the path to your test module, and will create a symlink to it in order to run tests.
|
||||
# The tests should be found in $MY_TEST_DIRECTORY in given "tests" folder.
|
||||
|
||||
|
||||
# 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).
|
||||
|
||||
# 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 subprocess
|
||||
import sys
|
||||
|
||||
# URL to current version of GoodTests.py - You only need to change this if you host an internal copy.
|
||||
GOODTESTS_URL = 'https://raw.githubusercontent.com/kata198/GoodTests/master/GoodTests.py'
|
||||
|
||||
# This should be your module name, and can be any relative or absolute path, or just a module name.
|
||||
# If just a module name is given, the directory must be in current directory or parent directory.
|
||||
MY_PACKAGE_MODULE = 'func_timeout'
|
||||
|
||||
# Normally, you want to test the codebase during development, so you don't care about the site-packages installed version.
|
||||
# If you want to allow testing with any module by @MY_PACKAGE_MODULE in the python path, change this to True.
|
||||
ALLOW_SITE_INSTALL = False
|
||||
|
||||
# This is the test directory that should contain all your tests. This should be a directory in your "tests" folder
|
||||
MY_TEST_DIRECTORY = 'FuncTimeoutTests'
|
||||
|
||||
__version__ = '3.0.5'
|
||||
__version_tuple__ = (3, 0, 5)
|
||||
|
||||
def findGoodTests():
|
||||
'''
|
||||
findGoodTests - Tries to find GoodTests.py
|
||||
|
||||
@return <dict> {
|
||||
'path' <str> -> Path to GoodTests.py (for execution)
|
||||
'success' <bool> -> True/False if we successfully found GoodTests.py
|
||||
}
|
||||
'''
|
||||
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('/'):
|
||||
path = path[:-1]
|
||||
guess = path + '/GoodTests.py'
|
||||
if os.path.exists(guess):
|
||||
success = True
|
||||
result = guess
|
||||
break
|
||||
|
||||
return {
|
||||
'path' : result,
|
||||
"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():
|
||||
'''
|
||||
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()
|
||||
|
||||
return res
|
||||
|
||||
def download_goodTests(GOODTESTS_URL=None):
|
||||
'''
|
||||
download_goodTests - Attempts to download GoodTests, using the default global url (or one provided).
|
||||
|
||||
@return <int> - 0 on success (program should continue), otherwise non-zero (program should abort with this exit status)
|
||||
'''
|
||||
if GOODTESTS_URL is None:
|
||||
GOODTESTS_URL = globals()['GOODTESTS_URL']
|
||||
|
||||
validAnswer = False
|
||||
while validAnswer == False:
|
||||
sys.stdout.write('GoodTests not found. Would you like to install it to local folder? (y/n): ')
|
||||
sys.stdout.flush()
|
||||
answer = sys.stdin.readline().strip().lower()
|
||||
if answer not in ('y', 'n', 'yes', 'no'):
|
||||
continue
|
||||
validAnswer = True
|
||||
answer = answer[0]
|
||||
|
||||
if answer == 'n':
|
||||
sys.stderr.write('Cannot run tests without installing GoodTests. http://pypi.python.org/pypi/GoodTests or https://github.com/kata198/Goodtests\n')
|
||||
return 1
|
||||
try:
|
||||
import urllib2 as urllib
|
||||
except ImportError:
|
||||
try:
|
||||
import urllib.request as urllib
|
||||
except:
|
||||
sys.stderr.write('Failed to import urllib. Trying pip.\n')
|
||||
res = try_pip_install()
|
||||
if res != 0:
|
||||
sys.stderr.write('Failed to install GoodTests with pip or direct download. aborting.\n')
|
||||
return 1
|
||||
try:
|
||||
response = urllib.urlopen(GOODTESTS_URL)
|
||||
contents = response.read()
|
||||
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('\nTrying pip.\n')
|
||||
res = try_pip_install()
|
||||
if res != 0:
|
||||
sys.stderr.write('Failed to install GoodTests with pip or direct download. aborting.\n')
|
||||
return 1
|
||||
try:
|
||||
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,)))
|
||||
return 1
|
||||
try:
|
||||
os.chmod('GoodTests.py', 0o775)
|
||||
except:
|
||||
sys.stderr.write('WARNING: Failed to chmod +x GoodTests.py, may not be able to be executed.\n')
|
||||
|
||||
try:
|
||||
import GoodTests
|
||||
except ImportError:
|
||||
sys.stderr.write('Seemed to download GoodTests okay, but still cannot import. Aborting.\n')
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def main(thisDir=None, additionalArgs=[], MY_PACKAGE_MODULE=None, ALLOW_SITE_INSTALL=None, MY_TEST_DIRECTORY=None, GOODTESTS_URL=None):
|
||||
'''
|
||||
Do the work - Try to find GoodTests.py, else prompt to download it, then run the tests.
|
||||
|
||||
@param thisDir <None/str> - None to use default (directory this test file is in, or if not obtainable, current directory).
|
||||
@param additionalArgs <list> - Any additional args to pass to GoodTests.py
|
||||
|
||||
Remainder of params take their global (top of file) defaults unless explicitly set here. See top of file for documentation.
|
||||
|
||||
@return <int> - Exit code of application. 0 on success, non-zero on failure.
|
||||
|
||||
TODO: Standardize return codes so external applications can derive failure without parsing error strings.
|
||||
'''
|
||||
|
||||
if MY_PACKAGE_MODULE is None:
|
||||
MY_PACKAGE_MODULE = globals()['MY_PACKAGE_MODULE']
|
||||
if ALLOW_SITE_INSTALL is None:
|
||||
ALLOW_SITE_INSTALL = globals()['ALLOW_SITE_INSTALL']
|
||||
if MY_TEST_DIRECTORY is None:
|
||||
MY_TEST_DIRECTORY = globals()['MY_TEST_DIRECTORY']
|
||||
if GOODTESTS_URL is None:
|
||||
GOODTESTS_URL = globals()['GOODTESTS_URL']
|
||||
|
||||
|
||||
if not thisDir:
|
||||
thisDir = os.path.dirname(__file__)
|
||||
|
||||
if not thisDir:
|
||||
thisDir = str(os.getcwd())
|
||||
elif not thisDir.startswith('/'):
|
||||
thisDir = str(os.getcwd()) + '/' + thisDir
|
||||
|
||||
# If GoodTests is in current directory, make sure we find it later
|
||||
if os.path.exists('./GoodTests.py'):
|
||||
os.environ['PATH'] = str(os.getcwd()) + ':' + os.environ['PATH']
|
||||
|
||||
os.chdir(thisDir)
|
||||
|
||||
goodTestsInfo = findGoodTests()
|
||||
if goodTestsInfo['success'] is False:
|
||||
downloadRet = download_goodTests(GOODTESTS_URL)
|
||||
if downloadRet != 0:
|
||||
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,))
|
||||
return 1
|
||||
|
||||
baseName = os.path.basename(MY_PACKAGE_MODULE)
|
||||
dirName = os.path.dirname(MY_PACKAGE_MODULE)
|
||||
|
||||
newPath = None
|
||||
if dirName not in ('.', ''):
|
||||
if dirName.startswith('.'):
|
||||
dirName = os.getcwd() + os.sep + dirName + os.sep
|
||||
newPath = dirName
|
||||
elif dirName == '':
|
||||
inCurrentDir = False
|
||||
try:
|
||||
find_mod(MY_PACKAGE_MODULE)
|
||||
inCurrentDir = True
|
||||
except ImportError:
|
||||
# COMPAT WITH PREVIOUS runTests.py: Try plain module in parent directory
|
||||
foundIt = False
|
||||
oldSysPath = sys.path[:]
|
||||
sys.path = [os.path.realpath(os.getcwd() + os.sep + '..' + os.sep)]
|
||||
try:
|
||||
find_mod(MY_PACKAGE_MODULE)
|
||||
foundIt = True
|
||||
sys.path = oldSysPath
|
||||
except ImportError as e:
|
||||
sys.path = oldSysPath
|
||||
if not ALLOW_SITE_INSTALL:
|
||||
sys.stderr.write('Cannot find "%s" locally.\n' %(MY_PACKAGE_MODULE,))
|
||||
return 2
|
||||
else:
|
||||
try:
|
||||
__import__(baseName)
|
||||
except:
|
||||
sys.stderr.write('Cannot find "%s" locally or in global python path.\n' %(MY_PACKAGE_MODULE,))
|
||||
return 2
|
||||
|
||||
if foundIt is True:
|
||||
newPath = os.path.realpath(os.getcwd() + os.sep + '..' + os.sep)
|
||||
if inCurrentDir is True:
|
||||
newPath = os.path.realpath(os.getcwd() + os.sep + '..' + os.sep)
|
||||
|
||||
if newPath:
|
||||
newPythonPath = [newPath] + [x for x in os.environ.get('PYTHONPATH', '').split(':') if x]
|
||||
os.environ['PYTHONPATH'] = ':'.join(newPythonPath)
|
||||
sys.path = [newPath] + sys.path
|
||||
|
||||
try:
|
||||
__import__(baseName)
|
||||
except ImportError as e:
|
||||
if baseName.endswith(('.py', '.pyc', '.pyo')):
|
||||
MY_PACKAGE_MODULE = baseName[ : baseName.rindex('.')]
|
||||
|
||||
try:
|
||||
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
|
||||
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
|
||||
|
||||
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,))
|
||||
else:
|
||||
sys.stderr.write('Provided test directory, "%s" is not a directory.\n' %(MY_TEST_DIRECTORY,))
|
||||
return 3
|
||||
|
||||
sys.stdout.write('Starting test..\n')
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
|
||||
didTerminate = False
|
||||
pipe = subprocess.Popen([sys.executable, goodTestsInfo['path']] + additionalArgs + [MY_TEST_DIRECTORY], env=os.environ, shell=False)
|
||||
while True:
|
||||
try:
|
||||
pipe.wait()
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
if not didTerminate:
|
||||
pipe.terminate()
|
||||
didTerminate = True
|
||||
else:
|
||||
pipe.kill()
|
||||
break
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ret = main(None, sys.argv[1:])
|
||||
sys.exit(ret)
|
||||
Loading…
Reference in New Issue
Block a user