Skip to content

Commit de67ee2

Browse files
Only use pyproject.toml for config when it already exists (#934)
* Only use `pyproject.toml` for config when it already exists * update token in README test * Remove empty test module * Split test to reflect new codespell config file behaviour * Consistently name tests `*_no_pyproject_toml` rather than `*_no_pyproject` * Split docstyle no backend test into two based on `pyproject.toml` presence * Only add `pyproject-fmt` if `pyproject.toml` is actually used in the project * Fix docstyle `test_none_backend_no_pyproject_toml` test * Split the `test_non_backend` test for `usethis lint` into two based on `pyproject.toml` presence * Split the `test_non_backend` test for `usethis rule` into two based on `pyproject.toml` presence * Split the `test_non_backend` test for `usethis spellcheck` into two based on `pyproject.toml` presence * Split the `test_non_backend` test for `usethis test` into two based on `pyproject.toml` presence * Split the `test_non_backend` test for `usethis tool coverage.py` into two based on `pyproject.toml` presence * Don't create empty config files (for Deptry etc.) unnecessarily * Pass `test_empty_dir` test for Ruff
1 parent 9372870 commit de67ee2

24 files changed

Lines changed: 394 additions & 36 deletions

src/usethis/_tool/base.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@
3030
if TYPE_CHECKING:
3131
from pathlib import Path
3232

33-
from usethis._integrations.backend.uv.deps import (
34-
Dependency,
35-
)
33+
from usethis._integrations.backend.uv.deps import Dependency
3634
from usethis._integrations.ci.bitbucket.schema import Step as BitbucketStep
3735
from usethis._integrations.pre_commit.schema import LocalRepo, UriRepo
3836
from usethis._io import KeyValueFileManager
@@ -409,7 +407,11 @@ def add_configs(self) -> None:
409407
def _add_config_item(
410408
self, config_item: ConfigItem, *, file_managers: set[KeyValueFileManager]
411409
) -> bool:
412-
"""Add a specific configuration item using specified file managers."""
410+
"""Add a specific configuration item using specified file managers.
411+
412+
Returns whether any config was added. Config might not be added in some cases
413+
where it's conditional and not applicable based on the current project state.
414+
"""
413415
# This is mostly a helper method for `add_configs`.
414416

415417
# Filter to just those active config file managers which can manage this
@@ -425,15 +427,10 @@ def _add_config_item(
425427
msg = f"No active config file managers found for one of the '{self.name}' config items."
426428
raise NotImplementedError(msg)
427429
else:
428-
# Early exist; this config item is not managed by any active files
430+
# Early exit; this config item is not managed by any active files
429431
# so it's optional, effectively.
430432
return False
431433

432-
for file_manager in used_file_managers:
433-
if not (file_manager.path.exists() and file_manager.path.is_file()):
434-
tick_print(f"Writing '{file_manager.relative_path}'.")
435-
file_manager.path.touch(exist_ok=True)
436-
437434
config_entries = [
438435
config_item
439436
for relative_path, config_item in config_item.root.items()
@@ -456,6 +453,13 @@ def _add_config_item(
456453
# No value to add, so skip this config item.
457454
return False
458455

456+
# N.B. we wait to create files until after all `return False` lines to avoid
457+
# creating empty files unnecessarily.
458+
for file_manager in used_file_managers:
459+
if not (file_manager.path.exists() and file_manager.path.is_file()):
460+
tick_print(f"Writing '{file_manager.relative_path}'.")
461+
file_manager.path.touch(exist_ok=True)
462+
459463
shared_keys = []
460464
for key in entry.keys:
461465
shared_keys.append(key)

src/usethis/_tool/impl/codespell.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from __future__ import annotations
22

33
from pathlib import Path
4+
from typing import TYPE_CHECKING
45

56
from typing_extensions import assert_never
67

8+
from usethis._config import usethis_config
79
from usethis._config_file import CodespellRCManager
810
from usethis._console import box_print
911
from usethis._integrations.backend.dispatch import get_backend
@@ -22,6 +24,9 @@
2224
from usethis._types.backend import BackendEnum
2325
from usethis._types.deps import Dependency
2426

27+
if TYPE_CHECKING:
28+
from usethis._io import KeyValueFileManager
29+
2530

2631
class CodespellTool(Tool):
2732
# https://github.com/codespell-project/codespell
@@ -54,6 +59,11 @@ def print_how_to_use(self) -> None:
5459
def get_dev_deps(self, *, unconditional: bool = False) -> list[Dependency]:
5560
return [Dependency(name="codespell")]
5661

62+
def preferred_file_manager(self) -> KeyValueFileManager:
63+
if (usethis_config.cpd() / "pyproject.toml").exists():
64+
return PyprojectTOMLManager()
65+
return CodespellRCManager()
66+
5767
def get_config_spec(self) -> ConfigSpec:
5868
# https://github.com/codespell-project/codespell?tab=readme-ov-file#using-a-config-file
5969

src/usethis/_tool/impl/coverage_py.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from __future__ import annotations
22

33
from pathlib import Path
4+
from typing import TYPE_CHECKING
45

6+
from usethis._config import usethis_config
57
from usethis._config_file import (
68
CoverageRCManager,
79
ToxINIManager,
@@ -17,6 +19,9 @@
1719
from usethis._types.backend import BackendEnum
1820
from usethis._types.deps import Dependency
1921

22+
if TYPE_CHECKING:
23+
from usethis._io import KeyValueFileManager
24+
2025

2126
class CoveragePyTool(Tool):
2227
# https://github.com/nedbat/coveragepy
@@ -54,6 +59,11 @@ def get_test_deps(self, *, unconditional: bool = False) -> list[Dependency]:
5459
deps += [Dependency(name="pytest-cov")]
5560
return deps
5661

62+
def preferred_file_manager(self) -> KeyValueFileManager:
63+
if (usethis_config.cpd() / "pyproject.toml").exists():
64+
return PyprojectTOMLManager()
65+
return CoverageRCManager()
66+
5767
def get_config_spec(self) -> ConfigSpec:
5868
# https://coverage.readthedocs.io/en/latest/config.html#configuration-reference
5969

src/usethis/_tool/impl/import_linter.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ def get_dev_deps(self, *, unconditional: bool = False) -> list[Dependency]:
9292
# This is because it needs to run from within the virtual environment.
9393
return [Dependency(name="import-linter")]
9494

95+
def preferred_file_manager(self) -> KeyValueFileManager:
96+
if (usethis_config.cpd() / "pyproject.toml").exists():
97+
return PyprojectTOMLManager()
98+
return DotImportLinterManager()
99+
95100
def get_config_spec(self) -> ConfigSpec:
96101
# https://import-linter.readthedocs.io/en/stable/usage.html
97102

src/usethis/_tool/impl/pytest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ def get_test_deps(self, *, unconditional: bool = False) -> list[Dependency]:
6767
deps += [Dependency(name="pytest-cov")]
6868
return deps
6969

70+
def preferred_file_manager(self) -> KeyValueFileManager:
71+
if (usethis_config.cpd() / "pyproject.toml").exists():
72+
return PyprojectTOMLManager()
73+
return PytestINIManager()
74+
7075
def get_config_spec(self) -> ConfigSpec:
7176
# https://docs.pytest.org/en/stable/reference/customize.html#configuration-file-formats
7277
# "Options from multiple configfiles candidates are never merged - the first match wins."

src/usethis/_tool/impl/ruff.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ def print_how_to_use_formatter(self) -> None:
130130
def get_dev_deps(self, *, unconditional: bool = False) -> list[Dependency]:
131131
return [Dependency(name="ruff")]
132132

133+
def preferred_file_manager(self) -> KeyValueFileManager:
134+
if (usethis_config.cpd() / "pyproject.toml").exists():
135+
return PyprojectTOMLManager()
136+
return RuffTOMLManager()
137+
133138
def get_config_spec(self) -> ConfigSpec:
134139
# https://docs.astral.sh/ruff/configuration/#config-file-discovery
135140

src/usethis/_toolset/format_.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from usethis._core.tool import use_pyproject_fmt, use_ruff
2+
from usethis._tool.impl.pyproject_toml import PyprojectTOMLTool
23

34

45
def use_formatters(remove: bool = False, how: bool = False):
56
use_ruff(linter=False, formatter=True, remove=remove, how=how)
6-
use_pyproject_fmt(remove=remove, how=how)
7+
if PyprojectTOMLTool().is_used() and (not remove or how):
8+
use_pyproject_fmt(remove=remove, how=how)

tests/usethis/_core/test_core_tool.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,17 @@ def test_setup_cfg_empty(self, uv_init_dir: Path):
236236
assert (uv_init_dir / "setup.cfg").read_text() == ""
237237
assert "[tool.codespell]" in (uv_init_dir / "pyproject.toml").read_text()
238238

239-
def test_none_backend(self, tmp_path: Path, capfd: pytest.CaptureFixture[str]):
239+
def test_none_backend_pyproject_toml(
240+
self, tmp_path: Path, capfd: pytest.CaptureFixture[str]
241+
):
242+
# Arrange
243+
(tmp_path / "pyproject.toml").touch()
244+
240245
# Act
241246
with (
242247
change_cwd(tmp_path),
243248
usethis_config.set(backend=BackendEnum.none),
244-
PyprojectTOMLManager(),
249+
files_manager(),
245250
):
246251
use_codespell()
247252

@@ -250,11 +255,31 @@ def test_none_backend(self, tmp_path: Path, capfd: pytest.CaptureFixture[str]):
250255
assert not err
251256
assert out == (
252257
"☐ Add the dev dependency 'codespell'.\n"
253-
"✔ Writing 'pyproject.toml'.\n"
254258
"✔ Adding Codespell config to 'pyproject.toml'.\n"
255259
"☐ Run 'codespell' to run the Codespell spellchecker.\n"
256260
)
257261

262+
def test_none_backend_no_pyproject_toml(
263+
self, tmp_path: Path, capfd: pytest.CaptureFixture[str]
264+
):
265+
# Act
266+
with (
267+
change_cwd(tmp_path),
268+
usethis_config.set(backend=BackendEnum.none),
269+
files_manager(),
270+
):
271+
use_codespell()
272+
273+
# Assert
274+
out, err = capfd.readouterr()
275+
assert not err
276+
assert out == (
277+
"☐ Add the dev dependency 'codespell'.\n"
278+
"✔ Writing '.codespellrc'.\n"
279+
"✔ Adding Codespell config to '.codespellrc'.\n"
280+
"☐ Run 'codespell' to run the Codespell spellchecker.\n"
281+
)
282+
258283
class TestRemove:
259284
@pytest.mark.usefixtures("_vary_network_conn")
260285
def test_config_file(
@@ -2318,7 +2343,9 @@ def test_doesnt_invoke_ensure_dep_declaration_file(self, tmp_path: Path):
23182343
class TestPytest:
23192344
class TestAdd:
23202345
@pytest.mark.usefixtures("_vary_network_conn")
2321-
def test_no_pyproject(self, tmp_path: Path, capfd: pytest.CaptureFixture[str]):
2346+
def test_no_pyproject_toml(
2347+
self, tmp_path: Path, capfd: pytest.CaptureFixture[str]
2348+
):
23222349
with change_cwd(tmp_path), files_manager():
23232350
# Act
23242351
use_pytest()

tests/usethis/_integrations/backend/uv/test_python.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def test_upper_bound(self, tmp_path: Path):
5555
# Assert
5656
assert supported_major_python == [9, 10, 11]
5757

58-
def test_no_pyproject(self, tmp_path: Path):
58+
def test_no_pyproject_toml(self, tmp_path: Path):
5959
with change_cwd(tmp_path), PyprojectTOMLManager():
6060
assert get_supported_uv_major_python_versions() == [
6161
extract_major_version(get_python_version())

tests/usethis/_integrations/file/pyproject_toml/test_requires_python.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_lower_bound(self, tmp_path: Path):
2828
# Assert
2929
assert requires_python == SpecifierSet(">=3.7")
3030

31-
def test_no_pyproject(self, tmp_path: Path):
31+
def test_no_pyproject_toml(self, tmp_path: Path):
3232
with (
3333
change_cwd(tmp_path),
3434
PyprojectTOMLManager(),

0 commit comments

Comments
 (0)