It would be useful for locklib to provide a built-in lock implementation that satisfies the context-lock interface while deliberately doing no locking.
In throng, we currently keep a small local implementation for this purpose:
from types import TracebackType
from typing import Optional, Type
class EmptyLock:
"""
Provide the context-lock interface while deliberately doing no locking.
Temporary isolates receive this lock so they can reuse ``DirectoryIsolate``
without acquiring the serialization policy of the local plugin.
"""
def __enter__(self) -> None:
"""Enter a no-op critical section."""
self.acquire()
def __exit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType]) -> None:
"""Leave a no-op critical section without suppressing exceptions."""
self.release()
def acquire(self) -> None:
"""Accept a lock acquisition request without blocking."""
def release(self) -> None:
"""Accept a matching lock release request without side effects."""
Why this is useful
Some code is written against a lock-like interface because the operation may need serialization in one backend, but not in another.
For example, throng has two filesystem-backed isolate modes:
- a local isolate, where multiple operations share one directory and must be serialized;
- a temporary isolate, where each isolate owns a fresh directory and does not need that serialization.
Both modes can reuse the same implementation if the lock is injected as a dependency. The local mode passes a real lock, while the temporary mode passes a no-op lock.
This keeps the main implementation simpler:
with self.lock:
return self._run_unlocked(...)
The caller decides whether self.lock actually serializes anything.
Proposed API
A small class like this could live in locklib, for example:
from locklib import EmptyLock
Possible names:
EmptyLock
NoOpLock
NullLock
EmptyLock matches the existing naming in throng, but NoOpLock may be more immediately recognizable.
Expected behavior
The lock should:
- implement the same context-manager shape as other locklib-compatible locks;
- expose
acquire() and release();
- never block;
- never suppress exceptions from the wrapped block;
- have no side effects;
- be reusable across repeated
with lock: blocks.
Minimal behavior example:
lock = EmptyLock()
with lock:
pass
lock.acquire()
lock.release()
Benefit
Providing this in locklib would avoid each downstream project having to define its own local no-op lock, while still encouraging dependency injection of lock behavior instead of branching inside business logic.
It would be useful for
locklibto provide a built-in lock implementation that satisfies the context-lock interface while deliberately doing no locking.In
throng, we currently keep a small local implementation for this purpose:Why this is useful
Some code is written against a lock-like interface because the operation may need serialization in one backend, but not in another.
For example,
thronghas two filesystem-backed isolate modes:Both modes can reuse the same implementation if the lock is injected as a dependency. The local mode passes a real lock, while the temporary mode passes a no-op lock.
This keeps the main implementation simpler:
The caller decides whether
self.lockactually serializes anything.Proposed API
A small class like this could live in
locklib, for example:Possible names:
EmptyLockNoOpLockNullLockEmptyLockmatches the existing naming inthrong, butNoOpLockmay be more immediately recognizable.Expected behavior
The lock should:
acquire()andrelease();with lock:blocks.Minimal behavior example:
Benefit
Providing this in
locklibwould avoid each downstream project having to define its own local no-op lock, while still encouraging dependency injection of lock behavior instead of branching inside business logic.