Last active
December 8, 2025 22:12
-
-
Save JavaScriptDude/b436bfda31e765ef7a9916890c786138 to your computer and use it in GitHub Desktop.
Cross Platform Example of Single Instance Checking in Python
This file contains hidden or 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
| import time, sys, os | |
| class SingleInstanceChecker: | |
| def __init__(self, id): | |
| if isWin(): | |
| ensure_win32api() | |
| self.mutexname = id | |
| self.lock = win32event.CreateMutex(None, False, self.mutexname) | |
| self.running = (win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS) | |
| else: | |
| ensure_fcntl() | |
| self.lock = open(f"/tmp/isnstance_{id}.lock", 'wb') | |
| try: | |
| fcntl.lockf(self.lock, fcntl.LOCK_EX | fcntl.LOCK_NB) | |
| self.running = False | |
| except IOError: | |
| self.running = True | |
| def already_running(self): | |
| return self.running | |
| def __del__(self): | |
| if self.lock: | |
| try: | |
| if isWin(): | |
| win32api.CloseHandle(self.lock) | |
| else: | |
| os.close(self.lock) | |
| except Exception as ex: | |
| pass | |
| # --------------------------------------- | |
| # Utility Functions | |
| # Dynamically load win32api on demand | |
| # Install with: pip install pywin32 | |
| win32api=winerror=win32event=None | |
| def ensure_win32api(): | |
| global win32api,winerror,win32event | |
| if win32api is None: | |
| import win32api | |
| import winerror | |
| import win32event | |
| # Dynamically load fcntl on demand (python stdlib) | |
| fcntl=None | |
| def ensure_fcntl(): | |
| global fcntl | |
| if fcntl is None: | |
| import fcntl | |
| def isWin(): | |
| return (os.name == 'nt') | |
| # --------------------------------------- | |
| def main(argv): | |
| _timeout = 10 | |
| print("main() called. sleeping for %s seconds" % _timeout) | |
| time.sleep(_timeout) | |
| print("DONE") | |
| if __name__ == '__main__': | |
| SCR_NAME = "my_script" | |
| sic = SingleInstanceChecker(SCR_NAME) | |
| if sic.already_running(): | |
| print("An instance of {} is already running.".format(SCR_NAME)) | |
| sys.exit(1) | |
| else: | |
| main(sys.argv[1:]) |
Author
Author
The code definitely works and I've used it in dozens of usecases where I develop the code in linux and deploy in windows in some cases. I use this code for my scheduled task / cron based jobs.
@JavaScriptDude Apologies if I gave the impression the code doesn't work - it does work. My code base is typed and so wanted to type this. I'll investigate further.
Author
No problem at all. Glad to have my open source code being helpful.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Although I've never used
mypy. I suspect thatmypyis getting confused by the dynamic loading of the platform specific library viaensure_win32apiandensure_fcntl. I'm sure if you made a linux and windows specific versions and explicitly load the respective library, mypy will not have issues.There may be a more pythonic way to do this type of platform specific dynamic loading that would make mypy happy but I don't have the bandwidth to investigate. Suggestions would be appreciated.