Reputation: 705
I'm currently trying to accomplish a loop which stops on user interaction.
My first idea was to use signals which brought me to this:
import signal
interruptLoop = False
def interrupt_handler(sig, frame):
interruptLoop = True
signal.signal(signal.SIGINT, interrupt_handler) # handle ctrl+c
count = 0
while not interruptLoop:
print count; count += 1
this results in an infinite loop, because the rise of interruptLoop does not affect the variable out of the handler scope.
I'd appreciate any advice about why python/signal behaves like that and how to accomplish my task. Thanks in advance.
Upvotes: 2
Views: 1825
Reputation: 3161
You're only missing one line!
import signal
interruptLoop = False
def interrupt_handler(sig, frame):
global interruptLoop # You're missing this
interruptLoop = True
signal.signal(signal.SIGINT, interrupt_handler) # handle ctrl+c
count = 0
while not interruptLoop:
print(count); count += 1
print("I'm done!")
If you run this, you'll see the numbers printed until you hit Ctrl+C
, at which point you'll see "I'm done!" and the script will exit.
Why was the global interruptLoop
needed?
Python doesn't require you to declare variables in your function scope. The way it determines what variables are defined locally for a function is to see which variables are set. So when you set interruptLoop = True
in interrupt_handler
, python sees this as a cue that interrupt_handler
has a local variable. This local variable shadows the outer-scope's interruptLoop
, which python is treating as separate. So your handler essentially just creates a local variable, modifies it, and then exits. Of course this doesn't stop the loop (which depends on the outer scope's interruptLoop
). The global
keyword signals to python that the inner variable is actually supposed to reference the outer one rather than shadowing it. For more see here for a short explanation and here for a full discussion of python's variable scoping.
Upvotes: 2