Finish loop before exiting with a KeyboardInterrupt - python

I have some program logic that works as follows:
for i in range(10**6):
foo(i)
print("foo executed with parameter", i)
bar(i)
print("bar executed with parameter", i)
The problem arises when I want to interrupt the loop with Ctrl+C to raise a KeyboardInterrupt.
I make sure that the function bar(i) always runs after the function foo(i); i.e. the loop cannot be interrupted between the two function calls (or, if it is, I want to complete the loop before exiting). If the interrupt is received while one of the functions is being executed, I want both to finish before exiting.
How is this achievable (with a try...except statement or otherwise) in Python 3?

Thank you to Omer Ben Haim for providing an answer in the comments.
Indeed, the SIGINT signal can be captured using the signal module. Here is some proof-of-concept code that demonstrates this:
import signal
import time
stop = False
def handler(sig, frame):
global stop
stop = True
signal.signal(signal.SIGINT, handler)
############
i = 0
while i<10**6 and not stop:
print("Part 1:", i)
time.sleep(0.5)
print("Part 2:", i)
time.sleep(0.5)
i += 1

This could be done rather easily using a flag.
When a KeyboardInterrupt is raised(E.g. Ctrl+c is pressed), you capture it and set the quit_loop flag to True.
When we finish what we need to do, we check if we need to quit the loop and if we do then we break.
If you want to fully exit the program you can replace break with exit().
quit_loop = False
for i in range(10):
try:
foo()
except KeyboardInterrupt:
quit_loop = True
bar()
if quit_loop:
break

Related

how to stop one function when the other one stops on python

i've a working function which stops when the user writes exit, but i also have another function which doesn't stop so cmd just keeps staying open and won't close like it should as the other function keeps changing the cmd text color
import os,webbrowser,time,random,sys
from threading import Thread
def fun1():
Q=1
while Q==1:
print ('would you like to:')
print('1.launch an applcation')
print('2.browse the internet')
print('========================================')
bruv=input('your answer: ')
if bruv in ('1','launch','launch app','launch an application'):
print('========================================')
print('what app would you like to open?')
print('========================================')
x=input('your answer: ')
if x not in ('word'):
y=x+'.exe'
os.startfile(y)
else:
z='win'+x
os.startfile(z)
if bruv in ('2','browse the internet','browse','browse internet'):
print('========================================')
print ('where would you like to go?')
print('========================================')
p=input('your answer: ')
p='www.'+p+'.com'
webbrowser.open(p)
print('========================================')
print ('ok')
print('========================================')
print ('wanna continue?')
if input() in ('exit','no','quit','nope','close'):
print('========================================')
print ('ok! thanks for using my bot!')
print('========================================')
time.sleep(1)
Q+=1
countdown=3
while countdown>=0:
if countdown!=0:
print (countdown)
time.sleep(1)
countdown-=1
else:
sys.exit()
def fun2():
what=1
while what==1:
os.system('color E')
time.sleep(0.2)
os.system('color A')
time.sleep(0.2)
os.system('color C')
time.sleep(0.2)
os.system('color B')
time.sleep(0.2)
os.system('color D')
time.sleep(0.2)
else:
sys.exit()
t1=Thread(target=fun1)
t2=Thread(target=fun2)
t1.start()
t2.start()
In this code fun2 keeps rolling changing the color and wont close CMD
also i know this code is pretty bad i'm just starting python.
So the problem here is that your whole script (which starts both of your functions/threads) will on default only end when all the threads it started have stopped. So all your functions need to return at some point, but your fun2() will never finish as the endless loop while what==1 will always run as what is always equal to 1. (As a side note, the convention for endless loops in Python is to use while True: ...).
To tell a Thread that it should finish when all other threads have, you have to make it a Daemon. You might want to look here, where the documentation says:
A thread can be flagged as a “daemon thread”. The significance of this
flag is that the entire Python program exits when only daemon threads
are left. The initial value is inherited from the creating thread. The
flag can be set through the daemon property or the daemon constructor
argument.
So in your example above, making t2 a deamon should work.
...
t1 = Thread(target=fun1)
t2 = Thread(target=fun2)
t2.deamon = True
t1.start()
t2.start()
P.S.: Also be aware that color does not work in all terminals and operating systems (e.g. MacOS)

How to stop threads running infinite loops in python?

