27d28b8eef
The default encoding for source files is UTF-8 since Python 3, so we can drop all encoding headers. While we are at it, just replace them with SPDX headers.
84 lines
3.1 KiB
Python
84 lines
3.1 KiB
Python
# SPDX-License-Identifier: MIT
|
|
# Copied from: https://github.com/pycontribs/tendo
|
|
# License: PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
|
# Author: Sorin Sbarnea
|
|
# Changes: changed logging and formatting
|
|
import sys
|
|
import os
|
|
import errno
|
|
import tempfile
|
|
from .output import out
|
|
|
|
|
|
class SingleInstance(object):
|
|
"""
|
|
To prevent a script from running in parallel instantiate the
|
|
SingleInstance() class. If is there another instance
|
|
already running it will exit the application with the message
|
|
"Another instance is already running, quitting.",
|
|
returning an error code of -1.
|
|
|
|
>>> me = SingleInstance()
|
|
|
|
This is very useful to execute scripts by crontab that should only run
|
|
one at a time.
|
|
|
|
Note that this works by creating a lock file with a filename based
|
|
on the full path to the script file.
|
|
"""
|
|
|
|
def __init__(self, flavor_id="", exit_code=-1):
|
|
"""Create an exclusive lockfile or exit with an error and the given
|
|
exit code."""
|
|
self.initialized = False
|
|
scriptname = os.path.splitext(os.path.realpath(sys.argv[0]))[0]
|
|
lockname = scriptname.replace("/", "-").replace(":", "").replace("\\", "-")
|
|
if flavor_id:
|
|
lockname += "-%s" % flavor_id
|
|
lockname += '.lock'
|
|
tempdir = tempfile.gettempdir()
|
|
self.lockfile = os.path.normpath(os.path.join(tempdir, lockname))
|
|
out.debug("SingleInstance lockfile: " + self.lockfile)
|
|
if sys.platform == 'win32':
|
|
try:
|
|
# file already exists, try to remove it in case the previous
|
|
# execution was interrupted
|
|
if os.path.exists(self.lockfile):
|
|
os.unlink(self.lockfile)
|
|
self.fd = os.open(self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR)
|
|
except OSError:
|
|
type, e, tb = sys.exc_info()
|
|
if e.errno == errno.EACCES: # EACCES == 13
|
|
self.exit(exit_code)
|
|
raise
|
|
else: # non Windows
|
|
import fcntl
|
|
self.fp = open(self.lockfile, 'w')
|
|
try:
|
|
fcntl.lockf(self.fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
# raises IOError on Python << 3.3, else OSError
|
|
except (IOError, OSError):
|
|
self.exit(exit_code)
|
|
self.initialized = True
|
|
|
|
def exit(self, exit_code):
|
|
"""Exit with an error message and the given exit code."""
|
|
out.error("Another instance is already running, quitting.")
|
|
sys.exit(exit_code)
|
|
|
|
def __del__(self):
|
|
"""Remove the lock file."""
|
|
if not self.initialized:
|
|
return
|
|
try:
|
|
if sys.platform == 'win32':
|
|
if hasattr(self, 'fd'):
|
|
os.close(self.fd)
|
|
os.unlink(self.lockfile)
|
|
else:
|
|
import fcntl
|
|
fcntl.lockf(self.fp, fcntl.LOCK_UN)
|
|
if os.path.isfile(self.lockfile):
|
|
os.unlink(self.lockfile)
|
|
except Exception as e:
|
|
out.exception("could not remove lockfile: %s" % e)
|