Locket
Locket is a Python library (version 1.0.0, last released April 2022) that provides file-based locks for inter-process communication on both Linux and Windows. It offers a simple API for creating non-reentrant locks that behave similarly to `threading.Lock` instances, uniquely identified by the file path being locked. The library ensures that only one process can acquire a lock at a time for a given path, preventing conflicts when multiple processes access shared resources.
Warnings
- gotcha Locket provides non-reentrant locks. This means a thread cannot acquire a lock it already holds without blocking or raising a `LockError` if a timeout is specified. If you need reentrant behavior, use `threading.RLock` or ensure your code structure avoids re-acquiring the same lock.
- breaking The behavior of Locket's file-based locks after a process `fork` is undefined. This means using `locket` directly with `multiprocessing` modules that rely on forking might lead to unpredictable results or deadlocks.
- gotcha Calling `release()` on an already unlocked lock will raise a `locket.LockError`. This typically indicates a logic error where `release()` is called more times than `acquire()` or when a lock is not held by the current process/thread.
- deprecated The last release of Locket (version 1.0.0) was in April 2022. While functional, the library has not received recent updates, which might imply a maintenance-only status. Users should be aware that it may not receive updates for newer Python versions or address new edge cases/bugs promptly.
Install
-
pip install locket
Imports
- locket
import locket
- LockError
from locket import LockError
Quickstart
import locket
import os
import time
# Define a lock file path, using an environment variable for flexibility
lock_file_path = os.environ.get('LOCKET_EXAMPLE_PATH', './my_app.lock')
print(f"Attempting to acquire lock on {lock_file_path}...")
try:
# Acquire the lock using a context manager, with a 5-second timeout
# If another process holds the lock, this will block for up to 5 seconds.
# If the lock cannot be acquired, a LockError is raised.
with locket.lock_file(lock_file_path, timeout=5):
print(f"Lock acquired on {lock_file_path}. Performing critical action...")
# Simulate work within the critical section
time.sleep(2)
print("Critical action complete. Lock will be released automatically.")
except locket.LockError:
print(f"Could not acquire lock on {lock_file_path} within the timeout. Another process might hold it.")
except Exception as e:
print(f"An unexpected error occurred: {e}")
print("\nDemonstrating explicit acquire/release (less recommended)...")
lock = None
try:
# Acquire the lock explicitly
lock = locket.lock_file(lock_file_path, timeout=1)
lock.acquire()
print(f"Lock acquired on {lock_file_path} (manual). Performing critical action...")
time.sleep(1)
print("Critical action complete (manual).")
finally:
# Always ensure the lock is released, even if errors occur
if lock:
try:
lock.release()
print("Lock released (manual).")
except locket.LockError:
print("Warning: Attempted to release an already unlocked lock. This should not happen if `release()` is paired with `acquire()`.")