Yet another way to get a single instance application. This recipe uses file locking only.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | import sys
import os
try:
import fcntl
except ImportError:
fcntl = None
LOCK_PATH = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "lock")
OS_WIN = False
if 'win32' in sys.platform.lower():
OS_WIN = True
class SingleInstance:
def __init__(self):
self.fh = None
self.is_running = False
self.do_magic()
def do_magic(self):
if OS_WIN:
try:
if os.path.exists(LOCK_PATH):
os.unlink(LOCK_PATH)
self.fh = os.open(LOCK_PATH, os.O_CREAT | os.O_EXCL | os.O_RDWR)
except EnvironmentError as err:
if err.errno == 13:
self.is_running = True
else:
raise
else:
try:
self.fh = open(LOCK_PATH, 'w')
fcntl.lockf(self.fh, fcntl.LOCK_EX | fcntl.LOCK_NB)
except EnvironmentError as err:
if self.fh is not None:
self.is_running = True
else:
raise
def clean_up(self):
# this is not really needed
try:
if self.fh is not None:
if OS_WIN:
os.close(self.fh)
os.unlink(LOCK_PATH)
else:
fcntl.lockf(self.fh, fcntl.LOCK_UN)
self.fh.close() # ???
os.unlink(LOCK_PATH)
except Exception as err:
# logger.exception(err)
raise # for debugging porpuses, do not raise it on production
if __name__ == "__main__":
import time
si = SingleInstance()
try:
if si.is_running:
sys.exit("This app is already running!")
time.sleep(20) # remove
# do other stuff
finally:
si.clean_up()
|
Well, as long as the file is created, it's a reliable solution to this common issue.
If the file can not be created, it's your desission to exit or ignore it, allowing multiple instances.
Even if the process dies, the file should get unlocked (tested on Windows).
For multi session/users, you must create the file on the user directory
LOCK_PATH = os.path.join(os.path.expanduser("~"), "lock")