Skip to content

Instantly share code, notes, and snippets.

@carlosabalde
Created August 24, 2019 10:16
Show Gist options
  • Save carlosabalde/95796c80dbf1b45fa0568370361fa7db to your computer and use it in GitHub Desktop.
Save carlosabalde/95796c80dbf1b45fa0568370361fa7db to your computer and use it in GitHub Desktop.
Simple Python fire & forget skeleton useful to launch heavy tasks as background processes
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
- Requirements:
+ python-daemon==2.2.3
'''
from __future__ import absolute_import, division, print_function, unicode_literals
import argparse
import daemon
import errno
import os
import signal
import sys
import time
from lockfile import pidlockfile
###############################################################################
## TASK
###############################################################################
def task(options):
sys.stdout.write('Starting task execution...\n')
print(options)
time.sleep(10)
sys.stdout.write('Gracefully stopping task execution...\n')
###############################################################################
## FIRE & FORGET
###############################################################################
def fire_and_forget(options):
if options.pidfile is not None:
pid = pidlockfile.read_pid_from_pidfile(options.pidfile)
if pid is not None and is_pid_running(pid):
sys.stderr.write("PID {} in pidfile '{}' is already running.\n".format(
pid, options.pidfile))
sys.exit(1)
pidlockfile.remove_existing_pidfile(options.pidfile)
with daemon.DaemonContext(
pidfile=\
pidlockfile.PIDLockFile(options.pidfile) \
if options.pidfile is not None else None,
detach_process=False if options.nodetach else True,
stdout=sys.stdout if options.nodetach else None,
stderr=sys.stderr if options.nodetach else None):
if options.timeout > 0:
signal.signal(signal.SIGALRM, sigalrm_handler)
signal.alarm(options.timeout)
task(options)
if options.pidfile is not None:
pidlockfile.remove_existing_pidfile(options.pidfile)
def sigalrm_handler(signum, frame):
sys.stderr.write('Abruptly stopping due to SIGALRM event.\n')
sys.exit(1)
def is_pid_running(pid):
try:
os.kill(pid, signal.SIG_DFL)
except OSError as e:
if e.errno == errno.ESRCH:
return False
return True
###############################################################################
## MAIN
###############################################################################
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--pidfile', type=str, default=None)
parser.add_argument(
'--timeout', type=int, default=300)
parser.add_argument(
'--nodetach', action='store_const', const=True, default=False)
parser.add_argument(
'--foo', type=str, default=None)
parser.add_argument(
'--bar', type=str, default=None)
fire_and_forget(parser.parse_args())
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment