Python Only Allow Single Instance to Run (or Kill Previous Instance)

December 13, 2020

Install pip install psutil

Store pid and command line of current running instance.

NOTE: This will not work in Windows due to fcntl. Refer to tendo.

import fcntl
import psutil

lockfile_fp = None

def check_instance_running(lockfile):
    global lockfile_fp

    lockfile_fp = open(lockfile, 'a')

    try:
        fcntl.lockf(lockfile_fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
        lockfile_fp.seek(0)
        lockfile_fp.truncate()
        # lockfile_fp.write(str(os.getpid()))
        lockfile_fp.write(json.dumps({
            'pid': os.getpid(),
            'cmdline': psutil.Process(os.getpid()).cmdline()
        }))
        lockfile_fp.flush()
        running = False
    except IOError:
        with open(lockfile, 'r') as fp:
            # running = fp.read()
            running = json.loads(fp.read())
        # running = True

    return running

Only allow single instance to run

lockfile = 'test.lock'
data = check_instance_running(lockfile)
if data:
    raise Exception(f"Already running: {data['pid']}")

print('START')
while True:
    print('.', end='', flush=True)
    time.sleep(5)

Kill previous instance

lockfile = 'test.lock'
data = check_instance_running(lockfile)
if data:
    p = psutil.Process(data['pid'])
    # check cmdline, as pid could be recyled

    cmdline = p.cmdline()
    # check script and arguments must be the same
    # if (p.cmdline() == data['cmdline']):    
    # check if same script file only: cmdline[0]==python, cmdline[1]==scriptname, cmdline[2..]==arguments
    if len(cmdline) >= 2 and len(data['cmdline']) >= 2 and cmdline[1] == data['cmdline'][1]:
        print(f"Kill Previous Process: {data['pid']}")
        p.terminate()
        p.wait()
        # create new lock file
        util.check_instance_running(lockfile)

print('START')
while True:
    print('.', end='', flush=True)
    time.sleep(5)
This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.