I've made a program which has a main thread that spawns many other threads by subclassing the threading.Thread class.
Each such child thread runs an infinite while loop, and inside the loop I check a condition. If the condition is true, I make the thread sleep for 1 second using time.sleep(1) and if it's false, then the thread performs some computation.
The program itself works fine and I've achieved what I wanted to do, my only remaining problem is that I seem unable to stop the threads after my work is done. I want the user to be able to kill all the threads by pressing a button or giving a keyboard interrupt like Ctrl+C.
For this I had tried using the signal module and inserted a conditon in the threads' loops that breaks the loop when the main thread catches a signal but it didn't work for some reason. Can anyone please help with this?
EDIT: This is some of the relevant code snippets:
def sighandler(signal,frame):
BaseThreadClass.stop_flag = True
class BaseThreadClass(threading.Thread):
stop_flag = False
def __init__(self):
threading.Thread.__init__(self)
def run(self,*args):
while True:
if condition:
time.sleep(1)
else:
#do computation and stuff
if BaseThreadClass.stop_flag:
#do cleanup
break
Your basic method does work, but you've still not posted enough code to show the flaw. I added a few lines of code to make it runnable and produced a result like:
$ python3 test.py
thread alive
main alive
thread alive
main alive
^CSignal caught
main alive
thread alive
main alive
main alive
main alive
^CSignal caught
^CSignal caught
main alive
^Z
[2]+ Stopped python3 test.py
$ kill %2
The problem demonstrated above involves the signal handler telling all the threads to exit, except the main thread, which still runs and still catches interrupts. The full source of this variant of the sample snippet is:
import threading, signal, time
def sighandler(signal,frame):
BaseThreadClass.stop_flag = True
print("Signal caught")
class BaseThreadClass(threading.Thread):
stop_flag = False
def __init__(self):
threading.Thread.__init__(self)
def run(self,*args):
while True:
if True:
time.sleep(1)
print("thread alive")
else:
#do computation and stuff
pass
if BaseThreadClass.stop_flag:
#do cleanup
break
signal.signal(signal.SIGINT, sighandler)
t = BaseThreadClass()
t.start()
while True:
time.sleep(1)
print("main alive")
The problem here is that the main thread never checks for the quit condition. But as you never posted what the main thread does, nor how the signal handler is activated, or information regarding whether threads may go a long time without checking the quit condition... I still don't know what went wrong in your program. The signal example shown in the library documentation raises an exception in order to divert the main thread.
Signals are a rather low level concept for this task, however. I took the liberty of writing a somewhat more naïve version of the main thread:
try:
t = BaseThreadClass()
t.start()
while True:
time.sleep(1)
print("main alive")
except KeyboardInterrupt:
BaseThreadClass.stop_flag = True
t.join()
This version catches the exception thrown by the default interrupt handler, signals the thread to stop, and waits for it to do so. It might even be appropriate to change the except clause to a finally, since we could want to clean the threads up on other errors too.
If you want to do this kind of "cooperative" polled-shutdown, you can use a threading.Event to signal:
import threading
import time
def proc1():
while True:
print("1") # payload
time.sleep(1)
# have we been signalled to stop?
if not ev1.wait(0): break
# do any shutdown etc. here
print ("T1 exiting")
ev1 = threading.Event()
ev1.set()
thread1 = threading.Thread(target=proc1)
thread1.start()
time.sleep(3)
# signal thread1 to stop
ev1.clear()
But be aware that if the "payload" does something blocking like network or file IO, that op will not be interrupted. You can do those blocking ops with a timeout, but that obviously will complicate your code.

User input to break infinite loop?

