diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 1a0413f..0774be5 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -26,7 +26,6 @@ jobs: - "3.11" - "3.10" - "3.9" - - "3.8" os: - ubuntu-latest - windows-latest @@ -58,11 +57,13 @@ jobs: env: PYTEST_ADDOPTS: "-vv --durations=20" DIFF_AGAINST: HEAD + UV_PYTHON_PREFERENCE: only-managed - name: Run test suite without coverage if: ${{ startsWith(matrix.py, 'pypy')}} run: tox run --skip-pkg-install -e ${{ matrix.py }} -- env: PYTEST_ADDOPTS: "-vv --durations=20" + UV_PYTHON_PREFERENCE: only-managed - name: Rename coverage report file if: ${{ !startsWith(matrix.py, 'pypy')}} run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 68e510e..acc1c5e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,9 +13,9 @@ repos: rev: v2.3.0 hooks: - id: codespell - additional_dependencies: ["tomli>=2.0.1"] + additional_dependencies: ["tomli>=2.2.1"] - repo: https://github.com/tox-dev/tox-ini-fmt - rev: "1.4.1" + rev: "1.5.0" hooks: - id: tox-ini-fmt args: ["-p", "fix"] @@ -34,7 +34,7 @@ repos: hooks: - id: prettier additional_dependencies: - - prettier@3.3.3 + - prettier@3.4.2 - "@prettier/plugin-xml@3.4.1" - repo: meta hooks: diff --git a/pyproject.toml b/pyproject.toml index 259d8e9..64a7ef1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ build-backend = "hatchling.build" requires = [ "hatch-vcs>=0.4", - "hatchling>=1.25", + "hatchling>=1.27", ] [project] @@ -20,7 +20,7 @@ license = "Unlicense" maintainers = [ { name = "Bernát Gábor", email = "gaborjbernat@gmail.com" }, ] -requires-python = ">=3.8" +requires-python = ">=3.9" classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", @@ -28,7 +28,6 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -43,19 +42,19 @@ dynamic = [ ] optional-dependencies.docs = [ "furo>=2024.8.6", - "sphinx>=8.0.2", - "sphinx-autodoc-typehints>=2.4.1", + "sphinx>=8.1.3", + "sphinx-autodoc-typehints>=3", ] optional-dependencies.testing = [ "covdefaults>=2.3", - "coverage>=7.6.1", - "diff-cover>=9.2", - "pytest>=8.3.3", - "pytest-asyncio>=0.24", - "pytest-cov>=5", + "coverage>=7.6.10", + "diff-cover>=9.2.1", + "pytest>=8.3.4", + "pytest-asyncio>=0.25.2", + "pytest-cov>=6", "pytest-mock>=3.14", "pytest-timeout>=2.3.1", - "virtualenv>=20.26.4", + "virtualenv>=20.28.1", ] optional-dependencies.typing = [ "typing-extensions>=4.12.2; python_version<'3.11'", @@ -75,7 +74,6 @@ build.targets.sdist.include = [ version.source = "vcs" [tool.ruff] -target-version = "py38" line-length = 120 format.preview = true format.docstring-code-line-length = 100 @@ -84,7 +82,6 @@ lint.select = [ "ALL", ] lint.ignore = [ - "ANN101", # Missing type annotation for `self` in method "COM812", # Conflict with formatter "CPY", # No copyright statements "D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible diff --git a/tests/test_async_filelock.py b/tests/test_async_filelock.py index 73eef14..643c667 100644 --- a/tests/test_async_filelock.py +++ b/tests/test_async_filelock.py @@ -91,32 +91,32 @@ async def test_non_blocking(lock_type: type[BaseAsyncFileLock], tmp_path: Path) assert not lock_5.is_locked # try to acquire lock 2 - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): await lock_2.acquire(blocking=False) assert not lock_2.is_locked assert lock_1.is_locked # try to acquire pre-parametrized `blocking=False` lock 3 with `acquire` - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): await lock_3.acquire() assert not lock_3.is_locked assert lock_1.is_locked # try to acquire pre-parametrized `blocking=False` lock 3 with context manager - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): async with lock_3: pass assert not lock_3.is_locked assert lock_1.is_locked # try to acquire pre-parametrized `timeout=0` lock 4 with `acquire` - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): await lock_4.acquire() assert not lock_4.is_locked assert lock_1.is_locked # try to acquire pre-parametrized `timeout=0` lock 4 with context manager - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): async with lock_4: pass assert not lock_4.is_locked @@ -124,13 +124,13 @@ async def test_non_blocking(lock_type: type[BaseAsyncFileLock], tmp_path: Path) # blocking precedence over timeout # try to acquire pre-parametrized `timeout=-1,blocking=False` lock 5 with `acquire` - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): await lock_5.acquire() assert not lock_5.is_locked assert lock_1.is_locked # try to acquire pre-parametrized `timeout=-1,blocking=False` lock 5 with context manager - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): async with lock_5: pass assert not lock_5.is_locked diff --git a/tests/test_filelock.py b/tests/test_filelock.py index 8808bf6..b071396 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, Any, Callable, Iterator, Tuple, Type, Union +from typing import TYPE_CHECKING, Any, Callable, Union from uuid import uuid4 from weakref import WeakValueDictionary @@ -21,6 +21,8 @@ from filelock import BaseFileLock, FileLock, SoftFileLock, Timeout, UnixFileLock, WindowsFileLock if TYPE_CHECKING: + from collections.abc import Iterator + from pytest_mock import MockerFixture @@ -218,7 +220,7 @@ def test_nested_contruct(lock_type: type[BaseFileLock], tmp_path: Path) -> None: assert not lock_1.is_locked -_ExcInfoType = Union[Tuple[Type[BaseException], BaseException, TracebackType], Tuple[None, None, None]] +_ExcInfoType = Union[tuple[type[BaseException], BaseException, TracebackType], tuple[None, None, None]] class ExThread(threading.Thread): @@ -304,7 +306,7 @@ def test_timeout(lock_type: type[BaseFileLock], tmp_path: Path) -> None: assert not lock_2.is_locked # try to acquire lock 2 - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): lock_2.acquire(timeout=0.1) assert not lock_2.is_locked assert lock_1.is_locked @@ -333,44 +335,44 @@ def test_non_blocking(lock_type: type[BaseFileLock], tmp_path: Path) -> None: assert not lock_5.is_locked # try to acquire lock 2 - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): lock_2.acquire(blocking=False) assert not lock_2.is_locked assert lock_1.is_locked # try to acquire pre-parametrized `blocking=False` lock 3 with `acquire` - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): lock_3.acquire() assert not lock_3.is_locked assert lock_1.is_locked # try to acquire pre-parametrized `blocking=False` lock 3 with context manager - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."), lock_3: + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."), lock_3: pass assert not lock_3.is_locked assert lock_1.is_locked # try to acquire pre-parametrized `timeout=0` lock 4 with `acquire` - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): lock_4.acquire() assert not lock_4.is_locked assert lock_1.is_locked # try to acquire pre-parametrized `timeout=0` lock 4 with context manager - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."), lock_4: + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."), lock_4: pass assert not lock_4.is_locked assert lock_1.is_locked # blocking precedence over timeout # try to acquire pre-parametrized `timeout=-1,blocking=False` lock 5 with `acquire` - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): lock_5.acquire() assert not lock_5.is_locked assert lock_1.is_locked # try to acquire pre-parametrized `timeout=-1,blocking=False` lock 5 with context manager - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."), lock_5: + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."), lock_5: pass assert not lock_5.is_locked assert lock_1.is_locked @@ -397,7 +399,7 @@ def test_default_timeout(lock_type: type[BaseFileLock], tmp_path: Path) -> None: assert not lock_2.is_locked # try to acquire lock 2 - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): lock_2.acquire() assert not lock_2.is_locked assert lock_1.is_locked @@ -405,7 +407,7 @@ def test_default_timeout(lock_type: type[BaseFileLock], tmp_path: Path) -> None: lock_2.timeout = 0 assert lock_2.timeout == 0 - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): lock_2.acquire() assert not lock_2.is_locked assert lock_1.is_locked @@ -459,7 +461,7 @@ def test_del(lock_type: type[BaseFileLock], tmp_path: Path) -> None: assert not lock_2.is_locked # try to acquire lock 2 - with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): + with pytest.raises(Timeout, match=r"The file lock '.*' could not be acquired."): lock_2.acquire(timeout=0.1) # delete lock 1 and try to acquire lock 2 again diff --git a/tox.ini b/tox.ini index c82819c..960e01e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] requires = - tox>=4.2 - tox-uv>=1.11.3 + tox>=4.23.2 + tox-uv>=1.17 env_list = fix 3.13 @@ -9,7 +9,6 @@ env_list = 3.11 3.10 3.9 - 3.8 type coverage docs @@ -39,7 +38,7 @@ description = format the code base to adhere to our styles, and complain about w base_python = python3.10 skip_install = true deps = - pre-commit>=3.8 + pre-commit>=4.0.1 commands = pre-commit run --all-files --show-diff-on-failure python -c 'import pathlib; print("hint: run \{\} install to add checks as pre-commit hook".format(pathlib.Path(r"{envdir}") / "bin" / "pre-commit"))' @@ -47,7 +46,7 @@ commands = [testenv:type] description = run type check on code base deps = - mypy==1.11.2 + mypy==1.14.1 set_env = {tty:MYPY_FORCE_COLOR = 1} commands = @@ -59,8 +58,8 @@ description = combine coverage files and generate diff (against DIFF_AGAINST def skip_install = true deps = covdefaults>=2.3 - coverage[toml]>=7.6.1 - diff-cover>=9.2 + coverage[toml]>=7.6.10 + diff-cover>=9.2.1 extras = parallel_show_output = true pass_env = @@ -93,9 +92,9 @@ commands = description = check that the long description is valid skip_install = true deps = - check-wheel-contents>=0.6 - twine>=5.1.1 - uv>=0.4.10 + check-wheel-contents>=0.6.1 + twine>=6.0.1 + uv>=0.5.18 commands = uv build --sdist --wheel --out-dir {envtmpdir} . twine check {envtmpdir}{/}*