{"id":557,"library":"portalocker","title":"Portalocker","description":"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.","status":"active","version":"3.2.0","language":"python","source_language":"en","source_url":"https://github.com/wolph/portalocker","tags":["file locking","concurrency","inter-process communication","distributed locking","windows","linux","posix","context manager"],"install":[{"cmd":"pip install portalocker","lang":"bash","label":"Install core library"},{"cmd":"pip install \"portalocker[redis]\"","lang":"bash","label":"Install with Redis support"}],"dependencies":[{"reason":"Required for using Redis-based distributed locks (`portalocker.RedisLock`).","package":"redis","optional":true}],"imports":[{"note":"For low-level, manual locking. The context manager `Lock` is generally preferred.","symbol":"lock","correct":"from portalocker import lock"},{"note":"For low-level, manual unlocking. The context manager `Lock` handles unlocking automatically.","symbol":"unlock","correct":"from portalocker import unlock"},{"symbol":"Lock","correct":"from portalocker import Lock"},{"note":"Reentrant lock, similar to `threading.RLock` but for processes.","symbol":"RLock","correct":"from portalocker import RLock"},{"symbol":"BoundedSemaphore","correct":"from portalocker import BoundedSemaphore"},{"note":"Requires `portalocker[redis]` extra to be installed.","symbol":"RedisLock","correct":"from portalocker import RedisLock"},{"symbol":"LockFlags","correct":"from portalocker import LockFlags"},{"symbol":"AlreadyLocked","correct":"from portalocker.exceptions import AlreadyLocked"}],"quickstart":{"code":"import portalocker\nimport os\n\nfile_path = 'my_locked_file.txt'\n\n# Using the Lock context manager (recommended)\n# The file is created/opened with 'a' mode, preventing truncation before lock\nwith portalocker.Lock(file_path, timeout=5, mode='a', truncate=0) as fh:\n    print(f\"Acquired lock on {file_path}. PID: {os.getpid()}\")\n    fh.write(f\"Hello from process {os.getpid()}!\\n\")\n    fh.flush()\n    os.fsync(fh.fileno())\n    print(\"Wrote data and flushed.\")\n# Lock is automatically released when exiting the 'with' block\nprint(f\"Released lock on {file_path}.\")\n\n# Example of manual locking (less common, use with care)\n# try:\n#     f = open(file_path, 'r+')\n#     portalocker.lock(f, portalocker.LockFlags.EXCLUSIVE)\n#     print(f\"Manually acquired lock on {file_path}\")\n#     f.seek(0)\n#     f.write(f\"Manual write from process {os.getpid()}\\n\")\n#     f.flush()\n#     os.fsync(f.fileno())\n# finally:\n#     portalocker.unlock(f)\n#     f.close()\n#     print(f\"Manually released lock on {file_path}\")","lang":"python","description":"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."},"warnings":[{"fix":"Ensure all processes accessing the file use `portalocker` or coordinate access through other means. Be aware of the advisory nature on POSIX systems.","message":"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.","severity":"gotcha","affected_versions":"All versions"},{"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:`","message":"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`).","severity":"gotcha","affected_versions":"All versions"},{"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.","message":"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.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Upgrade to Python 3 (recommended) or pin `portalocker` to a version less than 2.0 (`pip install \"portalocker<2\"`).","message":"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`.","severity":"breaking","affected_versions":">=2.0.0"}],"env_vars":null,"last_verified":"2026-05-12T15:06:53.216Z","next_check":"2026-06-26T00:00:00.000Z","problems":[{"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.","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.","error":"portalocker.exceptions.LockException: [Errno 11] Resource temporarily unavailable"},{"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)`.","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.","error":"redis.exceptions.ConnectionError: Error 10061 connecting to localhost:6379. No connection could be made because the target machine actively refused it."},{"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.","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.","error":"PermissionError: [Errno 13] Permission denied"},{"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.","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).","error":"ImportError: cannot import name 'portalocker'"},{"fix":"pip install portalocker","cause":"The 'portalocker' Python package has not been installed in your current environment.","error":"ModuleNotFoundError: No module named 'portalocker'"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":0,"quickstart_tag":"stale","pypi_latest":null,"cli_name":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.4,"mem_mb":10.5,"disk_size":"21.8M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.05,"mem_mb":2.4,"disk_size":"17.9M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.34,"mem_mb":10.5,"disk_size":"22M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.05,"mem_mb":2.4,"disk_size":"18M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.64,"mem_mb":12.1,"disk_size":"24.3M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.1,"mem_mb":2.8,"disk_size":"19.8M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.52,"mem_mb":12.1,"disk_size":"25M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.12,"mem_mb":2.8,"disk_size":"20M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.96,"mem_mb":12,"disk_size":"16.0M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.07,"mem_mb":2.7,"disk_size":"11.7M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.91,"mem_mb":12,"disk_size":"17M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.12,"mem_mb":2.7,"disk_size":"12M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.79,"mem_mb":12.2,"disk_size":"15.6M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.07,"mem_mb":2.5,"disk_size":"11.3M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.9,"mem_mb":12.2,"disk_size":"16M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.1,"mem_mb":2.3,"disk_size":"12M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.38,"mem_mb":9.9,"disk_size":"20.6M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.07,"mem_mb":2.4,"disk_size":"17.4M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.46,"mem_mb":9.9,"disk_size":"21M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.07,"mem_mb":2.4,"disk_size":"18M"}]},"quickstart_checks":{"last_tested":"2026-04-23","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","exit_code":1},{"runtime":"python:3.10-slim","exit_code":1},{"runtime":"python:3.11-alpine","exit_code":1},{"runtime":"python:3.11-slim","exit_code":1},{"runtime":"python:3.12-alpine","exit_code":1},{"runtime":"python:3.12-slim","exit_code":1},{"runtime":"python:3.13-alpine","exit_code":1},{"runtime":"python:3.13-slim","exit_code":1},{"runtime":"python:3.9-alpine","exit_code":1},{"runtime":"python:3.9-slim","exit_code":1}]}}