I'm working on calculating a bunch of triangles with special properties for a Number Theorist friend. There are infinitely many of these triangles, but they require a lot of computational power to find.
We've got an infinite loop running through different b,d combinations. When the program ends, it calls the go(dict) function to export the triangles it found. Currently, we tell the program at the start what interval of time to run for. This is causing problems when we realize we need the computing power for something else, but the program still has hours to run and we don't want to lose the triangles it has already calculated by exiting the program without running go(dict).
Ideally, we want some user input to cause the program to break the loop, run go(dict) with whatever current version of the dictionary it is holding in memory, then exit. Trying with atexit.register(go, dict) was unsuccessful, as it is called many times within the loop and runs many times when the program is terminated.
(See the abbreviated loop code below)
interval = eval(input("How many hours shall I run for? "))*3600
starttime = time.time()
dict = {}
b = start_value
while True:
for d in range (1, b):
compute stuff
if (condition):
add triangle to dict
if (time.time()-starttime)>interval:
go(dict)
return
b +=1
This is what exceptions are can be used for: you press Ctrl+C to interrupt the process, and your code handles it by saving the results:
while True:
try:
# your code here
except KeyboardInterrupt:
go(dict)
break
Note that you can't return from a standalone loop, but you can break from it, however.
one thing you can do is take over ctrl+c using except KeyboardInterrupt: when you send an interrupt to the script it will run this block in which you can put code to exit cleanly
here is an example:
i = 0
try:
while True:
i+=1
except KeyboardInterrupt:
print 'caught INT'
print i
Using Signals:
import signal
interrupted = False # Used to break the loop when we send SIGINT
# When SIGINT is received, set interrupted to True
def signal_handler(signal, frame):
global interrupted
interrupted = True
# Sets signal_handler to run if a SIGINT was received
signal.signal(signal.SIGINT, signal_handler)
interval = eval(input("How many hours shall I run for? "))*3600
starttime = time.time()
dict = {}
b = start_value
while True:
for d in range (1, b):
compute stuff
if (condition):
add triangle to dict
if (time.time()-starttime)>interval:
go(dict)
break
if interrupted:
go(dict)
break
b +=1
Now when we hit ctrl+c, we set interrupted to True which runs go(dict) and breaks the loop.

Python threading script is not starting/working properly

I quickly wrote this example script for stackoverflow so ignore the functionality aspect of it (my version looks alot better than this), but:
from threading import Thread
from msvcrt import getch #I'm using Windows by the way.
from time import sleep
import sys
def KeyEvent():
while True:
key = getch()
if key.encode('hex') == '03': #^C
y = raw_input("Are you sure you want to quit? (y/n): ").lower()
if y == 'y':
sys.exit(0)
else: pass
def main():
t = 0
while True:
print "The count is {0}".format(t)
t +=1
sleep(1)
if __name__ == "__main__":
mainthread = Thread(target = main)
kev = Thread(target = KeyEvent)
mainthread.daemon = True
kev.daemon = True
mainthread.start()
kev.start()
What this script is supposed to do is run both loops at the same time, one counts up in seconds, while the other checks for ^C. Please don't recommend that I use a different method, because this is an example.
My problem is, that the script just doesn't start. It displays "The count is 0" and exits, but if I comletely omit the .daemon = True parts, the script runs, but it doesn't run sys.exit(0) correctly. How can I make this script run correctly AND exit when prompted?
Threads marked as daemon automatically die when every other non-daemon thread is dead. In your case, the main thread dies just after calling start() on your two daemon threads, bringing the python process with it. That anything gets done in your thread is a matter of luck and timing.
sys.exit(0) does not kill threads other than the main thread. You need a way to signal your threads that it's time to stop. One way to do that is through an Event object.
You shouldn't use getch to try to catch Ctrl+C, try using a signal handler instead:
from threading import Thread
from threading import Event
from time import sleep
import signal
stop = Event()
def handler(signum, frame):
y = raw_input("Are you sure you want to quit? (y/n): ").lower()
if y == 'y':
stop.set()
def main():
t = 0
while not stop.isSet():
print "The count is {0}".format(t)
t +=1
sleep(1)
if __name__ == "__main__":
signal.signal(signal.SIGINT, handler)
mainthread = Thread(target = main)
mainthread.start()
while mainthread.is_alive():
try:
mainthread.join(timeout = 0.1)
except IOError:
pass #Gets thrown when we interrupt the join
You have two problems. First is that your program ends too early. Second is that you try to quit the program in your KeyEvent thread.
Your program terminates early because you didn't hold your main thread. Your main thread ends right after your last statement kev.start() and your daemon threads die with it.
You need a mechanism to hold the main thread infinitively (because you expect the user to type "y" to quit.) There are several ways of doing this. One is add
mainthread.join()
to the end of your code.
Second, sys.exit(0) in your KeyEvent thread obviously can not terminate your whole program. Please find your answer in the following post: Why does sys.exit() not exit when called inside a thread in Python?

Python Threading - Managing thread termination and the main thread

