From 1ac225128ed34c00f5794875c2a258189b94e478 Mon Sep 17 00:00:00 2001 From: Mark Harfouche Date: Sun, 29 Oct 2023 22:12:52 -0400 Subject: [PATCH 1/2] Allow users to subclass FileLock with custom keyword arguments --- src/filelock/_api.py | 1 + tests/test_filelock.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/filelock/_api.py b/src/filelock/_api.py index a99d8a0a..2e9cdbad 100644 --- a/src/filelock/_api.py +++ b/src/filelock/_api.py @@ -87,6 +87,7 @@ def __new__( # noqa: PLR0913 thread_local: bool = True, # noqa: ARG003, FBT001, FBT002 *, is_singleton: bool = False, + **kwargs: dict[str, Any], # capture remaining kwargs for subclasses # noqa: ARG003 ) -> Self: """Create a new lock object or if specified return the singleton instance for the lock file.""" if not is_singleton: diff --git a/tests/test_filelock.py b/tests/test_filelock.py index a401a183..e35228d8 100644 --- a/tests/test_filelock.py +++ b/tests/test_filelock.py @@ -613,6 +613,21 @@ def test_lock_can_be_non_thread_local( lock.release(force=True) +@pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) +def test_subclass_compatibility(lock_type: type[BaseFileLock], tmp_path: Path) -> None: + class MyFileLock(lock_type): + def __init__( + self, + *args, # noqa: ANN002 + my_param: int = 0, + **kwargs, # noqa: ANN003 + ) -> None: + pass + + lock_path = tmp_path / "a" + MyFileLock(str(lock_path), my_param=1) + + @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_singleton_and_non_singleton_locks_are_distinct(lock_type: type[BaseFileLock], tmp_path: Path) -> None: lock_path = tmp_path / "a" From 9bc2e910db1d465743524b4f4973fe3b0ed1e215 Mon Sep 17 00:00:00 2001 From: Mark Harfouche Date: Mon, 30 Oct 2023 13:20:01 -0400 Subject: [PATCH 2/2] Lint --- tests/test_filelock.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/tests/test_filelock.py b/tests/test_filelock.py index e35228d8..4bb40b87 100644 --- a/tests/test_filelock.py +++ b/tests/test_filelock.py @@ -12,7 +12,7 @@ from pathlib import Path, PurePath from stat import S_IWGRP, S_IWOTH, S_IWUSR, filemode from types import TracebackType -from typing import TYPE_CHECKING, Callable, Iterator, Tuple, Type, Union +from typing import TYPE_CHECKING, Any, Callable, Iterator, Tuple, Type, Union from uuid import uuid4 import pytest @@ -613,20 +613,36 @@ def test_lock_can_be_non_thread_local( lock.release(force=True) -@pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) -def test_subclass_compatibility(lock_type: type[BaseFileLock], tmp_path: Path) -> None: - class MyFileLock(lock_type): - def __init__( +def test_subclass_compatibility(tmp_path: Path) -> None: + class MyFileLock(FileLock): + def __init__( # noqa: PLR0913 Too many arguments to function call (6 > 5) self, - *args, # noqa: ANN002 + lock_file: str | os.PathLike[str], + timeout: float = -1, + mode: int = 0o644, + thread_local: bool = True, my_param: int = 0, - **kwargs, # noqa: ANN003 + **kwargs: dict[str, Any], ) -> None: pass lock_path = tmp_path / "a" MyFileLock(str(lock_path), my_param=1) + class MySoftFileLock(SoftFileLock): + def __init__( # noqa: PLR0913 Too many arguments to function call (6 > 5) + self, + lock_file: str | os.PathLike[str], + timeout: float = -1, + mode: int = 0o644, + thread_local: bool = True, + my_param: int = 0, + **kwargs: dict[str, Any], + ) -> None: + pass + + MySoftFileLock(str(lock_path), my_param=1) + @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_singleton_and_non_singleton_locks_are_distinct(lock_type: type[BaseFileLock], tmp_path: Path) -> None: