Python – How to guarantee a graceful shutdown

How to guarantee a graceful shutdown… here is a solution to the problem.

How to guarantee a graceful shutdown

I

have a class and I want to make sure it ends properly. IN PARTICULAR, EVEN IF MY MAIN PROCESS IS KILLED BY AN EXTERNAL EVENT (E.G. SIGTERM), I WANT IT TO CLOSE.

If Exception terminates, the context manager and atexit are not called. So I’ve been trying to use signals to capture events and then run my specific cleanup code.

But now I’m diving into how python cleans up different types of exports. For example, although I can capture the signal. SIGTERM and add my own handler, but I don’t know how to call the default handler when I’m done. But not an expert, having my library take over with my own code to exit the correct system makes me uncomfortable.

(Note: I’m using shelve, and if you don’t close shelve, it stays locked and won’t open – basically unusable.) )

Am I too hard? I don’t think deep expertise is required to make sure I can clean up properly.

Or, if I

really need to capture a signal and then take over the system exit, can I simply execute the cleanup code, and if the only other handler is SIG_DFL, then I just execute sys.exit()? It sounds risky, but is it okay?

My current code:

def reg_sig(signum):
    old_handler = signal.getsignal(signum)

def sighandler(signum, frame):
        close_open_objects() # <== my custom code
        if callable(old_handler):
            old_handler()
        else:
            # What goes here?
            # do_default_handler() # <- this seems ideal
            # or
            # sys.exit()

try:
        # I'm not sure what signals might be called so I register a bunch.
        signal.signal(signum, sighandler) 
    except Exception as err:
        pass

# Register signals
for sig in [signal. SIGHUP, signal. SIGINT, signal. SIGQUIT, signal. SIGABRT, 
            signal. SIGKILL,signal. SIGTERM]:
    reg_sig(sig)

Solution

A simple solution is to use atexit and register a signal handler that calls sys.exit(0). Note that this changes the process exit code, not the code that is normally returned when a signal occurs. From a related answer :

import sys, atexit, signal
atexit.register(close_open_objects)  # Your custom code
signal.signal(signal. SIGTERM, lambda n, f: sys.exit(0))

Related Problems and Solutions