How to stop timeout task
I am trying to figure out the correct way to use this library, I use the following test code:
Python 2.7.5 (default, Sep 15 2016, 22:37:39)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import time
>>> from func_timeout import func_set_timeout
>>>
>>> @func_set_timeout(3)
... def test():
... while True:
... time.sleep(1)
... print "test"
...
>>>
>>> test()
test
test
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/site-packages/func_timeout/dafunc.py", line 185, in <lambda>
return wraps(func)(lambda *args, **kwargs : func_timeout(defaultTimeout, func, args=args, kwargs=kwargs))
File "/usr/lib/python2.7/site-packages/func_timeout/dafunc.py", line 101, in func_timeout
raise FunctionTimedOut('', timeout, func, args, kwargs)
func_timeout.exceptions.FunctionTimedOut: Function test (args=()) (kwargs={}) timed out after 3.000000 seconds.
>>> test
test
test
test
test
KeyboardInterrupt
>>> test
test
test
test
After the func_timeout.exceptions.FunctionTimedOut, the test function is not terminated, how can i stop the function?
you should try this code for same:
Code :
import time
from func_timeout import func_set_timeout , FunctionTimedOut
@func_set_timeout(3)
def test():
while True:
time.sleep(1)
print("test")
try:
test()
except FunctionTimedOut:
print("end test!!")
print("function ended syccesfully!!!")
Output :
test
test
end test!!
function ended syccesfully!!!
@Utsav13 Thanks for relay, but the function test doesn't actually stopped, it's just because the main thread ended and the process exited. I can test it with this:
import time
from func_timeout import func_set_timeout , FunctionTimedOut
@func_set_timeout(3)
def test():
while True:
time.sleep(1)
print("test")
try:
test()
except FunctionTimedOut:
print("end test!!")
print("function ended syccesfully!!!")
for i in range(1,10):
time.sleep(1)
Output:
test
test
end test!!
function ended syccesfully!!!
test
test
test
test
test
test
test
test
test
okay, previously I tested code with python 3.6 and it works fine. but when I have tested it with python 2.7 then I got the same error which u have right now. so if it is possible to use the same code with python 3.x then you have not to change anything with your code. but if your python version is mandatory for your use then you can use simple tricks to exit the loop.
import time
from func_timeout import func_set_timeout , FunctionTimedOut
stop = False
@func_set_timeout(3)
def test():
while True:
time.sleep(1)
if stop == True :
break
print("test")
def test2():
global stop
try :
test()
except FunctionTimedOut:
stop = True
print("end test!!")
test2()
You have found a real bug! This works in a file in python2, and works in python3 both interactive and with a file. The problem is interactive mode in python2, and here is the problem: The thread is marked dead, but still executing! (This is a bug in python2, hence why it acts differently in python3).
Here is my output for running the same in a file:
$ python2 2>&1 | head -n1 & sleep .5; kill -9 %%
[2] 13940
Python 2.7.14 (default, Oct 31 2017, 21:12:13)
$ cat test.py
#!/usr/bin/python2
import time
from func_timeout import func_set_timeout
@func_set_timeout(3)
def test():
while True:
time.sleep(1)
print ( "Test" )
if __name__ == '__main__':
test()
And the COMPLETE output:
$ ./test.py
Test
Test
Traceback (most recent call last):
File "./test.py", line 16, in <module>
test()
File "/home/tim/projects/func_timeout/func_timeout/dafunc.py", line 185, in <lambda>
return wraps(func)(lambda *args, **kwargs : func_timeout(defaultTimeout, func, args=args, kwargs=kwargs))
File "/home/tim/projects/func_timeout/func_timeout/dafunc.py", line 101, in func_timeout
raise FunctionTimedOut('', timeout, func, args, kwargs)
func_timeout.exceptions.FunctionTimedOut: Function test (args=()) (kwargs={}) timed out after 3.000000 seconds.
Caveat
I AM able to reproduce this by running the exact same code in the python2 interactive terminal, however it does NOT reproduce in a standalone file.
This appears to be a bug in python, see the output of the threading.enumerate() call, which lists all active threads:
[<_MainThread(MainThread, started 25769803792)>, <StoppableThread(Thread-1, stopped daemon 25771807856)>]
Show that it is not alive:
threading.enumerate()[1].isAlive()
False
So.... there is obviously some bug in python2 here, where it is still running a thread that it has internally marked as dead, and is not cleaning it up.
Trying the same in python3 interactive terminal works as expected:
>>> test()
Test
Test
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/tim/projects/func_timeout/func_timeout/dafunc.py", line 185, in <lambda>
return wraps(func)(lambda *args, **kwargs : func_timeout(defaultTimeout, func, args=args, kwargs=kwargs))
File "/home/tim/projects/func_timeout/func_timeout/dafunc.py", line 101, in func_timeout
raise FunctionTimedOut('', timeout, func, args, kwargs)
func_timeout.exceptions.FunctionTimedOut: Function test (args=()) (kwargs={}) timed out after 3.000000 seconds.
And as you can see, the thread is properly cleaned-up.
>>> import threading
>>> threading.enumerate()
[<_MainThread(MainThread, started 25769803792)>]
I tested the following patch, which seems to stop the thread after 6 seconds (instead of the expected 3). Note in this case I reduced "repeatEvery" / "raiseEvery" from 2.0 seconds to .25 seconds for brevity sake.
diff --git a/func_timeout/StoppableThread.py b/func_timeout/StoppableThread.py
index 9b8bb3b..4e9c116 100644
--- a/func_timeout/StoppableThread.py
+++ b/func_timeout/StoppableThread.py
@@ -119,11 +120,29 @@ class JoinThread(threading.Thread):
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.. )
+ print ( "IsAlive1? " + str(self.otherThread.is_alive()) )
self.otherThread._Thread__stop()
+ print ( "IsAlive2? " + str(self.otherThread.is_alive()) )
+ self.otherThread.join(.1)
+ print ( "IsAlive3? " + str(self.otherThread.is_alive()) )
+ ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self.otherThread.ident), ctypes.py_object(self.exception))
+ print ( "IsAlive4? " + str(self.otherThread.is_alive()) )
+ self.otherThread.join(.1)
+ print ( "IsAlive5? " + str(self.otherThread.is_alive()) )
+ self.otherThread._Thread__stop()
+ print ( "IsAlive6? " + str(self.otherThread.is_alive()) )
+ self.otherThread.join(.1)
+ print ( "IsAlive7? " + str(self.otherThread.is_alive()) )
+ self.otherThread.join(self.repeatEvery)
+ print ( "IsAlive8? " + str(self.otherThread.is_alive()) )
+
while self.otherThread.is_alive():
+ print ( "IsAlive9? " + str(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))
+ print ( "IsAlive10? " + str(self.otherThread.is_alive()) )
self.otherThread.join(self.repeatEvery)
+ print ( "IsAlive11? " + str(self.otherThread.is_alive()) )
And you can see the results:
Test
Test
IsAlive1? True
IsAlive2? False
IsAlive3? False
IsAlive4? False
IsAlive5? False
IsAlive6? False
IsAlive7? False
IsAlive8? False
Traceback (most recent call last):
File "
Test Test Test Test
So, even though it is marked dead, apparently hammering it with exceptions and joins finally works around the bug and makes it die.
I will have to find a clean way to accomplish this for python2 (which is just about EOL, btw...), as the above patch obviously isn't acceptable. I would suggest that you use python3 for now.
I am not sure if this is limited to the print function, or if other things (such as math) retain the running.
Stay tuned!
`@func_set_timeout(7) def myloop(n): for i in range(n): print (i) time.sleep(1)
try: myloop(10) except FunctionTimedOut: print ('I timed out')
print ('************')
try: func_timeout(5, myloop, args=[10]) except FunctionTimedOut: print ('I timed out') `
Output: 0 1 2 3 4 5 6 I timed out
0 1 2 3 4 5 I timed out
6 7 8 9
I use python 3.7 and this issue is still there. I've embedded the myloop function within the func_set_timeout decorator with 7 seconds timeout. In first case, everything works fine when I call this function as it is now. In the second case I call it via the func_timeout wrapper but with 5 seconds timeout this time. I would have expected the function to timeout at 5 seconds. It does raise the FunctionTimedOut exception at 5 secs, but the thread keeps going, and this time the func_set_timeout is completely ignored. Looks like a more fundamental issue than just the python version. Many thanks for your help.
This is still an issue with Python 3.6, which sadly renders the library pretty much useless :(
on python 3.10 the bug mentioned is fixed, 0 1 2 3 4 5 I timed out
however
import time
import func_timeout
from func_timeout import func_set_timeout
@func_set_timeout(1)
def jls_extract_def():
return print(eval(s))
s='4.10**15**10**9'
try:
jls_extract_def()
except func_timeout.exceptions.FunctionTimedOut:
print('out_time')#
# return 1
eval can not be interupted correctly