daemon-application 2.0.1
A simple python package for creating daemon applications.
License: MIT
Requires Python: !=3.2.0,>=2.7
Keywords: daemon-application

# daemon-application

## Description

A simple python package for creating daemon applications.

*Notice:*

- *Runs the application in daemon mode on Linux only. On Windows, the application runs in foreground model.*


## Install

```
pip install daemon-application
```

## Usage

### Example for raw APIs

```
import time
import threading
import signal
from daemon_application import daemon_start

stopflag = False

def main():
    def on_exit(*args, **kwargs):
        with open("backgroud.log", "a", encoding="utf-8") as fobj:
            print("process got exit signal...", file=fobj)
            print(args, file=fobj)
            print(kwargs, file=fobj)
        global stopflag
        stopflag = True
    signal.signal(signal.SIGTERM, on_exit)
    signal.signal(signal.SIGINT, on_exit)
    while not stopflag:
        time.sleep(1)
        print(time.time())

if __name__ == "__main__":
    print("start background application...")
    daemon_start(main, "background.pid", True)
```

### Example for DaemonApplication

```
import time
from daemon_application import DaemonApplication

class HelloApplication(DaemonApplication):
    def main(self):
        while True:
            print("hello")
            time.sleep(1)

controller = HelloApplication().get_controller()

if __name__ == "__main__":
    controller()

```

### Example for DaemonApplication adding new global options

```
import time
import click
from daemon_application import DaemonApplication

class HelloApplication(DaemonApplication):

    def get_main_options(self):
        options = [
            click.option("-m", "--message", default="hello")
        ]
        return options + super().get_main_options()

    def main(self):
        while True:
            print(self.config["message"])
            time.sleep(1)

controller = HelloApplication().get_controller()

if __name__ == "__main__":
    controller()
```

*The output of the command help that added a new global option*

```
Usage: example.py [OPTIONS] COMMAND [ARGS]...

Options:
  --pidfile TEXT          pidfile file path.
  --workspace TEXT        Set running folder
  --daemon / --no-daemon  Run application in background or in foreground.
  -c, --config TEXT       Config file path. Application will search config
                          file if this option is missing. Use sub-command
                          show-config-fileapaths to get the searching tactics.

  -m, --message TEXT
  --help                  Show this message and exit.

Commands:
  restart                Restart Daemon application.
  show-config-filepaths  Print out the config searching paths.
  start                  Start daemon application.
  stop                   Stop daemon application.
```

### Example of graceful-stop-application using daemon-application and graceful-sigterm.

```python
import time
import signal
import logging
import sigterm # pip install graceful-sigterm
from daemon_application import DaemonApplication

_logger = logging.getLogger(__name__)


class HelloApplication(DaemonApplication):
    def main(self):
        # setup sigterm
        sigterm.setup()
        sigterm.setup(signal.SIGINT)
        sigterm.register_worker(self._main)
        sigterm.execute()

    def _main(self):
        # start application main
        while not sigterm.is_stopped():
            _logger.info("hello")
            time.sleep(1)
        _logger.info("gracefully stopped!")


controller = HelloApplication().get_controller()

if __name__ == "__main__":
    controller()
```

Start the application, and kill by the pid. The application output looks like:

```log
test@test daemon-application % python t1.py --no-daemon start
Start application without config file.
2023-09-11 10:13:44,186 INFO 7634 8205548160base daemon_start 174 Start application in FRONT mode, pid=7634.
2023-09-11 10:13:44,187 INFO 7634 8205548160sigterm setup 88 signal setup: sig=15, handler=<function default_handler at 0x100beaa20>
2023-09-11 10:13:44,187 INFO 7634 8205548160sigterm setup 88 signal setup: sig=2, handler=<function default_handler at 0x100beaa20>
2023-09-11 10:13:44,187 INFO 7634 8205548160sigterm execute 94 start workers...
2023-09-11 10:13:44,187 INFO 7634 6180237312t1 _main 19 hello
2023-09-11 10:13:45,192 INFO 7634 6180237312t1 _main 19 hello
2023-09-11 10:13:46,197 INFO 7634 6180237312t1 _main 19 hello
2023-09-11 10:13:47,202 INFO 7634 6180237312t1 _main 19 hello
2023-09-11 10:13:48,208 INFO 7634 6180237312t1 _main 19 hello
2023-09-11 10:13:49,214 INFO 7634 6180237312t1 _main 19 hello
2023-09-11 10:13:50,219 INFO 7634 6180237312t1 _main 19 hello
2023-09-11 10:13:50,404 INFO 7634 8205548160sigterm default_handler 51 get TERM signal, prepare to stop server...
2023-09-11 10:13:51,224 INFO 7634 6180237312t1 _main 21 gracefully stopped!
2023-09-11 10:13:51,225 INFO 7634 8205548160sigterm execute 101 worker <Thread(Thread-1 (_main), stopped daemon 6180237312)> end.
2023-09-11 10:13:51,225 INFO 7634 8205548160sigterm execute 104 main thread end.
```

Look into the log, you will see `gracefully stopped!`. The kill command is not kill the main process, but set the is_stopped flag, and the application gracefully exit by it's logical controller.

## Configs

### Config items and default values

- pidfile: app.pid
- stop-timeout: 30
- stop-signal: SIGINT
- daemon: True
- workspace: ""
- loglevel: INFO
- logfile: app.log
- logfmt: default

### services fields

- class: class path string, e.g. zenutils.serviceutils.DebugService
- args: []
- kwargs: {}

## Note

Logging is ENABLED as `logutils.setup(**self.config)` by default.

## Test Passed With Pythons

- 2.7
- 3.3
- 3.4
- 3.5
- 3.6
- 3.7
- 3.8
- 3.9
- 3.10
- 3.11

## Release

### v2.0.1

- Doc update.

### v2.0.0

- Remove rpc related parts. *Notice:* SimpleRpcApplication Based Application should change deps to `daemon-application~=1.0.5`.

### v1.0.5

- Change rpcutils, make it more easy to use.

### v0.5.10

- Use dictutils.deep_merge to update config.

### v0.5.9

- Unit test passed.

### v0.5.8

- Work with zenutils.socketserverutils.

### v0.5.7

- Improve the srpcd command.

### v0.5.6

- Add SimpleRpcServer class and srpcd command.

### v0.5.5

- Config in DaemonApplication is set to the dictutils.Object class for ease use while remaining compatible with all dict operations.

### v0.5.4

- Doc update.

### v0.5.3

- Add DaemonApplication.load_config, so that you can start DaemonApplication service directly by your code.
- Add stop_timeout for daemon_stop. If stop timeout, kill process tree by force.

### v0.5.2

- Add global options: loglevel, logfile, logfmt.
- Update default_config override mechanism.

### v0.4.4

- Fix the problem in sub-command stop.

### v0.4.3

- Deps on pyyaml.

### v0.4.2

- Remove a print() statement.

### v0.4.1

- Fix documents URLs.

### v0.4.0

- Remove fastutils deps.
- Add `--config` global command option for DaemonApplication.
- Provide a way to override the global options for subclass of DaemonApplication.
- The sub-command `restart` will do just `start` if the old application is not running or crashed.
- Use gitee.com source code hosting service.

### v0.3.3

- Fix show-config-filepaths.

### v0.3.2

- Add click deps in requirements.txt

### v0.3.1

- Add DaemonApplication.

### v0.3.0

- New wrapper.

### v0.2.1

- Old releases.