How do you use python-daemon the way that it's documentation dictates?

m0meni picture m0meni · May 23, 2015 · Viewed 16k times · Source

I'm trying to make a daemon in python and I've come across the python-daemon package. The interesting thing about it is that the most common way I've seen it used isn't even what the documentation, which is very sparse, tells you to do

import os
import grp
import signal
import daemon
import lockfile

from spam import (
    initial_program_setup,
    do_main_program,
    program_cleanup,
    reload_program_config,
    )

context = daemon.DaemonContext(
    working_directory='/var/lib/foo',
    umask=0o002,
    pidfile=lockfile.FileLock('/var/run/spam.pid'),
    )

context.signal_map = {
    signal.SIGTERM: program_cleanup,
    signal.SIGHUP: 'terminate',
    signal.SIGUSR1: reload_program_config,
    }

mail_gid = grp.getgrnam('mail').gr_gid
context.gid = mail_gid

important_file = open('spam.data', 'w')
interesting_file = open('eggs.data', 'w')
context.files_preserve = [important_file, interesting_file]

initial_program_setup()

with context:
    do_main_program()

Instead, people use it like this:

#!/usr/bin/python
import time
from daemon import runner

class App():
    def __init__(self):
        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/tty'
        self.stderr_path = '/dev/tty'
        self.pidfile_path =  '/tmp/foo.pid'
        self.pidfile_timeout = 5
    def run(self):
        while True:
            print("Howdy!  Gig'em!  Whoop!")
            time.sleep(10)

app = App()
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()

Examples here and in this thread How do you create a daemon in Python?

So can anyone tell me how the package is supposed to be used as intended? There are 0 examples to be found that use it the way the documentation specifies.

Answer

abarnert picture abarnert · May 23, 2015

First, the reason you can't find good documentation is that, to the best of my knowledge, nobody ever wrote it. When Ben Finney proposed the PEP, there was plenty of interest, but then when he asked for someone else to take over the project and champion it, nobody did, so… beyond the PEP, and the sparse documentation in the docs directory of the project, there's really nothing but the source to explain things.

A DaemonContext is the way you're meant to create a daemon. Its API was bikeshedded extensively, and was the only part that was proposed to be part of the public interface in the stdlib. People from the Debian, Ubuntu, and RedHat/Fedora projects were involved in the initial discussion, and changes have been incorporated based on their experiences moving their distros to systemd.

A DaemonRunner wraps up a DaemonContext-based daemon and a control tool (ala apachectl). This implements a “service”, which is only one way of running a daemon out of many other different ways.

Often, you don't want this—if you want to build a "service", you usually want to only implement the daemon using a daemon.DaemonContext, and let systemd or launchd or their older predecessors manage the service by invoking that daemon. So, the PEP, to keep things simple, explicitly said that a service is outside the scope of what the daemon module should attempt.

But there is code for services in the python-daemon distribution. It isn't fully documented, because it is only an example of one way to use a daemon.

It does appear to work, and it's definitely been maintained and updated over the years. So, if you want an apachectl-type tool, I think it makes sense to use a DaemonRunner; just make sure you read the docstrings and write some tests to make sure it's doing what you wanted.