What you need to do is catch the interrupt, set a flag saying you were interrupted but then continue working until it's time to check the flag (at the end of each loop). Because python's try-except construct will abandon the current run of the loop, you need to set up a proper signal handler; it'll handle the interrupt but then let python continue where it left off. Here's how:
import signal
import time # For the demo only
def signal_handler(signal, frame):
global interrupted
interrupted = True
signal.signal(signal.SIGINT, signal_handler)
interrupted = False
while True:
print("Working hard...")
time.sleep(3)
print("All done!")
if interrupted:
print("Gotta go")
break
Notes:
Use this from the command line. In the IDLE console, it'll trample on IDLE's own interrupt handling.
A better solution would be to "block" KeyboardInterrupt for the duration of the loop, and unblock it when it's time to poll for interrupts. This is a feature of some Unix flavors but not all, hence python does not support it (see the third "General rule")
The OP wants to do this inside a class. But the interrupt function is invoked by the signal handling system, with two arguments: The signal number and a pointer to the stack frame-- no place for a
selfargument giving access to the class object. Hence the simplest way to set a flag is to use a global variable. You can rig a pointer to the local context by using closures (i.e., define the signal handler dynamically in__init__(), but frankly I wouldn't bother unless a global is out of the question due to multi-threading or whatever.
Caveat: If your process is in the middle of a system call, handling an signal may interrupt the system call. So this may not be safe for all applications. Safer alternatives would be (a) Instead of relying on signals, use a non-blocking read at the end of each loop iteration (and type input instead of hitting ^C); (b) use threads or interprocess communication to isolate the worker from the signal handling; or (c) do the work of implementing real signal blocking, if you are on an OS that has it. All of them are OS-dependent to some extent, so I'll leave it at that.
Answer from alexis on Stack OverflowWhat you need to do is catch the interrupt, set a flag saying you were interrupted but then continue working until it's time to check the flag (at the end of each loop). Because python's try-except construct will abandon the current run of the loop, you need to set up a proper signal handler; it'll handle the interrupt but then let python continue where it left off. Here's how:
import signal
import time # For the demo only
def signal_handler(signal, frame):
global interrupted
interrupted = True
signal.signal(signal.SIGINT, signal_handler)
interrupted = False
while True:
print("Working hard...")
time.sleep(3)
print("All done!")
if interrupted:
print("Gotta go")
break
Notes:
Use this from the command line. In the IDLE console, it'll trample on IDLE's own interrupt handling.
A better solution would be to "block" KeyboardInterrupt for the duration of the loop, and unblock it when it's time to poll for interrupts. This is a feature of some Unix flavors but not all, hence python does not support it (see the third "General rule")
The OP wants to do this inside a class. But the interrupt function is invoked by the signal handling system, with two arguments: The signal number and a pointer to the stack frame-- no place for a
selfargument giving access to the class object. Hence the simplest way to set a flag is to use a global variable. You can rig a pointer to the local context by using closures (i.e., define the signal handler dynamically in__init__(), but frankly I wouldn't bother unless a global is out of the question due to multi-threading or whatever.
Caveat: If your process is in the middle of a system call, handling an signal may interrupt the system call. So this may not be safe for all applications. Safer alternatives would be (a) Instead of relying on signals, use a non-blocking read at the end of each loop iteration (and type input instead of hitting ^C); (b) use threads or interprocess communication to isolate the worker from the signal handling; or (c) do the work of implementing real signal blocking, if you are on an OS that has it. All of them are OS-dependent to some extent, so I'll leave it at that.
the below logic will help you do this,
import signal
import sys
import time
run = True
def signal_handler(signal, frame):
global run
print("exiting")
run = False
signal.signal(signal.SIGINT, signal_handler)
while run:
print("hi")
time.sleep(1)
# do anything
print("bye")
while running this, try pressing CTRL + C
Hello! Just started learning python and got introduced to what infinite loops are! I typed up some stuff to make an infinite loop and tried to do ctrl+C to kill it in the vs code terminal but it wouldn't work. I tried cmd+C too (I have a Mac) and that code kept going... Would love help and direction in what I'm doing wrong + how to stop an infinite loop!
Edit: Thank you all for your helpful tips! :)
As the title says, I'm an absolute noob at VSC, I'm using it for Python. Previously, I used to use Notepad++, but it's been bugging me.
Basically , I have a bad habit of creating infinite loops and generally I've been able to set up keyboard shortcuts to terminate the code. Having trouble finding an answer. I've seen places say to use Ctrl + C and Ctrl + Alt + M, but neither work.
Edit: I have a Windows, not Mac.
CTRL+d -> Defines EOF (End of File).
CTRL+c -> Will terminate most jobs.
If, however you have written a python wrapper program that calls other python programs in turn, Ctrl-c will only stop the the job that is currently running. The wrapper program will keep running. Worst case scenario, you can do this:
Open up: Applications -> Utilities -> Activity Monitor, Find the process labeled as python, highlight it in the activity monitor then click on "Quit Process".
These three suggestions should work for most situations where you want a program to stop.
This isn't a Python issue, per se. This ia a Unix shell behavior and is a difference from Windows and other systems. See the Wikipedia article on CTRL-Z for a more complete explanation:
On Unix-like systems, Control+Z is the most common default keyboard mapping for the key sequence that suspends a process (SIGTSTP). When entered by a user at their computer terminal, the currently running foreground process is sent a SIGTSTP signal, which generally causes the process to suspend its execution. The user can later continue the process execution by typing the command 'fg' (short for foreground) or by typing 'bg' (short for background) and furthermore typing the command 'disown' to separate the background process from the terminal.
On OS X as on various other Unix-based systems, you could use the stty command to change which key, if any, produces a SIGTSTP (or an eof, for that matter):
$ stty -a
speed 38400 baud; 30 rows; 90 columns;
lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl
-echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo
-extproc
iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8
-ignbrk brkint -inpck -ignpar -parmrk
oflags: opost onlcr -oxtabs -onocr -onlret
cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow
-dtrflow -mdmbuf
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;
min = 1; quit = ^\; reprint = ^R; start = ^Q; status = ^T;
stop = ^S; susp = ^Z; time = 0; werase = ^W;
$ stty susp ^Y
$ cat
^Y
[1]+ Stopped cat
What is a Pythonic way to stop an inifinite loop while it's executing? For example if you have a function that is monitoring something and you want to use a Python command to stop the monitoring at will after the function has been executed.
Edit: This has to do with a GUI. I want a start/stop button for my monitoring loop.
I accidentally created an infinite loop and had to force quit my program.
Is there a mental checklist people use to make sure loops actually stop? I want to avoid freezing my computer again.
Let me present you an alternative. It looks like you want real-time updates for some kind of information. You could use a pub/sub interface (publish/subscribe). Since you are using python, there are plenty of possibilities.
One of them is using Redis pub/sub functionality: http://redis.io/topics/pubsub/ - and here is the corresponding python module: redis-py
-Update-
Example
Here is an example from dirkk0 (question / answer):
import sys
import threading
import cmd
def monitor():
r = redis.Redis(YOURHOST, YOURPORT, YOURPASSWORD, db=0)
channel = sys.argv[1]
p = r.pubsub()
p.subscribe(channel)
print 'monitoring channel', channel
for m in p.listen():
print m['data']
class my_cmd(cmd.Cmd):
"""Simple command processor example."""
def do_start(self, line):
my_thread.start()
def do_EOF(self, line):
return True
if __name__ == '__main__':
if len(sys.argv) == 1:
print "missing argument! please provide the channel name."
else:
my_thread = threading.Thread(target=monitor)
my_thread.setDaemon(True)
my_cmd().cmdloop()
-Update 2-
In addition, look at this tutorial:
http://blog.abourget.net/2011/3/31/new-and-hot-part-6-redis-publish-and-subscribe/
I guess one way to work around the issue is having a script for one loop run, that would:
- check no other instance of the script is running
- look into the queue and process everything found there
Now, then you can run this script from cron every minute between 8 a.m. and 8 p.m. The only downside is that new items may some time to get processed.