173

How can you have a function or something that will be executed before your program quits? I have a script that will be constantly running in the background, and I need it to save some data to a file before it exits. Is there a standard way of doing this?

Chris
  • 1,416
  • 18
  • 29
RacecaR
  • 1,907
  • 2
  • 13
  • 7
  • 2
    The script shouldn't ever stop, but maybe someone will kill the process or press Ctrl+\ or something. – RacecaR Oct 03 '10 at 15:11

6 Answers6

277

Check out the atexit module:

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

For example, if I wanted to print a message when my application was terminating:

import atexit

def exit_handler():
    print 'My application is ending!'

atexit.register(exit_handler)

Just be aware that this works great for normal termination of the script, but it won't get called in all cases (e.g. fatal internal errors).

Brent Writes Code
  • 19,075
  • 7
  • 52
  • 56
  • 10
    Is there any way to make it where it will be called if you press Ctrl+C or Ctrl+\? – RacecaR Oct 03 '10 at 15:08
  • 11
    It will be called if you press Ctrl+C. That simply raises a KeyboardInterrupt exception. – Ned Batchelder Oct 03 '10 at 15:11
  • 1
    Oh, I forgot that. And I assume that nothing you can do will be run if somebody kills the python process right? – RacecaR Oct 03 '10 at 15:11
  • 6
    @RacecaR: indeed; the point of killing a process is to stop it dead. From the docs: `Note The exit function is not called when the program is killed by a signal, when a Python fatal internal error is detected, or when os._exit() is called`. – Katriel Oct 03 '10 at 15:12
  • 30
    @RacecaR, the only way you can run termination code even if a process badly crashes or is brutally killed is in **another** process, known as a "monitor" or "watchdog", whose only job is to keep an eye on the target process and run the termination code when apropriate. Of course that requires a very different architecture and has its limitations; if you need such functionality it's best for you to open a different Q on the matter. – Alex Martelli Oct 03 '10 at 15:18
  • But you can listen to signals, see the signal module. – Jochen Ritzel Oct 03 '10 at 15:20
  • @Alex Martelli: your comment deserves to be a separate answer – Boris Gorelik Oct 03 '10 at 15:42
  • 1
    @NamGVU It should be. – Brent Writes Code Jul 21 '17 at 04:06
  • @katrielalex Not calling the exit function under any circumstances when the process is killed by a signal, if indeed true, would be very unfortunate: it should be possible to trap and handle in Python code some signals such as SIGTERM (the default signal sent by the `kill` command), SIGQUIT, and under some circumstances even SIGHUP. See [this link](http://programmergamer.blogspot.nl/2013/05/clarification-on-sigint-sigterm-sigkill.html). – ack Feb 17 '18 at 10:52
  • FWIW uses a LIFO (Last In, First Out) system for calling functions. – Mattwmaster58 Apr 15 '18 at 02:46
  • 1
    Not working in Python 3.9 – Volatil3 Jan 08 '23 at 07:08
44

If you want something to always run, even on errors, use try: finally: like this -

def main():
    try:
        execute_app()
    finally:
        handle_cleanup()

if __name__=='__main__':
    main()

If you want to also handle exceptions you can insert an except: before the finally:

Adriaan
  • 715
  • 10
  • 22
Brian C. Lane
  • 4,073
  • 1
  • 24
  • 23
29

If you stop the script by raising a KeyboardInterrupt (e.g. by pressing Ctrl-C), you can catch that just as a standard exception. You can also catch SystemExit in the same way.

try:
    ...
except KeyboardInterrupt:
    # clean up
    raise

I mention this just so that you know about it; the 'right' way to do this is the atexit module mentioned above.

Katriel
  • 120,462
  • 19
  • 136
  • 170
7

This is a version adapted from other answers. It should work (not fully tested) with graceful exits, kills, and PyCharm stop button (the last one I can confirm).

import signal
import atexit


def handle_exit(*args):
    try:
        ... do computation ...
    except BaseException as exception:
        ... handle the exception ...


atexit.register(handle_exit)
signal.signal(signal.SIGTERM, handle_exit)
signal.signal(signal.SIGINT, handle_exit)
GarouDan
  • 3,743
  • 9
  • 49
  • 75
4

If you have class objects, that exists during the whole lifetime of the program, you can also execute commands from the classes with the __del__(self) method:

class x:
def __init__(self):
    while True:
        print ("running")
        sleep(1)

def __del__(self):
    print("destructuring")


a = x()

this works on normal program end as well if the execution is aborted, for sure there will be some exceptions:

running
running
running
running
running
Traceback (most recent call last):
  File "x.py", line 14, in <module>
    a = x()
  File "x.py", line 8, in __init__
    sleep(1)
KeyboardInterrupt
destructuring
Rastefan
  • 49
  • 5
0

Based on answers here:

import sys
import atexit
import signal

def exit_handler():
    print("Cleaning up")

def kill_handler(*args):
    sys.exit(0)

atexit.register(exit_handler)
signal.signal(signal.SIGINT, kill_handler)
signal.signal(signal.SIGTERM, kill_handler)

# MAIN PROGRAM
# for example just reading from the input:
input("Press enter: ")

Alek
  • 634
  • 7
  • 7