How to run one last function before getting killed in Python?

29,810

Solution 1

import time

try:
    time.sleep(10)
finally:
    print "clean up"
    
clean up
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyboardInterrupt

If you need to catch other OS level interrupts, look at the signal module:

http://docs.python.org/library/signal.html

Signal Example

from signal import *
import sys, time

def clean(*args):
    print "clean me"
    sys.exit(0)

for sig in (SIGABRT, SIGBREAK, SIGILL, SIGINT, SIGSEGV, SIGTERM):
    signal(sig, clean)

time.sleep(10)

Solution 2

You could use the atexit module. With it, you can register a function which will be called at program termination. An example from here: http://docs.python.org/library/atexit.html

try:
    _count = int(open("/tmp/counter").read())
except IOError:
    _count = 0

def incrcounter(n):
    global _count
    _count = _count + n

def savecounter():
    open("/tmp/counter", "w").write("%d" % _count)

import atexit
atexit.register(savecounter)

You can also pass positional and keyword parameters to the function you want to call at program termination.

Note that there are a few circumstances listed in the docs in which your handler won't be called:

Note: The functions registered via this module are not called when the program is killed by a signal not handled by Python, when a Python fatal internal error is detected, or when os._exit() is called.

As such, you may want to also register a signal handler.

Solution 3

import signal
import sys
import time

def cleanup(*args):
    print 'Exiting'
    sys.exit(0)

signal.signal(signal.SIGINT, cleanup)
signal.signal(signal.SIGTERM, cleanup)
while True:
    time.sleep(60)  # less busy loop

Solution 4

WIth apologies to 'Unknown' for taking their answer and correcting it as though it was my own answer, but my edits were rejected.

The approved answer contains an error that will cause a segfault.

You cannot use sys.exit() in a signal handler, but you can use os._exit so that it becomes:

from signal import *
import os, time

def clean(*args):
    print "clean me"
    os._exit(0)

for sig in (SIGABRT, SIGINT, SIGTERM):
    signal(sig, clean)

time.sleep(10)

SIGBREAK may be used if the target platform is Windows.

Depending on the use case and the need to cleanup in the event of fatal errors - you may add SIGSEGV and SIGILL but generally this is not advised since the program state may be such that you create an infinite loop.

Solution 5

Use the atexit module to register a function that will be called at the end.

import atexit
atexit.register(some_function)
Share:
29,810
Admin
Author by

Admin

Updated on April 08, 2020

Comments

  • Admin
    Admin about 4 years

    Is there any way to run one last command before a running Python script is stopped by being killed by some other script, keyboard interrupt etc.

  • Dave
    Dave almost 15 years
    Note: although exit() is not async-safe in C, the wording of the Python documentation suggests to me that Python takes care of this problem with some special signal-handling code.
  • Dave
    Dave almost 15 years
    I have no idea why you chose that particular set, but SIGBREAK doesn't work on non-Windows Pythons, and SIGILL are SIGSEGV are probably not signals that you want to be trapping without a really good reason.
  • Bastien Léonard
    Bastien Léonard almost 15 years
    I've tested this code on Windows XP, and an error is actually raised because time.sleep() was interrupted. It works on GNU/Linux.
  • Unknown
    Unknown almost 15 years
    @Dave its just an example. And its for dan to decide which interrupts he wants to trap.
  • previous_developer
    previous_developer almost 8 years
    Is sys.exit(0) necessary? I thought python exits after your function finished anyway. Did you use that to show it is a handled termination?
  • Mark Amery
    Mark Amery about 6 years
    -1 for the weird and arbitrary set of signals, as noted by @Dave. Using SIGTERM would make sense (since its purpose is to instruct processes to exit), and using SIGUSR1 would be defensible (since it's for user-defined actions), but having stuff like SIGILL in there is just illogical. Also, this only partially answers the question; it won't handle the case where the script is killed by a keyboard interrupt, which was a possibility included in the question.
  • Mark Amery
    Mark Amery about 6 years
    "The approved answer contains an error that will cause a segfault." - what's the error? The use of sys.exit()? It doesn't segfault for me; what do I need to do to see this segfault? "You cannot use sys.exit() in a signal handler" - it seems to work for me; what's your basis for this? A cautious -1 for now since the key claims in this answer are unsubstantiated.
  • Stuart M
    Stuart M about 6 years
    The crash is not guarenteed, basically the behaviour of raising the exception from sys.exit() is undefined. Various behaviours seen include crashing (stackoverflow.com/questions/24789269/…) or the exit call simply appearing not to work at all (stackoverflow.com/questions/29980078/…). See also thushw.blogspot.co.uk/2010/12/…
  • Stuart M
    Stuart M about 6 years
    You also didn't acknowledge my comments on SIGSEGV/SIGILL which are also incorrect in the accepted answer.
  • ash
    ash almost 6 years
    @StuartM it seems likely that the problem in that blog post was caused by the bare except: catching the SystemExit. Please could you expand on what you mean by "the behaviour of raising the exception from sys.exit() is undefined"? I'm not sure I understand you.