Created
August 24, 2019 10:16
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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