Source code for sniputils.daemon

"""
make current process run as daemon

.. note::
    need install ``python-daemon``

https://www.python.org/dev/peps/pep-3143

https://pypi.org/project/python-daemon/

Usage:

.. code:: python

    # deamon.py
    from sniputils.logsetting import reset_logbase
    reset_logbase('xxx.log')

    from sniputils.deamon import daemon
    with daemon:
        main()

.. code:: bash

    # use reset_logbase to set logging file
    $ export DAEMON_ERR_LOG='error.log'
    $ python deamon.py

    # its like the command via nohup
    $ nohup python -m main > xxx.log 2> error.log &
"""

import os
import signal
import sys
from logging import FileHandler, Logger

import lockfile
from daemon import DaemonContext

from ..snippets import ensure_dir_exist

DEFALUT_ERROR_LOG_FILE = 'error.log'
signal.signal(signal.SIGHUP, signal.SIG_IGN)


def freeze_logger_file_handlers():
    handlers = []
    handlers.extend(Logger.root.handlers)
    for logger in Logger.manager.loggerDict.values():
        if hasattr(logger, 'handlers'):
            handlers.extend(logger.handlers)
    return handlers


def preserve_logger(handler):
    if isinstance(handler, FileHandler):
        return handler.stream


def stderr_log(err=os.environ.get('DAEMON_ERR_LOG') or DEFALUT_ERROR_LOG_FILE):
    if err:
        ensure_dir_exist(err)
        stderr = open(err, 'a', encoding='utf-8')
        return stderr


class Daemon(DaemonContext):
    def terminate(self, signal_number, stack_frame):
        self.__exit__(None, None, None)
        sys.exit(0)


# ref: https://www.python.org/dev/peps/pep-3143
daemon = Daemon(
    working_directory=os.getcwd(),
    pidfile=lockfile.FileLock('.daemon.pid'),
    stderr=stderr_log(),
    files_preserve=freeze_logger_file_handlers(),
    signal_map={
        signal.SIGHUP: None,
        signal.SIGTERM: 'terminate'
    }
)