From e7257771d5deb506e2cd507959a33e27df504a8c Mon Sep 17 00:00:00 2001 From: Shane Hu <49306726+shanehh@users.noreply.github.com> Date: Thu, 28 Apr 2022 11:13:20 +0800 Subject: [PATCH 1/2] Fix for Python 3 --- quicklock/__init__.py | 76 ++++++++++++++++++- quicklock/quicklock.py | 53 ------------- test/__init__.py | 0 test/{quicklock_test.py => test_quicklock.py} | 2 +- 4 files changed, 76 insertions(+), 55 deletions(-) delete mode 100644 quicklock/quicklock.py create mode 100644 test/__init__.py rename test/{quicklock_test.py => test_quicklock.py} (94%) diff --git a/quicklock/__init__.py b/quicklock/__init__.py index 0d9e271..5058b9b 100644 --- a/quicklock/__init__.py +++ b/quicklock/__init__.py @@ -1 +1,75 @@ -from quicklock import singleton +import os +import re +import json +import psutil +import logging +from pathlib import Path + +log = logging.getLogger("quicklock") + + +def test_lock(resource_name: str, lock_file: os.PathLike): + """ + Check if the process that locked the file is still running + """ + if lock_file.exists(): + locked = False + try: + with open(lock_file, "r") as lock_handle: + data = json.load(lock_handle) + + # Check if process is still running + if psutil.pid_exists(data["pid"]): + other_process = psutil.Process(data["pid"]) + if other_process.is_running() and other_process.name() == data["name"]: + locked = True + + # Something is wrong with the lockfile, just ignore it and create the lock below + except Exception: + pass + + # Resource was locked + if locked: + raise RuntimeError( + 'Resource <{}> is currently locked by '.format( + resource_name, other_process.pid, other_process.name() + ) + ) + + +def obtain_lock(resource_name: str, lock_file: os.PathLike): + """ + Create the lock with the current process information + """ + process = psutil.Process(os.getpid()) + with open(lock_file, "w") as lock_handle: + json.dump({"name": process.name(), "pid": process.pid}, lock_handle) + log.info( + 'Obtained exclusive lock on resource <{}> (this is )'.format( + resource_name, process.pid, process.name() + ) + ) + + +def singleton(resource_name: str, dirname=".lock"): + """ + Lock a resource name so that if this function is called again before the lock is + released, a RuntimeError will be raised. + + Arguments: + resource -- Name of the resource to lock + dirname -- Base directory to store lock files, default is `pwd`/.lock + """ + lock_root = Path(dirname) + + if not lock_root.exists(): + lock_root.mkdir() + + lock_name = re.sub(r"[^a-zA-Z0-9]+", "_", resource_name) + lock_file = (lock_root / lock_name).with_suffix(".lock") + + test_lock(resource_name, lock_file) + obtain_lock(resource_name, lock_file) + + +__all__ = ["singleton"] diff --git a/quicklock/quicklock.py b/quicklock/quicklock.py deleted file mode 100644 index c469a91..0000000 --- a/quicklock/quicklock.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -import re -import json -import psutil -import logging -import traceback - -log = logging.getLogger('quicklock') - -def singleton(resource, dirname='.lock'): - """ - Lock a resource name so that if this function is called again before the lock is - released, a RuntimeError will be raised. - - Arguments: - resource -- Name of the resource to lock - dirname -- Base directory to store lock files, default is `pwd`/.lock - """ - lock_root = os.path.realpath(dirname) - - if not os.path.exists(lock_root): - os.mkdir(lock_root) - - lock_name = re.sub(r'[^a-zA-Z0-9]+', '_', resource) - - lock_file = os.path.join(lock_root, lock_name + '.lock') - - # Check if the process that locked the file is still running - if os.path.exists(lock_file): - locked = False - try: - with open(lock_file, 'r') as lock_handle: - data = json.load(lock_handle) - - # Check if process is still running - if psutil.pid_exists(data['pid']): - other_process = psutil.Process(data['pid']) - if other_process.is_running() and other_process.name() == data['name']: - locked = True - - # Something is wrong with the lockfile, just ignore it and create the lock below - except Exception, exc: - pass - - # Resource was locked - if locked: - raise RuntimeError('Resource <{}> is currently locked by '.format(resource, other_process.pid, other_process.name())) - - # Create the lock with the current process information - process = psutil.Process(os.getpid()) - with open(lock_file, 'w') as lock_handle: - json.dump({"name": process.name(), "pid": process.pid}, lock_handle) - log.info('Obtained exclusive lock on resource <{}> (this is )'.format(resource, process.pid, process.name())) diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/quicklock_test.py b/test/test_quicklock.py similarity index 94% rename from test/quicklock_test.py rename to test/test_quicklock.py index e3db643..e752ecc 100644 --- a/test/quicklock_test.py +++ b/test/test_quicklock.py @@ -12,7 +12,7 @@ def test(self): time.sleep(60) print('>> Test Note: Quitting and releasing lock') - except RuntimeError, exc: + except RuntimeError as exc: print('>> Test Note: Lock was already in use') self.assertTrue(isinstance(exc, RuntimeError)) self.assertTrue('Resource is currently locked by' in exc.message) From bbac2881a914925d82ab58a1a5a653d2d01b8df9 Mon Sep 17 00:00:00 2001 From: Shane Hu <49306726+shanehh@users.noreply.github.com> Date: Thu, 28 Apr 2022 11:24:49 +0800 Subject: [PATCH 2/2] fix: allow deep dir --- quicklock/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quicklock/__init__.py b/quicklock/__init__.py index 5058b9b..0f306dc 100644 --- a/quicklock/__init__.py +++ b/quicklock/__init__.py @@ -51,7 +51,7 @@ def obtain_lock(resource_name: str, lock_file: os.PathLike): ) -def singleton(resource_name: str, dirname=".lock"): +def singleton(resource_name: str, dirname: os.PathLike = ".lock"): """ Lock a resource name so that if this function is called again before the lock is released, a RuntimeError will be raised. @@ -63,7 +63,7 @@ def singleton(resource_name: str, dirname=".lock"): lock_root = Path(dirname) if not lock_root.exists(): - lock_root.mkdir() + lock_root.mkdir(parents=True) lock_name = re.sub(r"[^a-zA-Z0-9]+", "_", resource_name) lock_file = (lock_root / lock_name).with_suffix(".lock")