Skip to content

Commit 397d419

Browse files
Use adaptive logic to decide on correct alias for pre-commit system/u… (#1241)
* Use adaptive logic to decide on correct alias for pre-commit system/unsupported language hooks * Migrate tests to use new pre-commit file manager framework
1 parent 7fa0576 commit 397d419

8 files changed

Lines changed: 253 additions & 19 deletions

File tree

src/usethis/_integrations/pre_commit/hooks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
from usethis._integrations.pre_commit.init import (
88
ensure_pre_commit_config_exists,
99
)
10+
from usethis._integrations.pre_commit.language import get_system_language
1011
from usethis._integrations.pre_commit.schema import (
1112
HookDefinition,
12-
Language,
1313
LocalRepo,
1414
MetaRepo,
1515
)
@@ -187,7 +187,7 @@ def _get_placeholder_repo_config() -> LocalRepo:
187187
id=_PLACEHOLDER_ID,
188188
name="Placeholder - add your own hooks!",
189189
entry="""uv run --isolated --frozen --offline python -c "print('hello world!')\"""",
190-
language=Language("system"),
190+
language=get_system_language(),
191191
)
192192
],
193193
)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from packaging.version import Version
2+
3+
from usethis._integrations.pre_commit.schema import Language
4+
from usethis._integrations.pre_commit.version import get_minimum_pre_commit_version
5+
6+
7+
def get_system_language() -> Language:
8+
"""Get the appropriate 'system' language keyword based on pre-commit version.
9+
10+
Returns 'unsupported' for pre-commit >= 4.4.0, otherwise 'system'.
11+
12+
In the future, there may be a deprecation of the 'system' language in pre-commit,
13+
at which point falling back to 'system' may no longer be appropriate and this logic
14+
will need to be revisited.
15+
"""
16+
min_version = get_minimum_pre_commit_version()
17+
if min_version is not None and Version(min_version) >= Version("4.4.0"):
18+
return Language("unsupported")
19+
return Language("system")

src/usethis/_tool/impl/deptry.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from usethis._integrations.backend.dispatch import get_backend
1010
from usethis._integrations.backend.uv.used import is_uv_used
1111
from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager
12-
from usethis._integrations.pre_commit.schema import HookDefinition, Language, LocalRepo
12+
from usethis._integrations.pre_commit.language import get_system_language
13+
from usethis._integrations.pre_commit.schema import HookDefinition, LocalRepo
1314
from usethis._integrations.project.layout import get_source_dir_str
1415
from usethis._tool.base import Tool
1516
from usethis._tool.config import (
@@ -102,7 +103,7 @@ def get_pre_commit_config(self) -> PreCommitConfig:
102103
id="deptry",
103104
name="deptry",
104105
entry=f"uv run --frozen --offline deptry {_dir}",
105-
language=Language("system"),
106+
language=get_system_language(),
106107
always_run=True,
107108
pass_filenames=False,
108109
)
@@ -120,7 +121,7 @@ def get_pre_commit_config(self) -> PreCommitConfig:
120121
id="deptry",
121122
name="deptry",
122123
entry=f"deptry {_dir}",
123-
language=Language("system"),
124+
language=get_system_language(),
124125
always_run=True,
125126
pass_filenames=False,
126127
)

src/usethis/_tool/impl/import_linter.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
from usethis._integrations.file.ini.io_ import INIFileManager
1616
from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager
1717
from usethis._integrations.file.setup_cfg.io_ import SetupCFGManager
18-
from usethis._integrations.pre_commit.schema import HookDefinition, Language, LocalRepo
18+
from usethis._integrations.pre_commit.language import get_system_language
19+
from usethis._integrations.pre_commit.schema import HookDefinition, LocalRepo
1920
from usethis._integrations.project.errors import ImportGraphBuildFailedError
2021
from usethis._integrations.project.imports import (
2122
LayeredArchitecture,
@@ -342,7 +343,7 @@ def get_pre_commit_config(self) -> PreCommitConfig:
342343
name="import-linter",
343344
pass_filenames=False,
344345
entry="uv run --frozen --offline lint-imports",
345-
language=Language("system"),
346+
language=get_system_language(),
346347
require_serial=True,
347348
always_run=True,
348349
)
@@ -361,7 +362,7 @@ def get_pre_commit_config(self) -> PreCommitConfig:
361362
name="import-linter",
362363
pass_filenames=False,
363364
entry="lint-imports",
364-
language=Language("system"),
365+
language=get_system_language(),
365366
)
366367
],
367368
),

