jjst
jjst

Reputation: 2831

"Proper" way to handle signals other than SIGINT in Python?

I had some Python code that needed to be able to handle SIGINT. For this purpose I used something like this:

def mymethod(*params):
  obj = MyObj(params)
  try:
    obj.do_some_long_stuff()
  except KeyboardInterrupt:
    obj.cleanup()

Awesome and really straightforward. Yay, Python is great!

However, I now need to also handle other signals, namely SIGTSTP and SIGQUIT. What I'm trying to do is something similar. Here's some pseudocode demonstrating what I'm trying to do with SIGTSTP (I hope it's clear enough):

def mymethod(*params):
  obj = MyObj(params)
  try:
    obj.do_some_long_stuff()
  catch SIGINT:
    obj.cleanup()
  catch SIGTSTP:
    log_stop(obj) # Log that we stopped obj (add info about stop signal happening in obj-specific log file )
    raise SIGTSTP # Do normal SIGTSTP behavior as if we didn't catch the signal

It seems that the way to go here is to use the signal module. However, my problem is that I can then can't access the object state anymore as I could with the KeyboardInterruptException:

import os
import signal

def handler(signum, frame):
  print "I can't access obj from here anymore!" # How to access obj from here?
  signal.signal(signum, signal.SIG_DFL)
  os.kill(os.getpid(), signum) # Rethrow signal, this time without catching it

def mymethod(*params):
  obj = MyObj(params)
  signal.signal(signal.SIGTSTP, handler)
  obj.do_some_long_stuff()   

So how can I solve this, i.e. handle the signal while still having some access to the object I'm working with?

Upvotes: 3

Views: 3578

Answers (2)

codeape
codeape

Reputation: 100906

Or use a closure:

import os
import signal

def create_handler(obj):
    def _handler(signum, frame):
        print "obj is availiable here!"
        print obj
        signal.signal(signum, signal.SIG_DFL)
        os.kill(os.getpid(), signum) # Rethrow signal, this time without catching it
    return _handler

def mymethod(*params):
  obj = MyObj(params)
  signal.signal(signal.SIGTSTP, create_handler(obj))
  obj.do_some_long_stuff()   

create_handler(obj) returns a handler function with access to obj.

Upvotes: 10

Nemelis
Nemelis

Reputation: 5480

That would work under the condition that handler is a class-method and contains the self-member.

You could make obj global, since than you can access it from each function.

import os
import signal
obj = None

def handler(signum, frame):
    log_stop(obj)
    signal.signal(signum, signal.SIG_DFL)
    os.kill(os.getpid(), signum) # Rethrow signal, this time without catching it

def mymethod(*params):
    global obj # to signal that the obj we are changing here is the global obj

    obj = MyObj(params)
    handler.obj = obj
    signal.signal(signal.SIGTSTP, handler)
    obj.do_some_long_stuff()

(Note: Personally I avoid global parameters as much as possible, since global means really global).

Upvotes: 1

Related Questions