portalocker.utils module¶
High level locking utilities built on the low level lockers.
Where portalocker.portalocker wraps a single locking syscall, this module turns that primitive into ready-to-use objects: context managers that open, lock, retry, unlock and close a file for you.
The hierarchy:
LockBase: the abstract base. It owns the
timeout/check_interval/fail_when_lockedretry semantics that every other class here inherits, and the LockBase._timeout_generator that implements them.Lock: the workhorse. Opens a file, locks it, and hands the filehandle to the caller.
RLock: a Lock that may be acquired several times by the same instance and is only released once the acquire count drops to zero.
TemporaryFileLock: a Lock whose lock file is unlinked on release, including at interpreter exit.
PidFileLock: a TemporaryFileLock that publishes the owning PID, so a contender can report who holds the lock instead of merely failing.
BoundedSemaphore / NamedBoundedSemaphore: N-slot counting semaphores built from N separate lock files.
open_atomic is unrelated to locking: it writes to a temporary file and renames it into place, so readers never observe a partially written file.
Only Lock and open_atomic are listed in this module’s __all__, but
every concrete class above is re-exported from the portalocker package
itself. The abstract LockBase is not; import it from here.
Example
>>> import portalocker
>>> with portalocker.Lock('somefile', 'w', timeout=1) as fh:
... _ = fh.write('the file is locked for the duration of the block')
See also
portalocker.portalocker: the platform specific locking primitives this module builds on.
- class portalocker.utils.Lock(filename, mode='a', timeout=None, check_interval=0.25, fail_when_locked=False, flags=<LockFlags.EXCLUSIVE|NON_BLOCKING: 6>, *, raise_on_release_error=False, **file_open_kwargs)[source]¶
-
Lock manager with built-in timeout.
The class most users want. It opens filename, locks it, retries for as long as the inherited retry policy allows, and hands the open filehandle to the caller. Releasing unlocks and closes that handle; the file itself stays behind, which is what makes the lock usable as a plain data file as well as a mutex.
Example
>>> import portalocker >>> with portalocker.Lock('somefile', 'w', timeout=1) as fh: ... _ = fh.write('locked while the block runs')
Warning
The file is opened before it is locked, so letting open truncate would discard another holder’s data before anybody checks whether the lock is free. That is why a mode containing
wis silently turned intoaand the truncation is deferred to Lock._prepare_fh, which runs only after the lock has been taken.Note
Locking is per open filehandle, not per process. Two Lock instances on the same path in the same process do contend with each other, which is what makes single process examples and tests meaningful.
See also
LockBase: documents the
timeout,check_intervalandfail_when_lockedretry semantics shared by every lock. RLock: the reentrant variant. TemporaryFileLock: removes the lock file on release.- Parameters:
- acquire(timeout=None, check_interval=None, fail_when_locked=None)[source]¶
Open the file, lock it and return the filehandle.
Calling this on a lock that is already held is cheap and safe: the filehandle taken earlier is returned as is, without touching the operating system.
- Parameters:
- Returns:
The open, locked filehandle. It is stored on the instance as well, and stays valid until release.
- Raises:
AlreadyLocked – The first attempt found the file locked and fail_when_locked was set.
LockException – Retrying did not help and timeout expired; the exception is the last one the locking call produced. Also raised, wrapping the original, when something other than contention goes wrong, such as the locking backend refusing the flags.
OSError – Opening the file failed, for instance because the directory does not exist or the mode is not permitted. Only locking failures are translated; failures from open propagate untouched.
- Warns:
UserWarning – A timeout was passed while the lock uses blocking flags, where it has no effect.
- Return type:
Example
>>> import portalocker >>> lock = portalocker.Lock('somefile', timeout=1) >>> fh = lock.acquire() >>> fh is lock.acquire() True >>> lock.release()
- release()[source]¶
Unlock and close the file handle, if this instance holds one.
Doing nothing when no lock is held is deliberate: __del__ calls this on every lock that is collected. Unlocking and closing are both always attempted and the stored handle is always cleared, even when one of the two fails.
- Raises:
Exception – Only when the lock was built with
raise_on_release_error=True. The first failure of the unlock and close pair is raised, chained from the second if both failed. By default such failures are swallowed.- Return type:
None
- portalocker.utils.open_atomic(filename, binary=True)[source]¶
Open a new file for atomic writing without replacing an existing file.
The destination must not exist when entering or publishing the context. If another actor creates it while the context is open, publication raises
FileExistsErrorand leaves that destination untouched.The implementation writes and synchronizes a temporary file in the destination directory, then publishes it with an operation that refuses an existing destination. Windows uses an atomic rename; POSIX uses an atomic hard link, so the POSIX filesystem must support hard links.
https://docs.python.org/3/library/os.html#os.link
>>> filename = 'test_file.txt' >>> if os.path.exists(filename): ... os.remove(filename)
>>> with open_atomic(filename) as fh: ... written = fh.write(b'test') >>> assert os.path.exists(filename) >>> os.remove(filename)
>>> import pathlib >>> path_filename = pathlib.Path('test_file.txt')
>>> with open_atomic(path_filename) as fh: ... written = fh.write(b'test') >>> assert path_filename.exists() >>> path_filename.unlink()