The correct way to clean up a Python service is atexit, signal, try / finally

I have one Python 2.7 threading daemon / service running on Debian. I have a critical cleanup code that disables some hardware features.

import sys import atexit import signal from my_job import give_high_fives from my_cleanup import prevent_the_apocalypse atexit.register(prevent_the_apocalypse) signal.signal(signal.SIGTERM, prevent_the_apocalypse) signal.signal(signal.SIGHUP, prevent_the_apocalypse) try: while True: give_high_fives() finally: prevent_the_apocalypse() 

It looks paranoid, and I also don't like calling the cleanup code so many times. Now it looks like cleaning is called 3 or 4 times on SIGTERM .

Is there one single way to prevent_the_apocalypse exactly once in all possible exit conditions?

+6
source share
2 answers

Writing the right daemon in Python is difficult. In fact, it is difficult in any language. PEP 3143 explains the problems.

The daemon module wraps most of the details, so you don’t need to fix them. If you use this, it’s very easy to add a cleanup code.

One option is to simply subclass daemon.DaemonContext and put it there. For instance:

 class MyDaemonContext(daemon.DaemonContext): def close(self): if not self.is_open: return prevent_the_apocalypse() super(MyDaemonContext, self).close() with MyDaemonContext(): while True: give_high_fives() 

The daemon module already installs signal handlers to do what you configured them, but without missing the close method. ( close will be executed exactly once - in the context of __exit__ , in the atexit method or, possibly, in another place.)

If you need something more complicated, where some signals skip close and others don't, just set its signal_map appropriately instead of a subclass.

+6
source

Your signal handlers have nothing to do but set the global variable to true. Then check this variable in your main loop and break out if it is true by running the cleanup handler on exit.

-1
source

Source: https://habr.com/ru/post/955611/


All Articles