src/usethis/_tool/impl/requirements_txt.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
from usethis._config import usethis_config
99
from usethis._console import how_print
1010
from usethis._integrations.backend.dispatch import get_backend
11-
from usethis._integrations.pre_commit.schema import HookDefinition, Language, LocalRepo
11+
from usethis._integrations.pre_commit.language import get_system_language
12+
from usethis._integrations.pre_commit.schema import HookDefinition, LocalRepo
1213
from usethis._tool.base import Tool
1314
from usethis._tool.pre_commit import PreCommitConfig
1415
from usethis._types.backend import BackendEnum
@@ -73,7 +74,7 @@ def get_pre_commit_config(self) -> PreCommitConfig:
7374
files="^uv\\.lock$",
7475
pass_filenames=False,
7576
entry="uv export --frozen --offline --quiet -o=requirements.txt",
76-
language=Language("system"),
77+
language=get_system_language(),
7778
require_serial=True,
7879
)
7980
],
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
from pathlib import Path
2+
3+
from usethis._config_file import files_manager
4+
from usethis._integrations.pre_commit.language import get_system_language
5+
from usethis._integrations.pre_commit.schema import Language
6+
from usethis._test import change_cwd
7+
8+
9+
class TestGetSystemLanguage:
10+
def test_returns_unsupported_when_minimum_version_is_4_4_0(self, tmp_path: Path):
11+
# Arrange
12+
(tmp_path / ".pre-commit-config.yaml").write_text("""\
13+
minimum_pre_commit_version: '4.4.0'
14+
repos:
15+
- repo: https://github.com/abravalheri/validate-pyproject
16+
rev: v0.23
17+
hooks:
18+
- id: validate-pyproject
19+
""")
20+
21+
# Act
22+
with change_cwd(tmp_path), files_manager():
23+
result = get_system_language()
24+
25+
# Assert
26+
assert result == Language("unsupported")
27+
28+
def test_returns_unsupported_when_minimum_version_is_above_4_4_0(
29+
self, tmp_path: Path
30+
):
31+
# Arrange
32+
(tmp_path / ".pre-commit-config.yaml").write_text("""\
33+
minimum_pre_commit_version: '4.5.0'
34+
repos:
35+
- repo: https://github.com/abravalheri/validate-pyproject
36+
rev: v0.23
37+
hooks:
38+
- id: validate-pyproject
39+
""")
40+
41+
# Act
42+
with change_cwd(tmp_path), files_manager():
43+
result = get_system_language()
44+
45+
# Assert
46+
assert result == Language("unsupported")
47+
48+
def test_returns_system_when_minimum_version_is_below_4_4_0(self, tmp_path: Path):
49+
# Arrange
50+
(tmp_path / ".pre-commit-config.yaml").write_text("""\
51+
minimum_pre_commit_version: '4.3.0'
52+
repos:
53+
- repo: https://github.com/abravalheri/validate-pyproject
54+
rev: v0.23
55+
hooks:
56+
- id: validate-pyproject
57+
""")
58+
59+
# Act
60+
with change_cwd(tmp_path), files_manager():
61+
result = get_system_language()
62+
63+
# Assert
64+
assert result == Language("system")
65+
66+
def test_returns_system_when_config_doesnt_exist(self, tmp_path: Path):
67+
# Act
68+
with change_cwd(tmp_path), files_manager():
69+
result = get_system_language()
70+
71+
# Assert
72+
assert result == Language("system")
73+
74+
def test_returns_system_when_minimum_version_not_declared(self, tmp_path: Path):
75+
# Arrange
76+
(tmp_path / ".pre-commit-config.yaml").write_text("""\
77+
repos:
78+
- repo: https://github.com/abravalheri/validate-pyproject
79+
rev: v0.23
80+
hooks:
81+
- id: validate-pyproject
82+
""")
83+
84+
# Act
85+
with change_cwd(tmp_path), files_manager():
86+
result = get_system_language()
87+
88+
# Assert
89+
assert result == Language("system")
90+
91+
def test_returns_system_when_minimum_version_is_3_x(self, tmp_path: Path):
92+
# Arrange
93+
(tmp_path / ".pre-commit-config.yaml").write_text("""\
94+
minimum_pre_commit_version: '3.7.1'
95+
repos:
96+
- repo: https://github.com/abravalheri/validate-pyproject
97+
rev: v0.23
98+
hooks:
99+
- id: validate-pyproject
100+
""")
101+
102+
# Act
103+
with change_cwd(tmp_path), files_manager():
104+
result = get_system_language()
105+
106+
# Assert
107+
assert result == Language("system")

tests/usethis/_integrations/pre_commit/test_yaml.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,20 @@ def test_extra_config(self, tmp_path: Path):
136136

137137

138138
class TestPreCommitFancyDump:
139-
def test_placeholder(self):
140-
_pre_commit_fancy_dump(
141-
config=JsonSchemaForPreCommitConfigYaml(
142-
repos=[
143-
_get_placeholder_repo_config(),
144-
]
145-
),
146-
reference={},
147-
)
139+
def test_placeholder(self, tmp_path: Path):
140+
# Arrange - create a minimal pre-commit config for get_system_language()
141+
(tmp_path / ".pre-commit-config.yaml").write_text("repos: []\n")
142+
143+
# Act
144+
with change_cwd(tmp_path), files_manager():
145+
_pre_commit_fancy_dump(
146+
config=JsonSchemaForPreCommitConfigYaml(
147+
repos=[
148+
_get_placeholder_repo_config(),
149+
]
150+
),
151+
reference={},
152+
)
148153

