Skip to content

Commit 3f8e1f8

Browse files
Add test that pre-commit hooks actually runs successfully when adding… (#800)
* Add test that pre-commit hooks actually runs successfully when adding all tools at once (at least, among the ones that have pre-commits) * Add docstring and comment for `use_tool` * Clarify comment regarding intention of test * Add clarifying comment regarding the structure of the `use_tool` function * Add test for `use_tool`
1 parent f6e33be commit 3f8e1f8

3 files changed

Lines changed: 90 additions & 4 deletions

File tree

src/usethis/_core/tool.py

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

55
from typing import TYPE_CHECKING, Protocol
66

7+
from typing_extensions import assert_never
8+
79
from usethis._config import usethis_config
810
from usethis._console import box_print, tick_print
911
from usethis._integrations.ci.bitbucket.used import is_bitbucket_used
@@ -35,6 +37,7 @@
3537
from usethis._tool.rule import RuleConfig
3638

3739
if TYPE_CHECKING:
40+
from usethis._tool.all_ import SupportedToolType
3841
from usethis._tool.base import Tool
3942

4043
# Note - all these functions invoke ensure_pyproject_toml() at the start, since
@@ -467,3 +470,50 @@ def _get_basic_rule_config() -> RuleConfig:
467470
)
468471

469472
return rule_config
473+
474+
475+
def use_tool(
476+
tool: SupportedToolType,
477+
*,
478+
remove: bool = False,
479+
how: bool = False,
480+
) -> None:
481+
"""General dispatch function to add or remove a tool to/from the project.
482+
483+
This is mostly intended for situations when the exact tool being added is not known
484+
dynamically. If you know the specific tool you wish to add, it is strongly
485+
recommended to call the specific function directly, e.g. `use_codespell()`, etc.
486+
"""
487+
# One might wonder why we don't just implement a `use` method on the Tool class
488+
# itself. Basically it's for architectural reasons: we want to keep a layer of
489+
# abstraction between the tool and the logic to actually configure it.
490+
# In the future, that might change if we can create a sufficiently generalized logic
491+
# for all tools such that bespoke choices on a per-tool basis are not required, and
492+
# all the logic is just deterministic based on the tool's properties/methods, etc.
493+
if isinstance(tool, CodespellTool):
494+
use_codespell(remove=remove, how=how)
495+
elif isinstance(tool, CoveragePyTool):
496+
use_coverage_py(remove=remove, how=how)
497+
elif isinstance(tool, DeptryTool):
498+
use_deptry(remove=remove, how=how)
499+
elif isinstance(tool, ImportLinterTool):
500+
use_import_linter(remove=remove, how=how)
501+
elif isinstance(tool, PreCommitTool):
502+
use_pre_commit(remove=remove, how=how)
503+
elif isinstance(tool, PyprojectFmtTool):
504+
use_pyproject_fmt(remove=remove, how=how)
505+
elif isinstance(tool, PyprojectTOMLTool):
506+
use_pyproject_toml(remove=remove, how=how)
507+
elif isinstance(tool, PytestTool):
508+
use_pytest(remove=remove, how=how)
509+
elif isinstance(tool, RequirementsTxtTool):
510+
use_requirements_txt(remove=remove, how=how)
511+
elif isinstance(tool, RuffTool):
512+
use_ruff(remove=remove, how=how)
513+
else:
514+
# Having the assert_never here is effectively a way of testing cases are
515+
# exhaustively handled, which ensures it is kept up to date with ALL_TOOLS,
516+
# together with the type annotation on ALL_TOOLS itself. That's why this
517+
# function is implemented as a series of `if` statements rather than a
518+
# dictionary or similar alternative.
519+
assert_never(tool)

src/usethis/_tool/all_.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from typing import TYPE_CHECKING
3+
from typing import TypeAlias
44

55
from usethis._tool.impl.codespell import CodespellTool
66
from usethis._tool.impl.coverage_py import CoveragePyTool
@@ -13,10 +13,20 @@
1313
from usethis._tool.impl.requirements_txt import RequirementsTxtTool
1414
from usethis._tool.impl.ruff import RuffTool
1515

16-
if TYPE_CHECKING:
17-
from usethis._tool.base import Tool
16+
SupportedToolType: TypeAlias = (
17+
CodespellTool
18+
| CoveragePyTool
19+
| DeptryTool
20+
| ImportLinterTool
21+
| PreCommitTool
22+
| PyprojectFmtTool
23+
| PyprojectTOMLTool
24+
| PytestTool
25+
| RequirementsTxtTool
26+
| RuffTool
27+
)
1828

19-
ALL_TOOLS: list[Tool] = [
29+
ALL_TOOLS: list[SupportedToolType] = [
2030
CodespellTool(),
2131
CoveragePyTool(),
2232
DeptryTool(),

tests/usethis/_core/test_core_tool.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use_pytest,
2121
use_requirements_txt,
2222
use_ruff,
23+
use_tool,
2324
)
2425
from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager
2526
from usethis._integrations.pre_commit.hooks import (
@@ -1867,6 +1868,23 @@ def test_add_unsubsumed_tools(self, uv_init_repo_dir: Path):
18671868
assert "deptry" in contents
18681869
assert "ruff" in contents
18691870

1871+
class TestMultipleIntegrations:
1872+
def test_hooks_run_all_tools_empty_repo(self, uv_env_dir: Path):
1873+
# Arrange
1874+
with change_cwd(uv_env_dir), files_manager():
1875+
# Add the tools
1876+
use_pre_commit()
1877+
for tool in ALL_TOOLS:
1878+
if tool.get_pre_commit_repos():
1879+
use_tool(tool)
1880+
1881+
# Act, Assert
1882+
# Run the pre-commit hooks via subprocess - check it doesn't raise
1883+
call_uv_subprocess(
1884+
["run", "pre-commit", "run", "--all-files"],
1885+
change_toml=False,
1886+
)
1887+
18701888

18711889
class TestPyprojectFmt:
18721890
class TestAdd:
@@ -3450,3 +3468,11 @@ def test_removed_from_all_files(self, uv_init_dir: Path):
34503468
"[tool.ruff.lint]" not in (uv_init_dir / "pyproject.toml").read_text()
34513469
)
34523470
assert not (uv_init_dir / "ruff.toml").exists()
3471+
3472+
3473+
class TestUseTool:
3474+
def test_add_all_tool(self, uv_init_dir: Path):
3475+
# Act
3476+
with change_cwd(uv_init_dir), files_manager():
3477+
for tool in ALL_TOOLS:
3478+
use_tool(tool)

0 commit comments

Comments
 (0)