Portalocker

raw JSON →
3.2.0 verified Tue May 12 auth: no python install: verified quickstart: stale

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.

pip install portalocker
error portalocker.exceptions.LockException: [Errno 11] Resource temporarily unavailable
cause This error occurs when a process attempts to acquire a file lock, but the file is already locked by another process, and the acquisition attempt is non-blocking or times out before the lock can be obtained.
fix
To resolve this, you can either implement a retry mechanism for acquiring the lock with a suitable timeout, or configure portalocker.Lock to wait by setting an appropriate timeout parameter (which defaults to None for infinite wait, but can be set to a float for a specific duration). If fail_when_locked=True (the default for Lock), you might catch portalocker.exceptions.AlreadyLocked for immediate failure, or set fail_when_locked=False to make it block until timeout.
error redis.exceptions.ConnectionError: Error 10061 connecting to localhost:6379. No connection could be made because the target machine actively refused it.
cause This error specifically occurs when using `portalocker.RedisLock` and the Python application is unable to establish a connection to the Redis server. This is commonly due to the Redis server not running, being configured to listen on a different host/port, or being blocked by a firewall.
fix
Verify that your Redis server is running and accessible from the machine where your Python application is executing. Ensure that the host and port specified when initializing portalocker.RedisLock (or the underlying Redis connection) correctly match your Redis server's configuration. For example: import portalocker; import redis; client = redis.Redis(host='your_redis_host', port=6379); lock = portalocker.RedisLock('my_channel', connection=client).
error PermissionError: [Errno 13] Permission denied
cause This operating system error indicates that the Python process does not have the necessary permissions (read, write, or execute) to access, create, or modify the lock file or the directory where the lock file is being created.
fix
Grant the user running the Python script the appropriate read and write permissions for the directory where portalocker attempts to create its lock files, and for the file being locked itself. This often involves using chmod and chown commands on Unix-like systems or adjusting security settings on Windows.
error ImportError: cannot import name 'portalocker'
cause This error typically means the `portalocker` package is not installed in the active Python environment, or there is an issue with the Python environment path. Less commonly, a `SyntaxError` during import can occur if a version of `portalocker` that uses Python 3+ specific syntax (like type hints) is run with an older Python interpreter (e.g., Python 3.5 or earlier).
fix
First, ensure portalocker is correctly installed in your current Python environment using pip install portalocker. If you are working with older Python versions, consider upgrading your Python interpreter to 3.7+ or installing a compatible older version of portalocker (e.g., pip install 'portalocker<2' for Python 2.x environments if absolutely necessary). Verify that your Python interpreter is pointing to the correct environment where the package is installed.
error ModuleNotFoundError: No module named 'portalocker'
cause The 'portalocker' Python package has not been installed in your current environment.
fix
pip install portalocker
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.
fix Ensure all processes accessing the file use `portalocker` or coordinate access through other means. Be aware of the advisory nature on POSIX systems.
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`).
fix Use modes 'a' or 'a+' with `Lock(..., truncate=0)` to avoid premature truncation, or use `portalocker.utils.open_atomic` for atomic file writes. For example: `with portalocker.Lock('somefile', mode='a', truncate=0) as fh:`
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.
fix Always call `fh.flush()` followed by `os.fsync(fh.fileno())` after writing critical data to ensure it's written to disk within the locked section.
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`.
fix Upgrade to Python 3 (recommended) or pin `portalocker` to a version less than 2.0 (`pip install "portalocker<2"`).
pip install "portalocker[redis]"
python os / libc variant status wheel install import disk
3.10 alpine (musl) redis - - 0.40s 21.8M
3.10 alpine (musl) portalocker - - 0.05s 17.9M
3.10 slim (glibc) redis - - 0.34s 22M
3.10 slim (glibc) portalocker - - 0.05s 18M
3.11 alpine (musl) redis - - 0.64s 24.3M
3.11 alpine (musl) portalocker - - 0.10s 19.8M
3.11 slim (glibc) redis - - 0.52s 25M
3.11 slim (glibc) portalocker - - 0.12s 20M
3.12 alpine (musl) redis - - 0.96s 16.0M
3.12 alpine (musl) portalocker - - 0.07s 11.7M
3.12 slim (glibc) redis - - 0.91s 17M
3.12 slim (glibc) portalocker - - 0.12s 12M
3.13 alpine (musl) redis - - 0.79s 15.6M
3.13 alpine (musl) portalocker - - 0.07s 11.3M
3.13 slim (glibc) redis - - 0.90s 16M
3.13 slim (glibc) portalocker - - 0.10s 12M
3.9 alpine (musl) redis - - 0.38s 20.6M
3.9 alpine (musl) portalocker - - 0.07s 17.4M
3.9 slim (glibc) redis - - 0.46s 21M
3.9 slim (glibc) portalocker - - 0.07s 18M

This quickstart demonstrates the recommended way to use `portalocker` with the `Lock` context manager to safely acquire and release file locks, ensuring data integrity across processes. It explicitly shows flushing and syncing the file content to disk.

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}")