149154
def test_invalid(self):
150155
with pytest.raises(TypeError):

tests/usethis/_tool/impl/test_deptry.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from usethis._config_file import files_manager
66
from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager
7+
from usethis._integrations.pre_commit.schema import Language
78
from usethis._test import change_cwd
89
from usethis._tool.config import ConfigEntry, ConfigItem
910
from usethis._tool.impl.deptry import DeptryTool
@@ -268,3 +269,102 @@ def test_pyproject_toml_exists(self, tmp_path: Path):
268269

269270
# Assert
270271
assert (tmp_path / "pyproject.toml").exists()
272+
273+
class TestGetPreCommitConfig:
274+
def test_uses_system_language_when_no_minimum_version(self, tmp_path: Path):
275+
# Arrange
276+
(tmp_path / "pyproject.toml").write_text("""\
277+
[project]
278+
name = "test-project"
279+
""")
280+
(tmp_path / "src").mkdir()
281+
(tmp_path / "src" / "test_project").mkdir()
282+
(tmp_path / "src" / "test_project" / "__init__.py").touch()
283+
284+
# Act
285+
with change_cwd(tmp_path), files_manager():
286+
result = DeptryTool().get_pre_commit_config()
287+
288+
# Assert
289+
assert result.repo_configs is not None
290+
assert result.repo_configs[0].repo.hooks is not None
291+
assert result.repo_configs[0].repo.hooks[0].language == Language("system")
292+
293+
def test_uses_system_language_when_minimum_version_below_4_4_0(
294+
self, tmp_path: Path
295+
):
296+
# Arrange
297+
(tmp_path / "pyproject.toml").write_text("""\
298+
[project]
299+
name = "test-project"
300+
""")
301+
(tmp_path / ".pre-commit-config.yaml").write_text("""\
302+
minimum_pre_commit_version: '4.3.0'
303+
repos: []
304+
""")
305+
(tmp_path / "src").mkdir()
306+
(tmp_path / "src" / "test_project").mkdir()
307+
(tmp_path / "src" / "test_project" / "__init__.py").touch()
308+
309+
# Act
310+
with change_cwd(tmp_path), files_manager():
311+
result = DeptryTool().get_pre_commit_config()
312+
313+
# Assert
314+
assert result.repo_configs is not None
315+
assert result.repo_configs[0].repo.hooks is not None
316+
assert result.repo_configs[0].repo.hooks[0].language == Language("system")
317+
318+
def test_uses_unsupported_language_when_minimum_version_is_4_4_0(
319+
self, tmp_path: Path
320+
):
321+
# Arrange
322+
(tmp_path / "pyproject.toml").write_text("""\
323+
[project]
324+
name = "test-project"
325+
""")
326+
(tmp_path / ".pre-commit-config.yaml").write_text("""\
327+
minimum_pre_commit_version: '4.4.0'
328+
repos: []
329+
""")
330+
(tmp_path / "src").mkdir()
331+
(tmp_path / "src" / "test_project").mkdir()
332+
(tmp_path / "src" / "test_project" / "__init__.py").touch()
333+
334+
# Act
335+
with change_cwd(tmp_path), files_manager():
336+
result = DeptryTool().get_pre_commit_config()
337+
338+
# Assert
339+
assert result.repo_configs is not None
340+
assert result.repo_configs[0].repo.hooks is not None
341+
assert result.repo_configs[0].repo.hooks[0].language == Language(
342+
"unsupported"
343+
)
344+
345+
def test_uses_unsupported_language_when_minimum_version_above_4_4_0(
346+
self, tmp_path: Path
347+
):
348+
# Arrange
349+
(tmp_path / "pyproject.toml").write_text("""\
350+
[project]
351+
name = "test-project"
352+
""")
353+
(tmp_path / ".pre-commit-config.yaml").write_text("""\
354+
minimum_pre_commit_version: '4.5.0'
355+
repos: []
356+
""")
357+
(tmp_path / "src").mkdir()
358+
(tmp_path / "src" / "test_project").mkdir()
359+
(tmp_path / "src" / "test_project" / "__init__.py").touch()
360+
361+
# Act
362+
with change_cwd(tmp_path), files_manager():
363+
result = DeptryTool().get_pre_commit_config()
364+
365+
# Assert
366+
assert result.repo_configs is not None
367+
assert result.repo_configs[0].repo.hooks is not None
368+
assert result.repo_configs[0].repo.hooks[0].language == Language(
369+
"unsupported"
370+
)

0 commit comments

Comments
 (0)