Portalocker
Portalocker is a Python library that provides an easy-to-use API for cross-platform file locking. It supports file locking on Windows, Linux, BSD, and Unix systems, and can also facilitate distributed locking using Redis. The library is actively maintained, follows Semantic Versioning, and provides a convenient context manager for lock management.
Warnings
- gotcha On Linux and Unix systems, file locks implemented by `portalocker` are advisory by default. This means other processes might ignore the lock and access the file, potentially leading to race conditions or data corruption if they do not also use `portalocker` or respect advisory locks. Mandatory locking requires specific filesystem mount options (-o mand), which is generally not recommended.
- gotcha When using `portalocker.Lock` with file modes like 'w' (write) or 'w+' (write and read), the file is opened and potentially truncated *before* the lock is acquired. This can lead to data loss if another process is simultaneously attempting to write to the file. For safe writing, consider using mode 'a' (append) or 'a+' (append and read) with `truncate=0` or `truncate=None` (or `portalocker.utils.open_atomic`).
- gotcha Data written to a locked file might remain in a buffer and not be immediately visible to other processes or persisted to disk until `file.flush()` and `os.fsync(file.fileno())` are called, or the file is closed. Relying solely on `file.close()` might still leave data in OS buffers.
- breaking Python 2.x is no longer supported. `portalocker` versions 2.0 and above require Python 3. If you are on an older Python 2 environment, you must use `portalocker<2`.
Install
-
pip install portalocker -
pip install "portalocker[redis]"
Imports
- lock
from portalocker import lock
- unlock
from portalocker import unlock
- Lock
from portalocker import Lock
- RLock
from portalocker import RLock
- BoundedSemaphore
from portalocker import BoundedSemaphore
- RedisLock
from portalocker import RedisLock
- LockFlags
from portalocker import LockFlags
- AlreadyLocked
from portalocker.exceptions import AlreadyLocked
Quickstart
import portalocker
import os
file_path = 'my_locked_file.txt'
# Using the Lock context manager (recommended)
# The file is created/opened with 'a' mode, preventing truncation before lock
with portalocker.Lock(file_path, timeout=5, mode='a', truncate=0) as fh:
print(f"Acquired lock on {file_path}. PID: {os.getpid()}")
fh.write(f"Hello from process {os.getpid()}!\n")
fh.flush()
os.fsync(fh.fileno())
print("Wrote data and flushed.")
# Lock is automatically released when exiting the 'with' block
print(f"Released lock on {file_path}.")
# Example of manual locking (less common, use with care)
# try:
# f = open(file_path, 'r+')
# portalocker.lock(f, portalocker.LockFlags.EXCLUSIVE)
# print(f"Manually acquired lock on {file_path}")
# f.seek(0)
# f.write(f"Manual write from process {os.getpid()}\n")
# f.flush()
# os.fsync(f.fileno())
# finally:
# portalocker.unlock(f)
# f.close()
# print(f"Manually released lock on {file_path}")