Using variables in signal handler – require global?

Question:

I have a signal handler to handle ctrl-c interrupt. If in the signal handler I want to read a variable set in my main script, is there an alternative to using a “global” statement when setting the variable?

I don’t mind doing this, but read this post (Do you use the "global" statement in Python?) in which someone commented that there should be no reason to ever use global.

What is the alternative in this case?

My code looks like this:


def signal_handler(signal, frame):
    print "in sig handler - g_var=%s" % g_var

def main():
    global g_var
    g_var = "test"

    time.sleep(120)


if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)
    main()
Asked By: Joe Watkins

||

Answers:

If you’re just reading the variable, there should be no need to make the variable “global”

def foo():
    print a
a = 3
foo()  #3

global is necessary to allow you to change the variable and have that change propagate into the module namespace.

If you want to pass some state to your callback without using global, the typical way to do this us to use an instance method as the callback:

class foo(object):
     def __init__(self,arg):
         self.arg = arg
     def callback_print_arg(self):
         print self.arg

def call_callback(callback):
    callback()

a = foo(42)
call_callback(a.callback_print_arg) #42
Answered By: mgilson

You can access outer-scope variables from within an inline-defined function, like so:

my_values = {'foo':'bar'}
def handler(signum, frame):
    for key,val in my_values.items():
        print key,val
    my_values['bat']='baz'
    #remember to use mutable types, like dicts or lists

signal.signal(signal.SIGINT, handler)
Answered By: John Fink

You can use a closure as the signal handler that acquires its state from the main script:

import signal
import sys
import time

def main_function():

    data_for_signal_handler = 10

    def signal_handler(*args):
        print data_for_signal_handler
        sys.exit()

    signal.signal(signal.SIGINT, signal_handler) # Or whatever signal

    while True:
        data_for_signal_handler += 1
        time.sleep(0.5)

if __name__ == '__main__':
    main_function()
Answered By: Henry Gomersall

You can use partial to create a “closure”.

import signal
from functools import partial

def signal_handler(g_var, signal, frame):
    print "in sig handler - g_var=%s" % g_var

def main():
    g_var = "test"
    signal.signal(signal.SIGINT, partial(signal_handler, g_var))

    time.sleep(120)


if __name__ == '__main__':
    main()
Answered By: Ale

Within the object-oriented paradigm (OOP) it’s quite convenient to use lambdas for that purpose. Using lambdas you could pass some additional context (like a self reference) and/or get rid of the unused arguments (signal, frame).

import time
import signal

class Application:

    def __init__( self ):
        signal.signal( signal.SIGINT, lambda signal, frame: self._signal_handler() )
        self.terminated = False

    def _signal_handler( self ):
        self.terminated = True

    def MainLoop( self ):        
        while not self.terminated:
            print( "I'm just doing my job like everyone else" )
            time.sleep( 3 )

app = Application()
app.MainLoop()

print( "The app is terminated, exiting ..." )
Answered By: Maxim Ky
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.