Python 3 KeyboardInterrupt Multithreading - python

I am trying to have a main thread wait for its worker threads to finish using the following code, but when I try to interrupt it with Ctrl+C it doesn't stop
import threading
import sys
exit = threading.Event()
#pass it to the threads
try:
exit.wait()
print('Goodbye')
sys.exit()
except KeyboardInterrupt:
print('Interrupted')
sys.exit()
UPDATE
Nothing prints. All background threads are daemons.

import threading
import sys
import time
exit = threading.Event()
#pass it to the threads
try:
print("hi")
time.sleep(20)
print('Goodbye')
sys.exit()
except KeyboardInterrupt:
print('Interrupted')
sys.exit()
Please try the above statement to test your code. i have tested it and its working fine for me.
As you are using exit.wait() without giving any timeouts, its running for infinite seconds. So please put some time as an agument.
Follow the below code:
exit = threading.Event()
#pass it to the threads
try:
print("hi")
exit.wait(5)
print('Goodbye')
sys.exit()
except KeyboardInterrupt:
print('Interrupted')
sys.exit()

Related

How to exit a while loop only after the current loop has completed in Python?

I'm trying to set up my Python script to allow the user to end the program, however the program needs to finish what it's doing first. I have the following code set up:
import sys
import keyboard
import time
prepareToStop = 0;
try:
while prepareToStop == 0:
#Program code here
print(prepareToStop)
time.sleep(0.1)
except KeyboardInterrupt:
prepareToStop = 1
print("\nProgram will shut down after current operation is complete.\n")
print("Program shutting down...")
sys.exit()
However, the program still exits the loop as soon as the KeyboardInterrupt is received. I've seen advice that this could be fixed by placing the 'try, except' inside the while loop, however this causes the program to fail to detect the KeyboardInterrupt at all.
If I understand your problem correctly, maybe threading can help you. Note how end do something appears even after KeyboardInterrupt.
EDIT : I placed t.join() in the try
import sys
import time
import threading
def do_something(prepareToStop):
print(prepareToStop)
time.sleep(1)
print('end do something')
prepareToStop = 0
while prepareToStop == 0:
t = threading.Thread(target=do_something, args=[prepareToStop])
try:
t.start()
t.join() # wait for the threading task to end
except KeyboardInterrupt:
prepareToStop = 1
print("\nProgram will shut down after current operation is complete.\n")
print('will not appear for last run')
print("Program shutting down...")
sys.exit()
Example of output :
0
end do something
will not appear for last run
0
^C
Program will shut down after current operation is complete.
will not appear for last run
Program shutting down...
end do something
The keyboard interrupt that you were trying to use works like any system interrupt and will jump immediately to the exception block, without going back to where it was when the interrupt has occurred.
You can confirm this by using the debugger in your IDE (ie. Press F5 in VSCODE).
The code below works like you want, but the user has to hold ESC key pressed in the keyboard for it to be captured from the program in the proper time.
import sys
import keyboard
import time
prepareToStop = 0;
while prepareToStop == 0:
if keyboard.is_pressed('Esc'):
prepareToStop = 1
print("\nProgram will shut down after current operation is complete.\n")
#Program code here
print('Sleeping 5 sec - hold the ESC key for some time to exit')
time.sleep(5)
print('This prints only after the delay')
#end code here
print(prepareToStop)
time.sleep(0.1)
print("Program shutting down...")
sys.exit()
And then I would recommend to change into this:
import sys
import keyboard
import time
while not keyboard.is_pressed('Esc'):
#Program code here
print('Sleeping 5 sec - hold the ESC key for some time to exit')
time.sleep(5)
print('This prints only after the delay')
#end code here
time.sleep(0.1)
print("Program shutting down...")
sys.exit()
The above solution is not better than using threading, but it is much more simpler.

I need to stop multi thread code from running

I'm trying to stop the code from running if the user presses ctrl+shift+c. I use the code below. Unfortunately sys.exit() stops only "wait_for_ctrl_shift_c" function, but not "main_func". What should I use to stop them both?
Thanks.
def wait_for_ctrl_shift_c():
print ('wait_for_ctrl_shift_c is working')
keyboard.wait('ctrl+shift+c')
print('wait_for_ctrl_shift_c was pressed!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
sys.exit()
def main_func():
a=0
while True:
print ('Working2 ',a)
a=a+1
sleep(1)
if __name__ == '__main__':
Thread(target = wait_for_ctrl_shift_c).start()
Thread(target = main_func).start()
There are multiple ways to do it. First of all you have 3 threads, one main thread and the other 2 (infinite loop & keyboard one) you create.
You can register signals and handle it, also you can call interrupt_main to interrupt main thread (not the while loop thread). Interrupt will go to main exception handler. Also instead of True i changed the second thread to have an attribute to check if it should run for clean exit.
import os
import threading
import time
import sys
import _thread
def wait_for_ctrl_shift_c():
print ('wait_for_ctrl_shift_c is working')
keyboard.wait('ctrl+shift+c')
print ('exiting thread')
_thread.interrupt_main()
sys.exit()
def main_func():
a=0
t = threading.currentThread()
while getattr(t, "run", True):
print ('Working2 ',a)
a=a+1
time.sleep(1)
print ('exiting main_func')
if __name__ == '__main__':
try:
t1 = threading.Thread(target = wait_for_ctrl_shift_c)
t2 = threading.Thread(target = main_func)
t1.start()
t2.start()
t1.join()
t2.join()
except:
print ('main exiting')
t2.run = False
sys.exit()
Open shell in another window, type ps to list running processes, and kill the Python one (via kill 3145, if 3145 is its PID) to stop them both. This way we kill the process within which these threads run.

Terminate background python script nicely

I am running a python script in the background using the command python script.py &. The script might look like this.
import time
def loop():
while True:
time.sleep(1)
if __name__=='__main__':
try:
loop()
except KeyboardInterrupt:
print("Terminated properly")
When it comes to terminating the script, I would like to do some cleanup before it is stopped (such as printing "Terminated properly"). If I run as a current process, this would be handled by the except statement after a keyboard interrupt.
Using the kill PID command means the cleanup is never executed. How can I stop a background process and execute some lines of code before it is terminated?
You can use signal module to catch any signals sent to your script via kill.
You setup a signal handler to catch the signal in question that would perform the cleanup.
import signal
import time
running = 0
def loop ():
global running
running = 1
while running:
try: time.sleep(0.25)
except KeyboardInterrupt: break
print "Ended nicely!"
def cleanup (signumber, stackframe):
global running
running = 0
signal.signal(signal.SIGABRT, cleanup)
signal.signal(signal.SIGTERM, cleanup)
signal.signal(signal.SIGQUIT, cleanup)
loop()
Use finally clause:
def loop():
while True:
time.sleep(1)
if __name__=='__main__':
try:
loop()
except KeyboardInterrupt:
print("Terminated properly")
finally:
print('executes always')

KeyboardInterrupt does not work in multi threading python

I am trying to do multi threading to check the network connection. My code is:
exitFlag = 0
lst_doxygen=[]
lst_sphinx=[]
class myThread (threading.Thread):
def __init__(self, counter):
threading.Thread.__init__(self)
self.counter=counter
def run(self):
print "Starting thread"
link_urls(self.counter)
def link_urls(delay):
global lst_doxygen
global lst_sphinx
global exitFlag
while exitFlag==0:
try:
if network_connection() is True:
try:
links = lxml.html.parse(gr.prefs().get_string('grc', 'doxygen_base_uri', '').split(',')[1]+"annotated.html").xpath("//a/#href")
for url in links:
lst_doxygen.append(url)
links = lxml.html.parse(gr.prefs().get_string('grc', 'sphinx_base_uri', '').split(',')[1]+"genindex.html").xpath("//a/#href")
for url in links:
lst_sphinx.append(url)
exitFlag=1
except IOError, AttributeError:
pass
time.sleep(delay)
print "my"
except KeyboardInterrupt:
exitFlag=1
def network_connection():
network=False
try:
response = urllib2.urlopen("http://google.com", None, 2.5)
network=True
except urllib2.URLError, e:
pass
return network
I have set a flag to stop the thread inside while loop. I also want to exit the thread by pressing Ctrl-C. So I have used try-except but thread is still working and does not exit. If I try to use
if KeyboardInterrupt:
exitFlag=1
instead of try-except, thread just works for first time execution of while loop and then exist.
p.s.
I have created the instance of myThread class in another module.
Finally, I got the answer of my question. I need to flag my thread as Daemon. So when I will create the instance if myThread class, I will add one more line:
thread1.myThread(2)
thread1.setDaemon(True)
thread1.start()
You only get signals or KeyboardInterrupt on the main thread. There are various ways to handle it, but perhaps you could make exitFlag a global and move the exception handler to your main thread.
Here is how I catch a CTRL-C in general.
import time
import signal
import sys
stop = False
def run():
while not stop:
print 'I am alive'
time.sleep(3)
def signal_handler(signal, frame):
global stop
print 'You pressed Ctrl+C!'
stop = True
t1 = threading.Thread(target=run)
t1.start()
signal.signal(signal.SIGINT, signal_handler)
print 'Press Ctrl+C'
signal.pause()
output:
python threads.py
Press Ctrl+C
I am alive
I am alive
^CYou pressed Ctrl+C!

threading ignores KeyboardInterrupt exception

I'm running this simple code:
import threading, time
class reqthread(threading.Thread):
def run(self):
for i in range(0, 10):
time.sleep(1)
print('.')
try:
thread = reqthread()
thread.start()
except (KeyboardInterrupt, SystemExit):
print('\n! Received keyboard interrupt, quitting threads.\n')
But when I run it, it prints
$ python prova.py
.
.
^C.
.
.
.
.
.
.
.
Exception KeyboardInterrupt in <module 'threading' from '/usr/lib/python2.6/threading.pyc'> ignored
In fact python thread ignore my Ctrl+C keyboard interrupt and doesn't print Received Keyboard Interrupt. Why? What is wrong with this code?
Try
try:
thread=reqthread()
thread.daemon=True
thread.start()
while True: time.sleep(100)
except (KeyboardInterrupt, SystemExit):
print '\n! Received keyboard interrupt, quitting threads.\n'
Without the call to time.sleep, the main process is jumping out of the try...except block too early, so the KeyboardInterrupt is not caught. My first thought was to use thread.join, but that seems to block the main process (ignoring KeyboardInterrupt) until the thread is finished.
thread.daemon=True causes the thread to terminate when the main process ends.
To summarize the changes recommended in the comments, the following works well for me:
try:
thread = reqthread()
thread.start()
while thread.isAlive():
thread.join(1) # not sure if there is an appreciable cost to this.
except (KeyboardInterrupt, SystemExit):
print '\n! Received keyboard interrupt, quitting threads.\n'
sys.exit()
Slight modification of ubuntu's solution.
Removing tread.daemon = True as suggested by Eric and replacing the sleeping loop by signal.pause():
import signal
try:
thread=reqthread()
thread.start()
signal.pause() # instead of: while True: time.sleep(100)
except (KeyboardInterrupt, SystemExit):
print '\n! Received keyboard interrupt, quitting threads.\n'
My (hacky) solution is to monkey-patch Thread.join() like this:
def initThreadJoinHack():
import threading, thread
mainThread = threading.currentThread()
assert isinstance(mainThread, threading._MainThread)
mainThreadId = thread.get_ident()
join_orig = threading.Thread.join
def join_hacked(threadObj, timeout=None):
"""
:type threadObj: threading.Thread
:type timeout: float|None
"""
if timeout is None and thread.get_ident() == mainThreadId:
# This is a HACK for Thread.join() if we are in the main thread.
# In that case, a Thread.join(timeout=None) would hang and even not respond to signals
# because signals will get delivered to other threads and Python would forward
# them for delayed handling to the main thread which hangs.
# See CPython signalmodule.c.
# Currently the best solution I can think of:
while threadObj.isAlive():
join_orig(threadObj, timeout=0.1)
else:
# In all other cases, we can use the original.
join_orig(threadObj, timeout=timeout)
threading.Thread.join = join_hacked
Putting the try ... except in each thread and also a signal.pause() in true main() works for me.
Watch out for import lock though. I am guessing this is why Python doesn't solve ctrl-C by default.

Categories

Resources