Before you read on, know that I'm new to Python and very new to threading so forgive me if I misunderstand how threads work or make a rookie error :P
Short description of my goal:
The main thread (a) does some things (e.g. prints "begin!")
Main thread spawns a new thread (b) that first prints "Thread B
started" and then prints x+1 forever (1, 2, 3 ...)
Main thread prints "Woop!"
Then the end of the main thread is reached, it terminates itself and
then switches to thread b making b the main thread
Program is now running thread b as the main thread so is just
printing x+1 forever and a has been forgotten and is no longer
relevant
Ctrl+C will terminate thread b and effectively, the whole program
will be terminated because thread a doesn't exist anymore
Here's what I have so far (the basics):
import threading, time
def printCount():
print "Thread B started"
x = 0
while True:
time.sleep(1)
x = x + 1
print x
## User Code ##
print "begin!"
threadB = threading.Thread(target=printCount)
threadB.start()
print "woop!"
The requirements are:
I don't want to modify below the 'User Code' mark much at all. I
certainly don't want to wrap it in a class, a function or it's own
thread
Pressing Ctrl+C at any point should terminate the entire program
with no threads left running (Using something like: except
KeyboardInterrupt: os._exit(1)) inside User Code is fine
Thread a may continue to run instead of making thread b the main
thread, but in this case, I don't want the code to handle Ctrl+C
terminating the entire program inside the User Code section
This example is not my actual goal, just a simplified version of the problem I'm having. I'm trying to make an IRC framework where the user can import it and use the API very simply without messying their own code with threads and interrupts and the like. This is why it's important for the user code to be as clean as possible.
The framework would allow the user to create an IRC bot which runs forever, listening to commands whilst allowing the user to add their own commands. The Github link is here if you're interested (It's very WIP atm).
You cannot "switch" threads. So once you're done with your main thread, you have to wait for the other threads to terminate using the method join. But note this:
Since the join method is not interruptible with KeyboardInterrupt, you need to specify a timeout and loop over to detect user interrupts.
Since you can't force a thread to terminate, you have to implement a stop mecanism using threading.Event for instance
You also need to use threading.Lock to prevent concurrent access on shared resources, like sys.stdout (used when you print)
I gathered these aspects in a class called ThreadHandler, please have a look:
import threading, time
def printCount(lock, stop):
with lock:
print "Thread B started"
x = 0
while not stop.is_set():
time.sleep(1)
x = x + 1
with lock:
print x
class ThreadHandler():
STEP = 0.2
def __init__(self, target):
self.lock = threading.Lock()
self.stop = threading.Event()
args = (self.lock, self.stop)
self.thread = threading.Thread(target=target, args=args)
def start(self):
self.thread.start()
def join(self):
while self.thread.is_alive():
try:
self.thread.join(self.STEP)
except KeyboardInterrupt:
self.stop.set()
## User Code ##
print "begin!"
handler = ThreadHandler(target=printCount)
handler.start()
with handler.lock:
print "woop!"
handler.join()
Wrote a short note on another question yesterday having similar issues, this is a check you could implement in the subthread "b":
Instead of while 1: do the following:
def printCount():
main = None
for t in threading.enumerate():
if t.name == 'MainThread':
main = t
print "Thread B started"
x = 0
while main and main.isAlive():
time.sleep(1)
x = x + 1
print x
It would be a good idea to store main in the global scope for all threads to use isntead of having to look up the main thread each and every initation of the sub-thread.
But this would do the work in your example.
main will be the handle towards your main thread by iterating through all threads (.enumerate()) and then placing the thread called "MainThread" into main and then calling main.isAlive() to check if it's still running.
if main is None or False or if .isAlive() returns False it will indicate that the thread is either non-existant or dead, shutting down your subthread :)
You can't switch threads like that. It doesn't work like that.
However you could use signals with a global flag ALIVE:
import threading, time, signal
ALIVE = True
def handle_sigint(signum, frame):
global ALIVE
ALIVE = False
signal.signal(signal.SIGINT, handle_sigint)
def printCount():
print "Thread B started"
x = 0
while ALIVE: # <--- note the change
time.sleep(1)
x = x + 1
print x
## User Code ##
print "begin!"
threadB = threading.Thread(target=printCount)
threadB.start()
print "woop!"
signal.pause() # <--- wait for signals
Now it will gracefully quit after pressing CTRL+C.

Categories