Changelog¶
4.1.0:
Documentation release. No runtime behaviour changed; the only edits to executable code replaced two
...bodies on the abstractLockBase.acquire/LockBase.releasestubs with docstrings.Every module, class, function, private helper and dunder method in the package now carries a Google-style docstring.
ruff’s pydocstyle rules are enabled forportalocker/with no exemptionsAdded seven narrative guides: quickstart, lock types, platform behaviour, Redis locks, the
combineCLI, troubleshooting, and a 3.x to 4.0.0 migration guide. The API reference moved underdocs/api/and a changelog page was added
docs/platforms.rstdocuments the advisory-versus-mandatory distinction,flockversuslockf, msvcrt versus pywin32, and the networked-filesystem caveats that cause most locking confusion
docs/cli.rstdocuments the combiner’s ASCII-only requirement onportalocker/*.py,README.rstandLICENSE, which was previously only discoverable by reading__main__.pyDoctests now run for
docs/*.rstandREADME.rstas well as the package, so every documented example is executed on each supported platform and interpreter. No# doctest: +SKIPremains anywhereFixed the README telling readers to unlock a filehandle it had already closed, which raised
ValueError: I/O operation on closed fileFixed the README demonstrating
BoundedSemaphorewithout aname, which the library itself deprecates in favour ofNamedBoundedSemaphoreRemoved the obsolete Python 2 installation section from the README
4.0.0:
Fixed
open_atomic()replacing a destination created while its context was open on POSIX; publication now raisesFileExistsErrorand preserves the concurrent winner (#114)Python 3.10 or later is now required; Python 3.9 (EOL) support dropped
pywin32is no longer installed by default on Windows; the msvcrt-based locker is the default and works dependency-free for exclusive locks. Shared locks on Windows requireportalocker[win32], and an informativeImportErroris raised otherwise (#104)POSIX lock exceptions now populate
.strerrorand pass the message as a second positional argument, matching the Windows exception contract. This changes thestr()output of these exceptions on POSIX from the bareOSErrortext to a 2-tuple repr
LockBaseis now generic over the acquire return type (typing-only change; downstreamLocksubclasses are unaffected)Added
PidFileLockfor pidfile-based locking (#106)Added
PidFileLock.fail_closed()for ownership-only contexts; contention raisesAlreadyLockedbefore entering the body and exposes the competing PID throughAlreadyLocked.holder_pidwhen readable (#118)Packaging switched to the
uv_buildbackend; releases are published to PyPI through GitHub Actions Trusted Publishing
python -m portalocker combine:--output-filenow opens lazily; the vendored single-file output correctly includesRedisLock(it was alwaysNonebefore); the smoke-run now usessys.executable(fixes Windows)Fixed
PidFileLockignoringtimeoutwhenfail_when_locked=False; all PID-publication failures now transactionally release the sidecar and preserve the publication error if cleanup also fails (#116)Fixed a
TemporaryFileLock/PidFileLockunlock-then-unlink race that could let two processes hold the same lock (split-brain); release now unlinks before unlocking on POSIX and acquire re-verifies file identity (inode) after locking (#115)Fixed
BoundedSemaphorestaying permanently “Already locked” after a non-contention error (e.g. a missing directory)Fixed
RedisLockcrashed-holder detection: the liveness check was always satisfied by its own subscribe confirmation, so dead holders were never reaped; the ping is now published only after the subscription is confirmed active, pubsub connections no longer leak on the reap path,fail_when_locked=Truefails fast instead of polling the full timeout, internally-created connections are closed on release, and a failedacquire()rolls back cleanly so the lock can be retriedAdded shared
RedisLockreaders throughLockFlags.SHARED. Waiting writers gate new readers to prevent starvation, holder-specific heartbeats preserve stale-client cleanup, and legacy Redis lock responses are treated as exclusive for mixed-version safety (#124)Fixed
FlockLocker/LockfLockeron POSIX to use their named syscall (previously silently used the globalLOCKER); the module-levellock()/unlock()on POSIX now accept all documentedLOCKERforms (tuple/instance/class)Fixed the Windows msvcrt locker locking from the current file position instead of byte 0 for raw file descriptors, which could break mutual exclusion on files larger than 64KiB; unexpected Win32 errors now raise
LockExceptionper the documented contract
RedisLocktests now run everywhere viafakeredis, with a live Redis server still tested in CI
Lock.release()continues suppressing unlock and close errors by default; closing is always attempted and the file handle reference is cleared. Callers can opt into reporting cleanup failures withLock(..., raise_on_release_error=True)(#117)
TemporaryFileLock.release()andPidFileLock.release()are now no-ops when the object does not hold the lock, so a stale object (double release, or garbage collection of a failed acquire) can no longer unlink the lock file out from under the current holder
TemporaryFileLockno longer keeps a strongatexitreference to itself, so unused instances can be garbage collected; cleanup at interpreter exit still happens via a weak reference
NamedBoundedSemaphoreis now exported from the top-levelportalockernamespace
For newer changes please look at the comments for the Git tags: https://github.com/WoLpH/portalocker/tags
For more details the commit log for the master branch could be useful: https://github.com/WoLpH/portalocker/commits/master
1.5:
Moved tests to prevent collisions with other packages
1.4:
Added optional file open parameters
1.3:
Improved documentation
Added file handle to locking exceptions
1.2:
Added signed releases and tags to PyPI and Git
1.1:
Added support for Python 3.6+
Using real time to calculate timeout
1.0:
Complete code refactor.
Splitting of code in logical classes
100% test coverage and change in API behaviour
The default behavior of the Lock class has changed to append instead of write/truncate.
0.6:
Added msvcrt support for Windows
0.5:
Python 3 support
0.4:
Fixing a few bugs, added coveralls support, switched to py.test and added 100% test coverage.
Fixing exception thrown when fail_when_locked is true
Fixing exception “Lock object has no attribute ‘_release_lock’” when fail_when_locked is true due to the call to Lock._release_lock() which fails because _release_lock is not defined.
0.3:
Now actually returning the file descriptor from the Lock class
0.2:
Added Lock class to help prevent cache race conditions
0.1:
Initial release