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..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,6 +613,37 @@ def test_lock_can_be_non_thread_local( lock.release(force=True) +def test_subclass_compatibility(tmp_path: Path) -> None: + class MyFileLock(FileLock): + 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 + + 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: lock_path = tmp_path / "a"