Skip to content

Commit 4ed0ca2

Browse files
authored
🔧 chore: modernize tooling and bump deps (#470)
Replace mypy with ty for type checking as it provides faster feedback and simpler configuration. Adopt pytest 9's native TOML config format ([tool.pytest] with dotted keys). Drop the unused prettier xml plugin and configure print-width/prose-wrap to match project conventions. Remove the outdated black code style badge since the project uses ruff. Fix release.yml bot author exclusion patterns to use the [bot] suffix. Bump all dev dependencies to their latest releases. Signed-off-by: Bernát Gábor <[email protected]>
1 parent c45841d commit 4ed0ca2

File tree

10 files changed

+75
-89
lines changed

10 files changed

+75
-89
lines changed

‎.github/release.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
changelog:
22
exclude:
33
authors:
4-
- dependabot
5-
- pre-commit-ci
4+
- dependabot[bot]
5+
- pre-commit-ci[bot]

‎.pre-commit-config.yaml‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ repos:
1313
rev: v2.4.1
1414
hooks:
1515
- id: codespell
16-
additional_dependencies: ["tomli>=2.2.1"]
16+
additional_dependencies: ["tomli>=2.4"]
1717
- repo: https://github.com/tox-dev/tox-toml-fmt
18-
rev: "v1.2.2"
18+
rev: "v1.5.4"
1919
hooks:
2020
- id: tox-toml-fmt
2121
- repo: https://github.com/tox-dev/pyproject-fmt
22-
rev: "v2.11.1"
22+
rev: "v2.15.2"
2323
hooks:
2424
- id: pyproject-fmt
2525
- repo: https://github.com/astral-sh/ruff-pre-commit
26-
rev: "v0.14.14"
26+
rev: "v0.15.0"
2727
hooks:
2828
- id: ruff-format
2929
- id: ruff-check
@@ -32,9 +32,7 @@ repos:
3232
rev: "v3.8.1" # Use the sha / tag you want to point at
3333
hooks:
3434
- id: prettier
35-
additional_dependencies:
36-
37-
- "@prettier/[email protected]"
35+
args: ["--print-width=120", "--prose-wrap=always"]
3836
- repo: meta
3937
hooks:
4038
- id: check-hooks-apply

‎README.md‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
# filelock
22

33
[![PyPI](https://img.shields.io/pypi/v/filelock)](https://pypi.org/project/filelock/)
4-
[![Supported Python
5-
versions](https://img.shields.io/pypi/pyversions/filelock.svg)](https://pypi.org/project/filelock/)
6-
[![Documentation
7-
status](https://readthedocs.org/projects/py-filelock/badge/?version=latest)](https://py-filelock.readthedocs.io/en/latest/?badge=latest)
8-
[![Code style:
9-
black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
4+
[![Supported Python versions](https://img.shields.io/pypi/pyversions/filelock.svg)](https://pypi.org/project/filelock/)
5+
[![Documentation status](https://readthedocs.org/projects/py-filelock/badge/?version=latest)](https://py-filelock.readthedocs.io/en/latest/?badge=latest)
106
[![Downloads](https://static.pepy.tech/badge/filelock/month)](https://pepy.tech/project/filelock)
117
[![check](https://github.com/tox-dev/py-filelock/actions/workflows/check.yaml/badge.svg)](https://github.com/tox-dev/py-filelock/actions/workflows/check.yaml)
128

‎pyproject.toml‎

Lines changed: 36 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
build-backend = "hatchling.build"
33
requires = [
44
"hatch-vcs>=0.5",
5-
"hatchling>=1.27",
5+
"hatchling>=1.28",
66
]
77

88
[project]
@@ -54,39 +54,37 @@ dev = [
5454
{ include-group = "test" },
5555
{ include-group = "type" },
5656
]
57-
5857
test = [
5958
"covdefaults>=2.3",
60-
"diff-cover>=9.7.1",
61-
"pytest>=8.4.2",
62-
"pytest-asyncio>=1.2",
59+
"diff-cover>=10.2",
60+
"pytest>=9.0.2",
61+
"pytest-asyncio>=1.3",
6362
"pytest-cov>=7",
6463
"pytest-mock>=3.15.1",
6564
"pytest-timeout>=2.4",
66-
"virtualenv>=20.34",
65+
"virtualenv>=20.36.1",
6766
]
6867
type = [
69-
"mypy>=1.18.2",
70-
"typing-extensions>=4.15; python_version<'3.11'",
68+
"ty>=0.0.16",
7169
{ include-group = "test" },
7270
]
7371
docs = [
74-
"furo>=2025.9.25",
75-
"sphinx>=8.2.3",
76-
"sphinx-autodoc-typehints>=3.2",
72+
"furo>=2025.12.19",
73+
"sphinx>=9.1",
74+
"sphinx-autodoc-typehints>=3.6.2",
75+
]
76+
coverage = [
77+
"covdefaults>=2.3",
78+
"coverage[toml]>=7.13.4",
79+
"diff-cover>=10.2",
7780
]
7881
fix = [
79-
"pre-commit-uv>=4.1.5",
82+
"pre-commit-uv>=4.2",
8083
]
8184
pkg-meta = [
8285
"check-wheel-contents>=0.6.3",
8386
"twine>=6.2",
84-
"uv>=0.8.22",
85-
]
86-
coverage = [
87-
"covdefaults>=2.3",
88-
"coverage[toml]>=7.10.7",
89-
"diff-cover>=9.7.1",
87+
"uv>=0.10.2",
9088
]
9189

9290
[tool.hatch]
@@ -112,7 +110,7 @@ lint.ignore = [
112110
"D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible
113111
"D205", # 1 blank line required between summary line and description
114112
"D212", # `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible
115-
"D301", # Use `r"""` if any backslashes in a docstring
113+
"D301", # Use `r"""` if any backslashes in a docstring
116114
"D401", # First line of docstring should be in imperative mood
117115
"DOC", # no support yet
118116
"ISC001", # Conflict with formatter
@@ -143,46 +141,39 @@ ignore-words-list = "master"
143141
[tool.pyproject-fmt]
144142
max_supported_python = "3.14"
145143

146-
[tool.pytest.ini_options]
147-
asyncio_default_fixture_loop_scope = "session"
148-
testpaths = [
144+
[tool.pytest]
145+
ini_options.asyncio_default_fixture_loop_scope = "session"
146+
ini_options.testpaths = [
149147
"tests",
150148
]
151-
verbosity_assertions = 2
152-
filterwarnings = [
149+
ini_options.verbosity_assertions = 2
150+
ini_options.filterwarnings = [
153151
"error",
154152
"ignore:unclosed database in <sqlite3.Connection object at:ResourceWarning",
155153
"ignore:unclosed file <_io.TextIOWrapper:ResourceWarning",
156154
]
157155

158156
[tool.coverage]
159-
html.show_contexts = true
160-
html.skip_covered = false
157+
run.parallel = true
158+
run.plugins = [
159+
"covdefaults",
160+
]
161+
paths.other = [
162+
".",
163+
"*/filelock",
164+
"*\\filelock",
165+
]
161166
paths.source = [
162167
"src",
163168
".tox/*/lib/*/site-packages",
164169
".tox\\*\\Lib\\site-packages",
165170
"**/src",
166171
"**\\src",
167172
]
168-
paths.other = [
169-
".",
170-
"*/filelock",
171-
"*\\filelock",
172-
]
173173
report.fail_under = 76
174-
run.parallel = true
175-
run.plugins = [
176-
"covdefaults",
177-
]
174+
html.show_contexts = true
175+
html.skip_covered = false
178176

179-
[tool.mypy]
180-
python_version = "3.11"
181-
show_error_codes = true
182-
strict = true
183-
overrides = [
184-
{ module = [
185-
"appdirs.*",
186-
"jnius.*",
187-
], ignore_missing_imports = true },
188-
]
177+
[tool.ty]
178+
environment.python-version = "3.14"
179+
src.exclude = [ "docs/" ]

‎src/filelock/_api.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ class ThreadLocalFileContext(FileLockContext, local):
7979

8080

8181
class FileLockMeta(ABCMeta):
82+
_instances: WeakValueDictionary[str, BaseFileLock]
83+
8284
def __call__( # noqa: PLR0913
8385
cls,
8486
lock_file: str | os.PathLike[str],
@@ -91,7 +93,7 @@ def __call__( # noqa: PLR0913
9193
**kwargs: Any, # capture remaining kwargs for subclasses # noqa: ANN401
9294
) -> BaseFileLock:
9395
if is_singleton:
94-
instance = cls._instances.get(str(lock_file)) # type: ignore[attr-defined]
96+
instance = cls._instances.get(str(lock_file))
9597
if instance:
9698
params_to_check = {
9799
"thread_local": (thread_local, instance.is_thread_local()),
@@ -128,13 +130,13 @@ def __call__( # noqa: PLR0913
128130
**kwargs,
129131
}
130132

131-
present_params = inspect.signature(cls.__init__).parameters # type: ignore[misc]
133+
present_params = inspect.signature(cls.__init__).parameters
132134
init_params = {key: value for key, value in all_params.items() if key in present_params}
133135

134136
instance = super().__call__(lock_file, **init_params)
135137

136138
if is_singleton:
137-
cls._instances[str(lock_file)] = instance # type: ignore[attr-defined]
139+
cls._instances[str(lock_file)] = instance
138140

139141
return cast("BaseFileLock", instance)
140142

‎src/filelock/asyncio.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async def __aexit__( # noqa: D105
7070

7171

7272
class AsyncFileLockMeta(FileLockMeta):
73-
def __call__( # type: ignore[override] # noqa: PLR0913
73+
def __call__( # ty: ignore[invalid-method-override] # noqa: PLR0913
7474
cls, # noqa: N805
7575
lock_file: str | os.PathLike[str],
7676
timeout: float = -1,
@@ -179,7 +179,7 @@ def loop(self) -> asyncio.AbstractEventLoop | None:
179179
"""::return: the event loop."""
180180
return self._context.loop
181181

182-
async def acquire( # type: ignore[override]
182+
async def acquire( # ty: ignore[invalid-method-override]
183183
self,
184184
timeout: float | None = None,
185185
poll_interval: float = 0.05,
@@ -247,7 +247,7 @@ async def acquire( # type: ignore[override]
247247
raise
248248
return AsyncAcquireReturnProxy(lock=self)
249249

250-
async def release(self, force: bool = False) -> None: # type: ignore[override] # noqa: FBT001, FBT002
250+
async def release(self, force: bool = False) -> None: # ty: ignore[invalid-method-override] # noqa: FBT001, FBT002
251251
"""
252252
Releases the file lock. Please note, that the lock is only completely released, if the lock counter is 0.
253253
Also note, that the lock file itself is not automatically deleted.

‎tests/test_async_filelock.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,12 @@ async def test_coroutine_function(tmp_path: Path) -> None:
162162
acquired = released = False
163163

164164
class AioFileLock(BaseAsyncFileLock):
165-
async def _acquire(self) -> None: # type: ignore[override]
165+
async def _acquire(self) -> None: # ty: ignore[invalid-method-override]
166166
nonlocal acquired
167167
acquired = True
168168
self._context.lock_file_fd = 1
169169

170-
async def _release(self) -> None: # type: ignore[override]
170+
async def _release(self) -> None: # ty: ignore[invalid-method-override]
171171
nonlocal released
172172
released = True
173173
self._context.lock_file_fd = None

‎tests/test_filelock.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -779,10 +779,10 @@ def test_singleton_locks_are_deleted_when_no_external_references_exist(
779779
@pytest.mark.skipif(hasattr(sys, "pypy_version_info"), reason="del() does not trigger GC in PyPy")
780780
@pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock])
781781
def test_singleton_instance_tracking_is_unique_per_subclass(lock_type: type[BaseFileLock]) -> None:
782-
class Lock1(lock_type): # type: ignore[valid-type, misc]
782+
class Lock1(lock_type): # ty: ignore[unsupported-base]
783783
pass
784784

785-
class Lock2(lock_type): # type: ignore[valid-type, misc]
785+
class Lock2(lock_type): # ty: ignore[unsupported-base]
786786
pass
787787

788788
assert isinstance(Lock1._instances, WeakValueDictionary) # noqa: SLF001

‎tests/test_virtualenv.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from typing import TYPE_CHECKING
44

5-
from virtualenv import cli_run # type: ignore[import-untyped]
5+
from virtualenv import cli_run
66

77
if TYPE_CHECKING:
88
from pathlib import Path

‎tox.toml‎

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
requires = [ "tox>=4.30.2" ]
1+
requires = [ "tox>=4.34.1" ]
22
env_list = [ "fix", "3.14t", "3.14", "3.13", "3.12", "3.11", "3.10", "coverage", "type", "docs", "pkg_meta" ]
33
skip_missing_interpreters = true
44

@@ -33,7 +33,6 @@ commands = [
3333
"--cov-report",
3434
"xml:{env_tmp_dir}{/}coverage.xml",
3535
] },
36-
3736
"tests",
3837
],
3938
{ replace = "posargs", extend = true, default = [
@@ -46,6 +45,20 @@ commands = [
4645
] },
4746
]
4847

48+
[env.fix]
49+
description = "format the code base to adhere to our styles, and complain about what we cannot do automatically"
50+
skip_install = true
51+
dependency_groups = [ "fix" ]
52+
pass_env = [
53+
{ replace = "ref", of = [
54+
"env_run_base",
55+
"pass_env",
56+
], extend = true },
57+
"PROGRAMDATA",
58+
"DISABLE_PRE_COMMIT_UV_PATCH",
59+
]
60+
commands = [ [ "pre-commit", "run", "--all-files", "--show-diff-on-failure", { replace = "posargs", extend = true } ] ]
61+
4962
[env.coverage]
5063
description = "combine coverage files and generate diff (against DIFF_AGAINST defaulting to origin/main)"
5164
skip_install = true
@@ -73,24 +86,10 @@ commands = [
7386
parallel_show_output = true
7487
depends = [ "3.14t", "3.14", "3.13", "3.12", "3.11", "3.10" ]
7588

76-
[env.fix]
77-
description = "format the code base to adhere to our styles, and complain about what we cannot do automatically"
78-
skip_install = true
79-
dependency_groups = [ "fix" ]
80-
pass_env = [
81-
{ replace = "ref", of = [
82-
"env_run_base",
83-
"pass_env",
84-
], extend = true },
85-
"PROGRAMDATA",
86-
"DISABLE_PRE_COMMIT_UV_PATCH",
87-
]
88-
commands = [ [ "pre-commit", "run", "--all-files", "--show-diff-on-failure", { replace = "posargs", extend = true } ] ]
89-
9089
[env.type]
9190
description = "run type check on code base"
9291
dependency_groups = [ "type" ]
93-
commands = [ [ "mypy", "src{/}filelock" ], [ "mypy", "tests" ] ]
92+
commands = [ [ "ty", "check", "--output-format", "concise", "--error-on-warning", "." ] ]
9493

9594
[env.docs]
9695
description = "build documentation"
@@ -105,7 +104,7 @@ commands = [
105104
"--color",
106105
"-b",
107106
"html",
108-
{ replace = "posargs", default = [ ], extend = true },
107+
{ replace = "posargs", default = [], extend = true },
109108
"-W",
110109
],
111110
[

0 commit comments

Comments
 (0)