From 000a3fa3586ff231dfc564ccf90987ff3852431e Mon Sep 17 00:00:00 2001 From: nefrob <25070989+nefrob@users.noreply.github.com> Date: Tue, 9 Apr 2024 11:09:25 -0600 Subject: [PATCH] Raise error on incompatible singleton timeout and mode args (#320) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bernát Gábor --- src/filelock/_api.py | 12 ++++++++++-- tests/test_filelock.py | 11 +++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/filelock/_api.py b/src/filelock/_api.py index 7bec3ce..210b8c4 100644 --- a/src/filelock/_api.py +++ b/src/filelock/_api.py @@ -82,8 +82,8 @@ class BaseFileLock(ABC, contextlib.ContextDecorator): def __new__( # noqa: PLR0913 cls, lock_file: str | os.PathLike[str], - timeout: float = -1, # noqa: ARG003 - mode: int = 0o644, # noqa: ARG003 + timeout: float = -1, + mode: int = 0o644, thread_local: bool = True, # noqa: ARG003, FBT001, FBT002 *, is_singleton: bool = False, @@ -97,6 +97,9 @@ def __new__( # noqa: PLR0913 if not instance: instance = super().__new__(cls) cls._instances[str(lock_file)] = instance + elif timeout != instance.timeout or mode != instance.mode: + msg = "Singleton lock instances cannot be initialized with differing arguments" + raise ValueError(msg) return instance # type: ignore[return-value] # https://github.com/python/mypy/issues/15322 @@ -174,6 +177,11 @@ def timeout(self, value: float | str) -> None: """ self._context.timeout = float(value) + @property + def mode(self) -> int: + """:return: the file permissions for the lockfile""" + return self._context.mode + @abstractmethod def _acquire(self) -> None: """If the file lock could be acquired, self._context.lock_file_fd holds the file descriptor of the lock file.""" diff --git a/tests/test_filelock.py b/tests/test_filelock.py index f9a4e5f..9d3bd25 100644 --- a/tests/test_filelock.py +++ b/tests/test_filelock.py @@ -676,6 +676,17 @@ def test_singleton_locks_are_distinct_per_lock_file(lock_type: type[BaseFileLock assert lock_1 is not lock_2 +@pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) +def test_singleton_locks_must_be_initialized_with_the_same_args(lock_type: type[BaseFileLock], tmp_path: Path) -> None: + lock_path = tmp_path / "a" + lock = lock_type(str(lock_path), is_singleton=True) # noqa: F841 + + with pytest.raises(ValueError, match="Singleton lock instances cannot be initialized with differing arguments"): + lock_type(str(lock_path), timeout=10, is_singleton=True) + with pytest.raises(ValueError, match="Singleton lock instances cannot be initialized with differing arguments"): + lock_type(str(lock_path), mode=0, is_singleton=True) + + @pytest.mark.skipif(hasattr(sys, "pypy_version_info"), reason="del() does not trigger GC in PyPy") @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_singleton_locks_are_deleted_when_no_external_references_exist(