Skip to content

Commit 4555608

Browse files
Add ability to return immediately when a lock cannot be obtained inst… (#142)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 08a292e commit 4555608

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

src/filelock/_api.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ def acquire(
116116
poll_interval: float = 0.05,
117117
*,
118118
poll_intervall: float | None = None,
119+
blocking: bool = True,
119120
) -> AcquireReturnProxy:
120121
"""
121122
Try to acquire the file lock.
@@ -124,6 +125,8 @@ def acquire(
124125
if ``timeout < 0``, there is no timeout and this method will block until the lock could be acquired
125126
:param poll_interval: interval of trying to acquire the lock file
126127
:param poll_intervall: deprecated, kept for backwards compatibility, use ``poll_interval`` instead
128+
:param blocking: defaults to True. If False, function will return immediately if it cannot obtain a lock on the
129+
first attempt. Otherwise this method will block until the timeout expires or the lock is acquired.
127130
:raises Timeout: if fails to acquire lock within the timeout period
128131
:return: a context object that will unlock the file when the context is exited
129132
@@ -172,6 +175,9 @@ def acquire(
172175
if self.is_locked:
173176
_LOGGER.debug("Lock %s acquired on %s", lock_id, lock_filename)
174177
break
178+
elif blocking is False:
179+
_LOGGER.debug("Failed to immediately acquire lock %s on %s", lock_id, lock_filename)
180+
raise Timeout(self._lock_file)
175181
elif 0 <= timeout < time.monotonic() - start_time:
176182
_LOGGER.debug("Timeout on acquiring lock %s on %s", lock_id, lock_filename)
177183
raise Timeout(self._lock_file)

tests/test_filelock.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,29 @@ def test_timeout(lock_type: type[BaseFileLock], tmp_path: Path) -> None:
259259
assert not lock_2.is_locked
260260

261261

262+
@pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock])
263+
def test_non_blocking(lock_type: type[BaseFileLock], tmp_path: Path) -> None:
264+
# raises Timeout error when the lock cannot be acquired
265+
lock_path = tmp_path / "a"
266+
lock_1, lock_2 = lock_type(str(lock_path)), lock_type(str(lock_path))
267+
268+
# acquire lock 1
269+
lock_1.acquire()
270+
assert lock_1.is_locked
271+
assert not lock_2.is_locked
272+
273+
# try to acquire lock 2
274+
with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."):
275+
lock_2.acquire(blocking=False)
276+
assert not lock_2.is_locked
277+
assert lock_1.is_locked
278+
279+
# release lock 1
280+
lock_1.release()
281+
assert not lock_1.is_locked
282+
assert not lock_2.is_locked
283+
284+
262285
@pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock])
263286
def test_default_timeout(lock_type: type[BaseFileLock], tmp_path: Path) -> None:
264287
# test if the default timeout parameter works

0 commit comments

Comments
 (0)