Skip to content

Commit bd54adf

Browse files
usethis tool ruff with pytest configures PT rules to be disabled for non-test dirs (#1252)
1 parent 3a06c95 commit bd54adf

4 files changed

Lines changed: 41 additions & 3 deletions

File tree

src/usethis/_tool/impl/pytest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def get_managed_files(self) -> list[Path]:
162162
return [Path(".pytest.ini"), Path("pytest.ini"), Path("tests/conftest.py")]
163163

164164
def get_rule_config(self) -> RuleConfig:
165-
return RuleConfig(selected=["PT"])
165+
return RuleConfig(selected=["PT"], nontests_unmanaged_ignored=["PT"])
166166

167167
def get_active_config_file_managers(self) -> set[KeyValueFileManager]:
168168
# This is a variant of the "first" method

src/usethis/_tool/impl/ruff.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,11 +482,14 @@ def apply_rule_config(self, rule_config: RuleConfig) -> None:
482482
alert_only=(is_selected or is_ignored) or usethis_config.alert_only,
483483
instruct_only=(is_selected or is_ignored) or usethis_config.instruct_only,
484484
):
485-
# Only add test directory ignore rules if the tests directory exists
485+
# Only add test-related directory ignore rules if the tests directory exists
486486
if (usethis_config.cpd() / "tests").exists():
487487
self.ignore_rules_in_glob(
488488
rule_config.tests_unmanaged_ignored, glob="tests/**"
489489
)
490+
self.ignore_rules_in_glob(
491+
rule_config.nontests_unmanaged_ignored, glob="!tests/**/*.py"
492+
)
490493

491494
def remove_rule_config(self, rule_config: RuleConfig) -> None:
492495
"""Remove the Ruff rules associated with a rule config from the project.

src/usethis/_tool/rule.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,16 @@ class RuleConfig(BaseModel):
3030
unmanaged_ignored: Unmanaged ignored rules.
3131
tests_unmanaged_ignored: Unmanaged cases of rules ignored for specifically the
3232
tests directory.
33+
nontests_unmanaged_ignored: Unmanaged cases of rules ignored for specifically
34+
non-test directories (using !tests/**/*.py glob).
3335
"""
3436

3537
selected: list[Rule] = Field(default_factory=list)
3638
ignored: list[Rule] = Field(default_factory=list)
3739
unmanaged_selected: list[Rule] = Field(default_factory=list)
3840
unmanaged_ignored: list[Rule] = Field(default_factory=list)
3941
tests_unmanaged_ignored: list[Rule] = Field(default_factory=list)
42+
nontests_unmanaged_ignored: list[Rule] = Field(default_factory=list)
4043

4144
def get_all_selected(self) -> list[Rule]:
4245
"""Get all (project-scope) selected rules."""
@@ -68,12 +71,13 @@ def empty(self) -> bool:
6871
and not self.unmanaged_selected
6972
and not self.unmanaged_ignored
7073
and not self.tests_unmanaged_ignored
74+
and not self.nontests_unmanaged_ignored
7175
)
7276

7377
@property
7478
def is_related_to_tests(self) -> bool:
7579
"""Check if the rule config has any tests-related rules."""
76-
return bool(self.tests_unmanaged_ignored)
80+
return bool(self.tests_unmanaged_ignored or self.nontests_unmanaged_ignored)
7781

7882
def __repr__(self) -> str:
7983
"""Representation which omits empty-list fields."""
@@ -88,6 +92,8 @@ def __repr__(self) -> str:
8892
args.append(f"unmanaged_ignored={self.unmanaged_ignored}")
8993
if self.tests_unmanaged_ignored:
9094
args.append(f"tests_unmanaged_ignored={self.tests_unmanaged_ignored}")
95+
if self.nontests_unmanaged_ignored:
96+
args.append(f"nontests_unmanaged_ignored={self.nontests_unmanaged_ignored}")
9197
arg_str = ", ".join(args)
9298
return f"RuleConfig({arg_str})"
9399

@@ -115,4 +121,7 @@ def __or__(self, other: Self) -> Self:
115121
new.tests_unmanaged_ignored = (
116122
self.tests_unmanaged_ignored + other.tests_unmanaged_ignored
117123
)
124+
new.nontests_unmanaged_ignored = (
125+
self.nontests_unmanaged_ignored + other.nontests_unmanaged_ignored
126+
)
118127
return new

tests/usethis/_core/test_core_tool.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2741,6 +2741,32 @@ def test_ruff_import_linter_integration_adds_test_dir_ignores(
27412741
# Verify that INP rules are ignored in tests/** after pytest creates tests/
27422742
assert "INP" in RuffTool().get_ignored_rules_in_glob("tests/**")
27432743

2744+
@pytest.mark.usefixtures("_vary_network_conn")
2745+
def test_pytest_then_ruff_ignores_pt_in_nontests(self, uv_init_dir: Path):
2746+
with change_cwd(uv_init_dir), files_manager():
2747+
# Arrange
2748+
use_pytest()
2749+
2750+
# Act
2751+
use_ruff()
2752+
2753+
# Assert
2754+
# Verify that PT rules are ignored in non-test files (!tests/**/*.py)
2755+
assert "PT" in RuffTool().get_ignored_rules_in_glob("!tests/**/*.py")
2756+
2757+
@pytest.mark.usefixtures("_vary_network_conn")
2758+
def test_ruff_then_pytest_ignores_pt_in_nontests(self, uv_init_dir: Path):
2759+
with change_cwd(uv_init_dir), files_manager():
2760+
# Arrange
2761+
use_ruff()
2762+
2763+
# Act
2764+
use_pytest()
2765+
2766+
# Assert
2767+
# Verify that PT rules are ignored in non-test files (!tests/**/*.py)
2768+
assert "PT" in RuffTool().get_ignored_rules_in_glob("!tests/**/*.py")
2769+
27442770
@pytest.mark.usefixtures("_vary_network_conn")
27452771
def test_pytest_ini_priority(self, uv_init_dir: Path):
27462772
# Arrange

0 commit comments

Comments
 (0)