diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ec7ad4e3..54facff5 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -252,6 +252,10 @@ Follow the guide in CONTRIBUTING.md: - Avoid suppressions unless absolutely necessary - Format: `# ruff: noqa: RULE1, RULE2` (not `# ruff noqa:`) +## Reasoning Style + +Never use the word "Actually", say "I just realized" or any other meta-language. Instead, clearly state the insight immediately. Similarly, do not use ellipses, e.g. "Unless...". Don't say "Let me check." Be efficient, clear, and direct, not conversational. + ## Trust These Instructions These instructions have been validated by running actual commands and inspecting the full codebase. Only search for additional information if: diff --git a/.importlinter b/.importlinter index a37991ff..e922b60a 100644 --- a/.importlinter +++ b/.importlinter @@ -14,10 +14,13 @@ layers = _core _tool _init + _detect _deps _config_file _integrations - _io | _subprocess | _console + _backend + _file + _io | _subprocess | _console | _python _config _types | errors _pipeweld @@ -65,10 +68,10 @@ containers = usethis._tool.impl layers = pyproject_toml - codespell | deptry | import_linter | pyproject_fmt | requirements_txt + codespell | deptry | import_linter | mkdocs | pyproject_fmt | requirements_txt ruff pytest : coverage_py - pre_commit | mkdocs + pre_commit exhaustive = true [importlinter:contract:integrations] @@ -79,17 +82,15 @@ containers = layers = ci | pre_commit environ - backend | mkdocs | pytest | pydantic | sonarqube - project - file - python + mkdocs | pytest | pydantic | sonarqube + project | readme exhaustive = true -[importlinter:contract:integrations_file] -name = usethis._integrations.file +[importlinter:contract:_file] +name = usethis._file type = layers containers = - usethis._integrations.file + usethis._file layers = pyproject_toml | setup_cfg ini | toml | yaml diff --git a/pyproject.toml b/pyproject.toml index 09164b0e..61eba4b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -198,7 +198,7 @@ environment.python-platform = "all" rules.type-assertion-failure = "ignore" [[tool.ty.overrides]] -include = [ "src/usethis/_integrations/file/**" ] +include = [ "src/usethis/_file/**" ] rules.invalid-argument-type = "ignore" rules.invalid-return-type = "ignore" rules.invalid-assignment = "ignore" diff --git a/src/usethis/_integrations/backend/__init__.py b/src/usethis/_backend/__init__.py similarity index 100% rename from src/usethis/_integrations/backend/__init__.py rename to src/usethis/_backend/__init__.py diff --git a/src/usethis/_integrations/backend/dispatch.py b/src/usethis/_backend/dispatch.py similarity index 85% rename from src/usethis/_integrations/backend/dispatch.py rename to src/usethis/_backend/dispatch.py index 8dd56fe5..4d285f57 100644 --- a/src/usethis/_integrations/backend/dispatch.py +++ b/src/usethis/_backend/dispatch.py @@ -2,11 +2,11 @@ from typing import Literal +from usethis._backend.poetry.detect import is_poetry_used +from usethis._backend.uv.available import is_uv_available +from usethis._backend.uv.detect import is_uv_used from usethis._config import usethis_config from usethis._console import warn_print -from usethis._integrations.backend.poetry.used import is_poetry_used -from usethis._integrations.backend.uv.available import is_uv_available -from usethis._integrations.backend.uv.used import is_uv_used from usethis._types.backend import BackendEnum diff --git a/src/usethis/_integrations/backend/poetry/__init__.py b/src/usethis/_backend/poetry/__init__.py similarity index 100% rename from src/usethis/_integrations/backend/poetry/__init__.py rename to src/usethis/_backend/poetry/__init__.py diff --git a/src/usethis/_integrations/backend/poetry/used.py b/src/usethis/_backend/poetry/detect.py similarity index 83% rename from src/usethis/_integrations/backend/poetry/used.py rename to src/usethis/_backend/poetry/detect.py index c434f322..d95388e9 100644 --- a/src/usethis/_integrations/backend/poetry/used.py +++ b/src/usethis/_backend/poetry/detect.py @@ -1,5 +1,5 @@ from usethis._config import usethis_config -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager def is_poetry_used() -> bool: diff --git a/src/usethis/_integrations/backend/uv/__init__.py b/src/usethis/_backend/uv/__init__.py similarity index 100% rename from src/usethis/_integrations/backend/uv/__init__.py rename to src/usethis/_backend/uv/__init__.py diff --git a/src/usethis/_integrations/backend/uv/available.py b/src/usethis/_backend/uv/available.py similarity index 63% rename from src/usethis/_integrations/backend/uv/available.py rename to src/usethis/_backend/uv/available.py index b537df13..561c7302 100644 --- a/src/usethis/_integrations/backend/uv/available.py +++ b/src/usethis/_backend/uv/available.py @@ -1,5 +1,5 @@ -from usethis._integrations.backend.uv.call import call_uv_subprocess -from usethis._integrations.backend.uv.errors import UVSubprocessFailedError +from usethis._backend.uv.call import call_uv_subprocess +from usethis._backend.uv.errors import UVSubprocessFailedError def is_uv_available() -> bool: diff --git a/src/usethis/_integrations/backend/uv/call.py b/src/usethis/_backend/uv/call.py similarity index 75% rename from src/usethis/_integrations/backend/uv/call.py rename to src/usethis/_backend/uv/call.py index 3cd4534f..7f772350 100644 --- a/src/usethis/_integrations/backend/uv/call.py +++ b/src/usethis/_backend/uv/call.py @@ -1,13 +1,13 @@ from __future__ import annotations +from usethis._backend.uv.errors import UVSubprocessFailedError +from usethis._backend.uv.link_mode import ensure_symlink_mode +from usethis._backend.uv.toml import UVTOMLManager from usethis._config import usethis_config -from usethis._integrations.backend.uv.errors import UVSubprocessFailedError -from usethis._integrations.backend.uv.link_mode import ensure_symlink_mode -from usethis._integrations.backend.uv.toml import UVTOMLManager -from usethis._integrations.file.pyproject_toml.io_ import ( +from usethis._file.pyproject_toml.io_ import ( PyprojectTOMLManager, ) -from usethis._integrations.file.pyproject_toml.valid import ensure_pyproject_validity +from usethis._file.pyproject_toml.valid import ensure_pyproject_validity from usethis._subprocess import SubprocessFailedError, call_subprocess from usethis._types.backend import BackendEnum from usethis.errors import ForbiddenBackendError @@ -27,8 +27,6 @@ def call_uv_subprocess(args: list[str], change_toml: bool) -> str: msg = f"The '{usethis_config.backend.value}' backend is enabled, but a uv subprocess was invoked." raise ForbiddenBackendError(msg) - is_pyproject_toml = (usethis_config.cpd() / "pyproject.toml").exists() - if change_toml and args[0] in { "lock", "add", @@ -40,14 +38,8 @@ def call_uv_subprocess(args: list[str], change_toml: bool) -> str: }: ensure_symlink_mode() - if is_pyproject_toml and change_toml: - if PyprojectTOMLManager().is_locked(): - ensure_pyproject_validity() - PyprojectTOMLManager().write_file() - PyprojectTOMLManager()._content = None - else: - with PyprojectTOMLManager(): - ensure_pyproject_validity() + if change_toml: + _prepare_pyproject_write() if usethis_config.frozen and args[0] in { # Note, not "lock", for which the --frozen flags has quite a different effect @@ -83,6 +75,22 @@ def call_uv_subprocess(args: list[str], change_toml: bool) -> str: return output +def _prepare_pyproject_write() -> None: + is_pyproject_toml = (usethis_config.cpd() / "pyproject.toml").exists() + is_locked = PyprojectTOMLManager().is_locked() + + if is_pyproject_toml and is_locked: + ensure_pyproject_validity() + PyprojectTOMLManager().write_file() + PyprojectTOMLManager()._content = None # Basically a cache clear + elif not is_pyproject_toml and is_locked: + # Similarly; cache clear + PyprojectTOMLManager()._content = None + elif is_pyproject_toml: + with PyprojectTOMLManager(): + ensure_pyproject_validity() + + def add_default_groups_via_uv(groups: list[str]) -> None: """Add default groups using the uv command-line tool.""" if UVTOMLManager().path.exists(): diff --git a/src/usethis/_integrations/backend/uv/deps.py b/src/usethis/_backend/uv/deps.py similarity index 83% rename from src/usethis/_integrations/backend/uv/deps.py rename to src/usethis/_backend/uv/deps.py index af0e3d8e..e75229c2 100644 --- a/src/usethis/_integrations/backend/uv/deps.py +++ b/src/usethis/_backend/uv/deps.py @@ -2,13 +2,13 @@ from typing import TYPE_CHECKING -from usethis._integrations.backend.uv.call import call_uv_subprocess -from usethis._integrations.backend.uv.errors import ( +from usethis._backend.uv.call import call_uv_subprocess +from usethis._backend.uv.errors import ( UVDepGroupError, UVSubprocessFailedError, ) -from usethis._integrations.backend.uv.toml import UVTOMLManager -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._backend.uv.toml import UVTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager if TYPE_CHECKING: from usethis._types.deps import Dependency diff --git a/src/usethis/_integrations/backend/uv/used.py b/src/usethis/_backend/uv/detect.py similarity index 82% rename from src/usethis/_integrations/backend/uv/used.py rename to src/usethis/_backend/uv/detect.py index 9e88ceef..a46b9b1a 100644 --- a/src/usethis/_integrations/backend/uv/used.py +++ b/src/usethis/_backend/uv/detect.py @@ -1,5 +1,5 @@ from usethis._config import usethis_config -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager def is_uv_used() -> bool: diff --git a/src/usethis/_integrations/backend/uv/errors.py b/src/usethis/_backend/uv/errors.py similarity index 100% rename from src/usethis/_integrations/backend/uv/errors.py rename to src/usethis/_backend/uv/errors.py diff --git a/src/usethis/_integrations/backend/uv/init.py b/src/usethis/_backend/uv/init.py similarity index 85% rename from src/usethis/_integrations/backend/uv/init.py rename to src/usethis/_backend/uv/init.py index cacfdb86..ca3d9829 100644 --- a/src/usethis/_integrations/backend/uv/init.py +++ b/src/usethis/_backend/uv/init.py @@ -1,11 +1,11 @@ from __future__ import annotations -from usethis._config import usethis_config -from usethis._integrations.backend.uv import ( # Use this style to allow test mocking +from usethis._backend.uv import ( # Use this style to allow test mocking call, ) -from usethis._integrations.backend.uv.errors import UVInitError, UVSubprocessFailedError -from usethis._integrations.file.pyproject_toml.errors import PyprojectTOMLInitError +from usethis._backend.uv.errors import UVInitError, UVSubprocessFailedError +from usethis._config import usethis_config +from usethis._file.pyproject_toml.errors import PyprojectTOMLInitError def opinionated_uv_init() -> None: diff --git a/src/usethis/_integrations/backend/uv/link_mode.py b/src/usethis/_backend/uv/link_mode.py similarity index 82% rename from src/usethis/_integrations/backend/uv/link_mode.py rename to src/usethis/_backend/uv/link_mode.py index 53d03364..b4ccd25a 100644 --- a/src/usethis/_integrations/backend/uv/link_mode.py +++ b/src/usethis/_backend/uv/link_mode.py @@ -1,10 +1,10 @@ import contextlib -from usethis._integrations.backend.uv.toml import UVTOMLManager -from usethis._integrations.file.pyproject_toml.errors import ( +from usethis._backend.uv.toml import UVTOMLManager +from usethis._file.pyproject_toml.errors import ( PyprojectTOMLValueAlreadySetError, ) -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager def ensure_symlink_mode() -> None: diff --git a/src/usethis/_integrations/backend/uv/lockfile.py b/src/usethis/_backend/uv/lockfile.py similarity index 79% rename from src/usethis/_integrations/backend/uv/lockfile.py rename to src/usethis/_backend/uv/lockfile.py index 6424a806..17f0a675 100644 --- a/src/usethis/_integrations/backend/uv/lockfile.py +++ b/src/usethis/_backend/uv/lockfile.py @@ -1,6 +1,6 @@ +from usethis._backend.uv.call import call_uv_subprocess from usethis._config import usethis_config from usethis._console import tick_print -from usethis._integrations.backend.uv.call import call_uv_subprocess def ensure_uv_lock() -> None: diff --git a/src/usethis/_integrations/backend/uv/python.py b/src/usethis/_backend/uv/python.py similarity index 83% rename from src/usethis/_integrations/backend/uv/python.py rename to src/usethis/_backend/uv/python.py index 58f55e55..044b2bf5 100644 --- a/src/usethis/_integrations/backend/uv/python.py +++ b/src/usethis/_backend/uv/python.py @@ -2,14 +2,14 @@ import re -from usethis._integrations.backend.uv.call import call_uv_subprocess -from usethis._integrations.backend.uv.errors import UVUnparsedPythonVersionError -from usethis._integrations.file.pyproject_toml.errors import PyprojectTOMLNotFoundError -from usethis._integrations.file.pyproject_toml.requires_python import ( +from usethis._backend.uv.call import call_uv_subprocess +from usethis._backend.uv.errors import UVUnparsedPythonVersionError +from usethis._file.pyproject_toml.errors import PyprojectTOMLNotFoundError +from usethis._file.pyproject_toml.requires_python import ( MissingRequiresPythonError, get_requires_python, ) -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion def get_available_uv_python_versions() -> set[str]: diff --git a/src/usethis/_integrations/backend/uv/toml.py b/src/usethis/_backend/uv/toml.py similarity index 75% rename from src/usethis/_integrations/backend/uv/toml.py rename to src/usethis/_backend/uv/toml.py index 2a3ae51f..fa1d1c26 100644 --- a/src/usethis/_integrations/backend/uv/toml.py +++ b/src/usethis/_backend/uv/toml.py @@ -1,6 +1,6 @@ from pathlib import Path -from usethis._integrations.file.toml.io_ import TOMLFileManager +from usethis._file.toml.io_ import TOMLFileManager class UVTOMLManager(TOMLFileManager): diff --git a/src/usethis/_integrations/backend/uv/version.py b/src/usethis/_backend/uv/version.py similarity index 67% rename from src/usethis/_integrations/backend/uv/version.py rename to src/usethis/_backend/uv/version.py index 03c41924..28454b11 100644 --- a/src/usethis/_integrations/backend/uv/version.py +++ b/src/usethis/_backend/uv/version.py @@ -1,9 +1,9 @@ import json -from usethis._integrations.backend.uv.call import call_uv_subprocess -from usethis._integrations.backend.uv.errors import UVSubprocessFailedError +from usethis._backend.uv.call import call_uv_subprocess +from usethis._backend.uv.errors import UVSubprocessFailedError -FALLBACK_UV_VERSION = "0.9.21" +FALLBACK_UV_VERSION = "0.10.0" def get_uv_version() -> str: diff --git a/src/usethis/_config_file.py b/src/usethis/_config_file.py index 2d8fc0bc..9ec6570d 100644 --- a/src/usethis/_config_file.py +++ b/src/usethis/_config_file.py @@ -4,13 +4,13 @@ from pathlib import Path from typing import TYPE_CHECKING -from usethis._integrations.backend.uv.toml import UVTOMLManager +from usethis._backend.uv.toml import UVTOMLManager +from usethis._file.ini.io_ import INIFileManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.setup_cfg.io_ import SetupCFGManager +from usethis._file.toml.io_ import TOMLFileManager +from usethis._file.yaml.io_ import YAMLFileManager from usethis._integrations.ci.bitbucket.yaml import BitbucketPipelinesYAMLManager -from usethis._integrations.file.ini.io_ import INIFileManager -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.setup_cfg.io_ import SetupCFGManager -from usethis._integrations.file.toml.io_ import TOMLFileManager -from usethis._integrations.file.yaml.io_ import YAMLFileManager from usethis._integrations.pre_commit.yaml import PreCommitConfigYAMLManager if TYPE_CHECKING: diff --git a/src/usethis/_core/author.py b/src/usethis/_core/author.py index 2e4a59de..ce80b9cc 100644 --- a/src/usethis/_core/author.py +++ b/src/usethis/_core/author.py @@ -1,6 +1,6 @@ from usethis._console import tick_print +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._init import ensure_pyproject_toml -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager def add_author( diff --git a/src/usethis/_core/badge.py b/src/usethis/_core/badge.py index 1e5121fb..93ff97f6 100644 --- a/src/usethis/_core/badge.py +++ b/src/usethis/_core/badge.py @@ -8,12 +8,12 @@ from usethis._config import usethis_config from usethis._console import plain_print, tick_print, warn_print -from usethis._core.readme import ( +from usethis._core.readme import add_readme +from usethis._integrations.project.name import get_project_name +from usethis._integrations.readme.path import ( NonMarkdownREADMEError, - add_readme, get_markdown_readme_path, ) -from usethis._integrations.project.name import get_project_name if TYPE_CHECKING: from typing_extensions import Self diff --git a/src/usethis/_core/ci.py b/src/usethis/_core/ci.py index bb20ca2c..c953d602 100644 --- a/src/usethis/_core/ci.py +++ b/src/usethis/_core/ci.py @@ -1,12 +1,11 @@ from __future__ import annotations -from typing import TYPE_CHECKING - from usethis._console import how_print, info_print from usethis._integrations.ci.bitbucket.config import ( add_bitbucket_pipelines_config, remove_bitbucket_pipelines_config, ) +from usethis._tool.all_ import ALL_TOOLS from usethis._tool.impl.codespell import CodespellTool from usethis._tool.impl.deptry import DeptryTool from usethis._tool.impl.import_linter import ImportLinterTool @@ -15,13 +14,10 @@ from usethis._tool.impl.pytest import PytestTool from usethis._tool.impl.ruff import RuffTool -if TYPE_CHECKING: - from usethis._tool.base import Tool - -# These are tools would run via pre-commit if available -_CI_QA_TOOLS: list[type[Tool]] = [ # Not including pytest and pre-commit - # This order should match the canonical order in the function which adds - # steps +# Ordered list of QA tools that should run in CI (matches canonical step order) +# These tools run via pre-commit if available, otherwise directly in CI +_CI_QA_TOOL_TYPES = [ + PreCommitTool, PyprojectFmtTool, RuffTool, DeptryTool, @@ -38,18 +34,17 @@ def use_ci_bitbucket( return if not remove: - use_pre_commit = PreCommitTool().is_used() - use_any_tool = ( - use_pre_commit or PytestTool().is_used() or _using_any_ci_qa_tools() - ) + use_any_tool = any(tool.is_used() for tool in ALL_TOOLS) add_bitbucket_pipelines_config(report_placeholder=not use_any_tool) - if use_pre_commit: - PreCommitTool().update_bitbucket_steps() - else: - for tool in _CI_QA_TOOLS: - tool().update_bitbucket_steps() + # Add steps for QA tools in canonical order + for tool_type in _CI_QA_TOOL_TYPES: + # Find the matching tool instance in ALL_TOOLS + for tool in ALL_TOOLS: + if isinstance(tool, tool_type): + tool.update_bitbucket_steps() + break PytestTool().update_bitbucket_steps(matrix_python=matrix_python) @@ -58,10 +53,6 @@ def use_ci_bitbucket( remove_bitbucket_pipelines_config() -def _using_any_ci_qa_tools(): - return any(tool().is_used() for tool in _CI_QA_TOOLS) - - def print_how_to_use_ci_bitbucket() -> None: """Print how to use the Bitbucket CI service.""" _suggest_pytest() diff --git a/src/usethis/_core/list.py b/src/usethis/_core/list.py index c3e1199f..187ca518 100644 --- a/src/usethis/_core/list.py +++ b/src/usethis/_core/list.py @@ -5,8 +5,8 @@ from typing_extensions import assert_never from usethis._console import table_print -from usethis._core.readme import is_readme_used -from usethis._integrations.ci.bitbucket.used import is_bitbucket_used +from usethis._detect.ci.bitbucket import is_bitbucket_used +from usethis._detect.readme import is_readme_used from usethis._tool.all_ import ALL_TOOLS from usethis._tool.impl.ruff import RuffTool diff --git a/src/usethis/_core/readme.py b/src/usethis/_core/readme.py index 709b88da..63b9b1bd 100644 --- a/src/usethis/_core/readme.py +++ b/src/usethis/_core/readme.py @@ -1,16 +1,11 @@ from __future__ import annotations -from typing import TYPE_CHECKING - from usethis._config import usethis_config -from usethis._console import how_print, tick_print, warn_print -from usethis._integrations.file.pyproject_toml.errors import PyprojectTOMLError -from usethis._integrations.file.pyproject_toml.name import get_description +from usethis._console import how_print, tick_print +from usethis._file.pyproject_toml.errors import PyprojectTOMLError +from usethis._file.pyproject_toml.name import get_description from usethis._integrations.project.name import get_project_name -from usethis.errors import UsethisError - -if TYPE_CHECKING: - from pathlib import Path +from usethis._integrations.readme.path import get_readme_path def add_readme() -> None: @@ -57,48 +52,3 @@ def add_readme() -> None: tick_print("Writing 'README.md'.") (usethis_config.cpd() / "README.md").write_text(content, encoding="utf-8") how_print("Populate 'README.md' to help users understand the project.") - - -def get_readme_path(): - path_readme_md = usethis_config.cpd() / "README.md" - path_readme = usethis_config.cpd() / "README" - - if path_readme_md.exists() and path_readme_md.is_file(): - return path_readme_md - elif path_readme.exists() and path_readme.is_file(): - return path_readme - - for path in usethis_config.cpd().glob("README*"): - if path.is_file() and path.stem == "README": - return path - - msg = "No README file found." - raise FileNotFoundError(msg) - - -class NonMarkdownREADMEError(UsethisError): - """Raised when the README file is not Markdown based on its extension.""" - - -def get_markdown_readme_path() -> Path: - path = get_readme_path() - - if path.name == "README.md": - pass - elif path.name == "README": - warn_print("Assuming 'README' file is Markdown.") - else: - msg = f"README file '{path.name}' is not Markdown based on its extension." - raise NonMarkdownREADMEError(msg) - - return path - - -def is_readme_used(): - """Check if the README.md file is used.""" - try: - get_readme_path() - except FileNotFoundError: - return False - - return True diff --git a/src/usethis/_core/show.py b/src/usethis/_core/show.py index 6c5f9e46..39fb7d73 100644 --- a/src/usethis/_core/show.py +++ b/src/usethis/_core/show.py @@ -1,7 +1,7 @@ from __future__ import annotations +from usethis._backend.dispatch import get_backend from usethis._console import plain_print -from usethis._integrations.backend.dispatch import get_backend from usethis._integrations.project.name import get_project_name from usethis._integrations.sonarqube.config import get_sonar_project_properties diff --git a/src/usethis/_core/status.py b/src/usethis/_core/status.py index f4a8d481..31d59953 100644 --- a/src/usethis/_core/status.py +++ b/src/usethis/_core/status.py @@ -1,8 +1,8 @@ from __future__ import annotations from usethis._console import tick_print +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._init import ensure_pyproject_toml -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._types.status import DevelopmentStatusEnum diff --git a/src/usethis/_core/tool.py b/src/usethis/_core/tool.py index 7c90d5e1..471989b9 100644 --- a/src/usethis/_core/tool.py +++ b/src/usethis/_core/tool.py @@ -6,14 +6,14 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.call import call_uv_subprocess +from usethis._backend.uv.lockfile import ensure_uv_lock from usethis._config import usethis_config from usethis._console import info_print, instruct_print, tick_print +from usethis._detect.ci.bitbucket import is_bitbucket_used +from usethis._file.pyproject_toml.valid import ensure_pyproject_validity from usethis._init import ensure_dep_declaration_file, write_simple_requirements_txt -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.call import call_uv_subprocess -from usethis._integrations.backend.uv.lockfile import ensure_uv_lock -from usethis._integrations.ci.bitbucket.used import is_bitbucket_used -from usethis._integrations.file.pyproject_toml.valid import ensure_pyproject_validity from usethis._integrations.mkdocs.core import add_docs_dir from usethis._integrations.pre_commit.core import ( install_pre_commit_hooks, @@ -67,10 +67,8 @@ def use_codespell(*, remove: bool = False, how: bool = False) -> None: ensure_dep_declaration_file() tool.add_dev_deps() - if not PreCommitTool().is_used(): - tool.update_bitbucket_steps() - else: - tool.add_pre_commit_config() + tool.update_bitbucket_steps() + tool.add_pre_commit_config() tool.add_configs() tool.print_how_to_use() @@ -112,10 +110,8 @@ def use_deptry(*, remove: bool = False, how: bool = False) -> None: ensure_dep_declaration_file() tool.add_dev_deps() - if PreCommitTool().is_used(): - tool.add_pre_commit_config() - else: - tool.update_bitbucket_steps() + tool.update_bitbucket_steps() + tool.add_pre_commit_config() tool.add_configs() tool.print_how_to_use() @@ -143,10 +139,8 @@ def use_import_linter(*, remove: bool = False, how: bool = False) -> None: tool.add_configs() if RuffTool().is_used(): RuffTool().apply_rule_config(rule_config) - if PreCommitTool().is_used(): - tool.add_pre_commit_config() - else: - tool.update_bitbucket_steps() + tool.update_bitbucket_steps() + tool.add_pre_commit_config() tool.print_how_to_use() else: @@ -251,7 +245,7 @@ def _add_bitbucket_linter_steps_to_default() -> None: tools: list[Tool] = [PyprojectFmtTool(), DeptryTool(), RuffTool()] for tool in tools: if tool.is_used(): - tool.update_bitbucket_steps() + tool._unconditional_update_bitbucket_steps() def _remove_bitbucket_linter_steps_from_default() -> None: @@ -273,10 +267,8 @@ def use_pyproject_fmt(*, remove: bool = False, how: bool = False) -> None: ensure_dep_declaration_file() tool.add_dev_deps() - if not PreCommitTool().is_used(): - tool.update_bitbucket_steps() - else: - tool.add_pre_commit_config() + tool.update_bitbucket_steps() + tool.add_pre_commit_config() tool.add_configs() tool.print_how_to_use() @@ -480,10 +472,8 @@ def use_ruff( if linter: tool.apply_rule_config(rule_config) - if PreCommitTool().is_used(): - tool.add_pre_commit_config() - else: - tool.update_bitbucket_steps() + tool.update_bitbucket_steps() + tool.add_pre_commit_config() tool.print_how_to_use() else: diff --git a/src/usethis/_deps.py b/src/usethis/_deps.py index 1b376bcc..a8006c4d 100644 --- a/src/usethis/_deps.py +++ b/src/usethis/_deps.py @@ -5,17 +5,17 @@ from pydantic import TypeAdapter from typing_extensions import assert_never -from usethis._config import usethis_config -from usethis._console import instruct_print, tick_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.call import add_default_groups_via_uv -from usethis._integrations.backend.uv.deps import ( +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.call import add_default_groups_via_uv +from usethis._backend.uv.deps import ( add_dep_to_group_via_uv, get_default_groups_via_uv, remove_dep_from_group_via_uv, ) -from usethis._integrations.backend.uv.errors import UVDepGroupError -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._backend.uv.errors import UVDepGroupError +from usethis._config import usethis_config +from usethis._console import instruct_print, tick_print +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._types.backend import BackendEnum from usethis._types.deps import Dependency from usethis.errors import DepGroupError diff --git a/src/usethis/_detect/__init__.py b/src/usethis/_detect/__init__.py new file mode 100644 index 00000000..287441e1 --- /dev/null +++ b/src/usethis/_detect/__init__.py @@ -0,0 +1,9 @@ +"""The detections module. + +The purpose of this module is provide functions which use heuristics to detect whether +different tools are being used or not (and in some cases; to infer which one is the one +to prefer when there are multiple choices; e.g. choosing to use 'uv' as the backend +instead of 'poetry', etc.) + +These detections might make use of lower-level integrations. +""" diff --git a/src/usethis/_integrations/file/__init__.py b/src/usethis/_detect/ci/__init__.py similarity index 100% rename from src/usethis/_integrations/file/__init__.py rename to src/usethis/_detect/ci/__init__.py diff --git a/src/usethis/_detect/ci/bitbucket.py b/src/usethis/_detect/ci/bitbucket.py new file mode 100644 index 00000000..9325b963 --- /dev/null +++ b/src/usethis/_detect/ci/bitbucket.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from usethis._config import usethis_config + + +def is_bitbucket_used() -> bool: + """Detect if Bitbucket Pipelines is used in the current project.""" + return (usethis_config.cpd() / "bitbucket-pipelines.yml").exists() diff --git a/src/usethis/_detect/pre_commit.py b/src/usethis/_detect/pre_commit.py new file mode 100644 index 00000000..a3fb838c --- /dev/null +++ b/src/usethis/_detect/pre_commit.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from usethis._config import usethis_config +from usethis._deps import is_dep_in_any_group +from usethis._types.deps import Dependency + + +def is_pre_commit_used() -> bool: + """Check if pre-commit is being used in the project. + + Returns True if either: + 1. The .pre-commit-config.yaml file exists, or + 2. pre-commit is declared as a dependency + """ + if usethis_config.disable_pre_commit: + return False + + if (usethis_config.cpd() / ".pre-commit-config.yaml").exists(): + return True + + return is_dep_in_any_group(Dependency(name="pre-commit")) diff --git a/src/usethis/_detect/readme.py b/src/usethis/_detect/readme.py new file mode 100644 index 00000000..381aeb5e --- /dev/null +++ b/src/usethis/_detect/readme.py @@ -0,0 +1,11 @@ +from usethis._integrations.readme.path import get_readme_path + + +def is_readme_used(): + """Check if the README.md file is used.""" + try: + get_readme_path() + except FileNotFoundError: + return False + + return True diff --git a/src/usethis/_integrations/file/ini/__init__.py b/src/usethis/_file/__init__.py similarity index 100% rename from src/usethis/_integrations/file/ini/__init__.py rename to src/usethis/_file/__init__.py diff --git a/src/usethis/_integrations/file/dir.py b/src/usethis/_file/dir.py similarity index 100% rename from src/usethis/_integrations/file/dir.py rename to src/usethis/_file/dir.py diff --git a/src/usethis/_integrations/file/pyproject_toml/__init__.py b/src/usethis/_file/ini/__init__.py similarity index 100% rename from src/usethis/_integrations/file/pyproject_toml/__init__.py rename to src/usethis/_file/ini/__init__.py diff --git a/src/usethis/_integrations/file/ini/errors.py b/src/usethis/_file/ini/errors.py similarity index 100% rename from src/usethis/_integrations/file/ini/errors.py rename to src/usethis/_file/ini/errors.py diff --git a/src/usethis/_integrations/file/ini/io_.py b/src/usethis/_file/ini/io_.py similarity index 99% rename from src/usethis/_integrations/file/ini/io_.py rename to src/usethis/_file/ini/io_.py index a9538d04..a21b7e86 100644 --- a/src/usethis/_integrations/file/ini/io_.py +++ b/src/usethis/_file/ini/io_.py @@ -10,7 +10,7 @@ from pydantic import TypeAdapter from typing_extensions import assert_never -from usethis._integrations.file.ini.errors import ( +from usethis._file.ini.errors import ( INIDecodeError, ININestingError, ININotFoundError, diff --git a/src/usethis/_integrations/file/setup_cfg/__init__.py b/src/usethis/_file/pyproject_toml/__init__.py similarity index 100% rename from src/usethis/_integrations/file/setup_cfg/__init__.py rename to src/usethis/_file/pyproject_toml/__init__.py diff --git a/src/usethis/_integrations/file/pyproject_toml/errors.py b/src/usethis/_file/pyproject_toml/errors.py similarity index 97% rename from src/usethis/_integrations/file/pyproject_toml/errors.py rename to src/usethis/_file/pyproject_toml/errors.py index 06f38ab6..405c6f0f 100644 --- a/src/usethis/_integrations/file/pyproject_toml/errors.py +++ b/src/usethis/_file/pyproject_toml/errors.py @@ -1,6 +1,6 @@ from __future__ import annotations -from usethis._integrations.file.toml.errors import ( +from usethis._file.toml.errors import ( TOMLDecodeError, TOMLError, TOMLNotFoundError, diff --git a/src/usethis/_integrations/file/pyproject_toml/io_.py b/src/usethis/_file/pyproject_toml/io_.py similarity index 92% rename from src/usethis/_integrations/file/pyproject_toml/io_.py rename to src/usethis/_file/pyproject_toml/io_.py index 706f7a11..37751783 100644 --- a/src/usethis/_integrations/file/pyproject_toml/io_.py +++ b/src/usethis/_file/pyproject_toml/io_.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import TYPE_CHECKING -from usethis._integrations.file.pyproject_toml.errors import ( +from usethis._file.pyproject_toml.errors import ( PyprojectTOMLDecodeError, PyprojectTOMLNotFoundError, PyprojectTOMLValueAlreadySetError, @@ -11,7 +11,7 @@ UnexpectedPyprojectTOMLIOError, UnexpectedPyprojectTOMLOpenError, ) -from usethis._integrations.file.toml.errors import ( +from usethis._file.toml.errors import ( TOMLDecodeError, TOMLNotFoundError, TOMLValueAlreadySetError, @@ -19,7 +19,7 @@ UnexpectedTOMLIOError, UnexpectedTOMLOpenError, ) -from usethis._integrations.file.toml.io_ import TOMLFileManager +from usethis._file.toml.io_ import TOMLFileManager if TYPE_CHECKING: from collections.abc import Sequence diff --git a/src/usethis/_integrations/file/pyproject_toml/name.py b/src/usethis/_file/pyproject_toml/name.py similarity index 89% rename from src/usethis/_integrations/file/pyproject_toml/name.py rename to src/usethis/_file/pyproject_toml/name.py index f71a0fa8..f1f69aa6 100644 --- a/src/usethis/_integrations/file/pyproject_toml/name.py +++ b/src/usethis/_file/pyproject_toml/name.py @@ -2,11 +2,11 @@ from pydantic import TypeAdapter, ValidationError -from usethis._integrations.file.pyproject_toml.errors import ( +from usethis._file.pyproject_toml.errors import ( PyprojectTOMLProjectDescriptionError, PyprojectTOMLProjectNameError, ) -from usethis._integrations.file.pyproject_toml.project import get_project_dict +from usethis._file.pyproject_toml.project import get_project_dict def get_name() -> str: diff --git a/src/usethis/_integrations/file/pyproject_toml/project.py b/src/usethis/_file/pyproject_toml/project.py similarity index 83% rename from src/usethis/_integrations/file/pyproject_toml/project.py rename to src/usethis/_file/pyproject_toml/project.py index b08e9dd6..053a6ca2 100644 --- a/src/usethis/_integrations/file/pyproject_toml/project.py +++ b/src/usethis/_file/pyproject_toml/project.py @@ -4,10 +4,10 @@ from pydantic import TypeAdapter, ValidationError -from usethis._integrations.file.pyproject_toml.errors import ( +from usethis._file.pyproject_toml.errors import ( PyprojectTOMLProjectSectionError, ) -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager if TYPE_CHECKING: from typing import Any diff --git a/src/usethis/_integrations/file/pyproject_toml/remove.py b/src/usethis/_file/pyproject_toml/remove.py similarity index 83% rename from src/usethis/_integrations/file/pyproject_toml/remove.py rename to src/usethis/_file/pyproject_toml/remove.py index 575b02e5..29a35dad 100644 --- a/src/usethis/_integrations/file/pyproject_toml/remove.py +++ b/src/usethis/_file/pyproject_toml/remove.py @@ -2,7 +2,7 @@ from usethis._config import usethis_config from usethis._console import tick_print -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager def remove_pyproject_toml() -> None: diff --git a/src/usethis/_integrations/file/pyproject_toml/requires_python.py b/src/usethis/_file/pyproject_toml/requires_python.py similarity index 98% rename from src/usethis/_integrations/file/pyproject_toml/requires_python.py rename to src/usethis/_file/pyproject_toml/requires_python.py index 103db244..3090289e 100644 --- a/src/usethis/_integrations/file/pyproject_toml/requires_python.py +++ b/src/usethis/_file/pyproject_toml/requires_python.py @@ -3,8 +3,8 @@ from packaging.specifiers import SpecifierSet from pydantic import TypeAdapter -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.python.version import PythonVersion +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._python.version import PythonVersion class MissingRequiresPythonError(Exception): diff --git a/src/usethis/_integrations/file/pyproject_toml/valid.py b/src/usethis/_file/pyproject_toml/valid.py similarity index 92% rename from src/usethis/_integrations/file/pyproject_toml/valid.py rename to src/usethis/_file/pyproject_toml/valid.py index e4f6177c..625a1ef6 100644 --- a/src/usethis/_integrations/file/pyproject_toml/valid.py +++ b/src/usethis/_file/pyproject_toml/valid.py @@ -6,8 +6,8 @@ from usethis._config import usethis_config from usethis._console import tick_print -from usethis._integrations.file.dir import get_project_name_from_dir -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.dir import get_project_name_from_dir +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager if TYPE_CHECKING: from tomlkit import TOMLDocument diff --git a/src/usethis/_integrations/file/toml/__init__.py b/src/usethis/_file/setup_cfg/__init__.py similarity index 100% rename from src/usethis/_integrations/file/toml/__init__.py rename to src/usethis/_file/setup_cfg/__init__.py diff --git a/src/usethis/_integrations/file/setup_cfg/errors.py b/src/usethis/_file/setup_cfg/errors.py similarity index 96% rename from src/usethis/_integrations/file/setup_cfg/errors.py rename to src/usethis/_file/setup_cfg/errors.py index 642d2023..892bbb1d 100644 --- a/src/usethis/_integrations/file/setup_cfg/errors.py +++ b/src/usethis/_file/setup_cfg/errors.py @@ -1,6 +1,6 @@ from __future__ import annotations -from usethis._integrations.file.ini.errors import ( +from usethis._file.ini.errors import ( INIDecodeError, INIError, ININotFoundError, diff --git a/src/usethis/_integrations/file/setup_cfg/io_.py b/src/usethis/_file/setup_cfg/io_.py similarity index 92% rename from src/usethis/_integrations/file/setup_cfg/io_.py rename to src/usethis/_file/setup_cfg/io_.py index bd7a093d..68c23886 100644 --- a/src/usethis/_integrations/file/setup_cfg/io_.py +++ b/src/usethis/_file/setup_cfg/io_.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import TYPE_CHECKING -from usethis._integrations.file.ini.errors import ( +from usethis._file.ini.errors import ( INIDecodeError, ININotFoundError, INIValueAlreadySetError, @@ -11,8 +11,8 @@ UnexpectedINIIOError, UnexpectedINIOpenError, ) -from usethis._integrations.file.ini.io_ import INIFileManager -from usethis._integrations.file.setup_cfg.errors import ( +from usethis._file.ini.io_ import INIFileManager +from usethis._file.setup_cfg.errors import ( SetupCFGDecodeError, SetupCFGNotFoundError, SetupCFGValueAlreadySetError, diff --git a/src/usethis/_integrations/file/yaml/__init__.py b/src/usethis/_file/toml/__init__.py similarity index 100% rename from src/usethis/_integrations/file/yaml/__init__.py rename to src/usethis/_file/toml/__init__.py diff --git a/src/usethis/_integrations/file/toml/errors.py b/src/usethis/_file/toml/errors.py similarity index 100% rename from src/usethis/_integrations/file/toml/errors.py rename to src/usethis/_file/toml/errors.py diff --git a/src/usethis/_integrations/file/toml/io_.py b/src/usethis/_file/toml/io_.py similarity index 99% rename from src/usethis/_integrations/file/toml/io_.py rename to src/usethis/_file/toml/io_.py index 46e92ac2..6305f682 100644 --- a/src/usethis/_integrations/file/toml/io_.py +++ b/src/usethis/_file/toml/io_.py @@ -13,7 +13,7 @@ from tomlkit.exceptions import TOMLKitError from typing_extensions import assert_never -from usethis._integrations.file.toml.errors import ( +from usethis._file.toml.errors import ( TOMLDecodeError, TOMLNotFoundError, TOMLValueAlreadySetError, diff --git a/src/usethis/_integrations/python/__init__.py b/src/usethis/_file/yaml/__init__.py similarity index 100% rename from src/usethis/_integrations/python/__init__.py rename to src/usethis/_file/yaml/__init__.py diff --git a/src/usethis/_integrations/file/yaml/errors.py b/src/usethis/_file/yaml/errors.py similarity index 100% rename from src/usethis/_integrations/file/yaml/errors.py rename to src/usethis/_file/yaml/errors.py diff --git a/src/usethis/_integrations/file/yaml/io_.py b/src/usethis/_file/yaml/io_.py similarity index 99% rename from src/usethis/_integrations/file/yaml/io_.py rename to src/usethis/_file/yaml/io_.py index 572fb9ae..6d662adc 100644 --- a/src/usethis/_integrations/file/yaml/io_.py +++ b/src/usethis/_file/yaml/io_.py @@ -16,7 +16,7 @@ from typing_extensions import assert_never from usethis._console import info_print -from usethis._integrations.file.yaml.errors import ( +from usethis._file.yaml.errors import ( UnexpectedYAMLIOError, UnexpectedYAMLOpenError, UnexpectedYAMLValueError, @@ -25,7 +25,7 @@ YAMLValueAlreadySetError, YAMLValueMissingError, ) -from usethis._integrations.file.yaml.update import update_ruamel_yaml_map +from usethis._file.yaml.update import update_ruamel_yaml_map from usethis._io import ( KeyValueFileManager, UnexpectedFileIOError, diff --git a/src/usethis/_integrations/file/yaml/update.py b/src/usethis/_file/yaml/update.py similarity index 98% rename from src/usethis/_integrations/file/yaml/update.py rename to src/usethis/_file/yaml/update.py index ab395abd..a93c6def 100644 --- a/src/usethis/_integrations/file/yaml/update.py +++ b/src/usethis/_file/yaml/update.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from typing import Any - from usethis._integrations.file.yaml.io_ import YAMLLiteral + from usethis._file.yaml.io_ import YAMLLiteral def update_ruamel_yaml_map( diff --git a/src/usethis/_init.py b/src/usethis/_init.py index 11b08db7..a76b1815 100644 --- a/src/usethis/_init.py +++ b/src/usethis/_init.py @@ -2,15 +2,15 @@ from typing_extensions import assert_never -from usethis._config import usethis_config -from usethis._console import tick_print -from usethis._deps import get_project_deps -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.init import ( +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.init import ( ensure_pyproject_toml_via_uv, opinionated_uv_init, ) -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._config import usethis_config +from usethis._console import tick_print +from usethis._deps import get_project_deps +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.project.name import get_project_name from usethis._types.backend import BackendEnum diff --git a/src/usethis/_integrations/ci/bitbucket/steps.py b/src/usethis/_integrations/ci/bitbucket/steps.py index 022f13a8..bc98cb21 100644 --- a/src/usethis/_integrations/ci/bitbucket/steps.py +++ b/src/usethis/_integrations/ci/bitbucket/steps.py @@ -8,9 +8,9 @@ from typing_extensions import assert_never import usethis._pipeweld.func +from usethis._backend.dispatch import get_backend from usethis._config import usethis_config from usethis._console import instruct_print, tick_print -from usethis._integrations.backend.dispatch import get_backend from usethis._integrations.ci.bitbucket import schema from usethis._integrations.ci.bitbucket.anchor import ( ScriptItemAnchor, @@ -36,8 +36,8 @@ if TYPE_CHECKING: from ruamel.yaml.anchor import Anchor + from usethis._file.yaml.io_ import YAMLDocument from usethis._integrations.ci.bitbucket.anchor import ScriptItemName - from usethis._integrations.file.yaml.io_ import YAMLDocument _CACHE_LOOKUP = { diff --git a/src/usethis/_integrations/ci/bitbucket/used.py b/src/usethis/_integrations/ci/bitbucket/used.py index 09cb6ad2..e69de29b 100644 --- a/src/usethis/_integrations/ci/bitbucket/used.py +++ b/src/usethis/_integrations/ci/bitbucket/used.py @@ -1,7 +0,0 @@ -from __future__ import annotations - -from usethis._config import usethis_config - - -def is_bitbucket_used() -> bool: - return (usethis_config.cpd() / "bitbucket-pipelines.yml").exists() diff --git a/src/usethis/_integrations/ci/bitbucket/yaml.py b/src/usethis/_integrations/ci/bitbucket/yaml.py index 4fa18339..61a50b99 100644 --- a/src/usethis/_integrations/ci/bitbucket/yaml.py +++ b/src/usethis/_integrations/ci/bitbucket/yaml.py @@ -5,10 +5,10 @@ from pydantic import ValidationError +from usethis._file.yaml.io_ import YAMLFileManager +from usethis._file.yaml.update import update_ruamel_yaml_map from usethis._integrations.ci.bitbucket import schema from usethis._integrations.ci.bitbucket.errors import BitbucketPipelinesYAMLSchemaError -from usethis._integrations.file.yaml.io_ import YAMLFileManager -from usethis._integrations.file.yaml.update import update_ruamel_yaml_map from usethis._integrations.pydantic.dump import fancy_model_dump if TYPE_CHECKING: diff --git a/src/usethis/_integrations/environ/python.py b/src/usethis/_integrations/environ/python.py index f72a4bb9..abc4de47 100644 --- a/src/usethis/_integrations/environ/python.py +++ b/src/usethis/_integrations/environ/python.py @@ -2,18 +2,18 @@ from typing_extensions import assert_never -from usethis._console import warn_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.python import ( +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.python import ( get_supported_uv_minor_python_versions, ) -from usethis._integrations.file.pyproject_toml.errors import PyprojectTOMLNotFoundError -from usethis._integrations.file.pyproject_toml.requires_python import ( +from usethis._console import warn_print +from usethis._file.pyproject_toml.errors import PyprojectTOMLNotFoundError +from usethis._file.pyproject_toml.requires_python import ( MissingRequiresPythonError, get_required_minor_python_versions, get_requires_python, ) -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion from usethis._types.backend import BackendEnum diff --git a/src/usethis/_integrations/pre_commit/core.py b/src/usethis/_integrations/pre_commit/core.py index 75d34e58..d91948fb 100644 --- a/src/usethis/_integrations/pre_commit/core.py +++ b/src/usethis/_integrations/pre_commit/core.py @@ -2,12 +2,12 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.call import call_uv_subprocess +from usethis._backend.uv.detect import is_uv_used +from usethis._backend.uv.errors import UVSubprocessFailedError from usethis._config import usethis_config from usethis._console import info_print, instruct_print, tick_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.call import call_uv_subprocess -from usethis._integrations.backend.uv.errors import UVSubprocessFailedError -from usethis._integrations.backend.uv.used import is_uv_used from usethis._integrations.pre_commit.errors import PreCommitInstallationError from usethis._types.backend import BackendEnum diff --git a/src/usethis/_integrations/pre_commit/yaml.py b/src/usethis/_integrations/pre_commit/yaml.py index a81fcbe7..d44cd335 100644 --- a/src/usethis/_integrations/pre_commit/yaml.py +++ b/src/usethis/_integrations/pre_commit/yaml.py @@ -6,8 +6,8 @@ from pydantic import ValidationError from ruamel.yaml.comments import CommentedMap -from usethis._integrations.file.yaml.io_ import YAMLFileManager -from usethis._integrations.file.yaml.update import update_ruamel_yaml_map +from usethis._file.yaml.io_ import YAMLFileManager +from usethis._file.yaml.update import update_ruamel_yaml_map from usethis._integrations.pre_commit import schema from usethis._integrations.pre_commit.errors import PreCommitConfigYAMLConfigError from usethis._integrations.pydantic.dump import fancy_model_dump diff --git a/src/usethis/_integrations/project/build.py b/src/usethis/_integrations/project/build.py index 7775ea3d..fac3cdc6 100644 --- a/src/usethis/_integrations/project/build.py +++ b/src/usethis/_integrations/project/build.py @@ -1,4 +1,4 @@ -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager def has_pyproject_toml_declared_build_system() -> bool: diff --git a/src/usethis/_integrations/project/name.py b/src/usethis/_integrations/project/name.py index 2816d937..7a062fc5 100644 --- a/src/usethis/_integrations/project/name.py +++ b/src/usethis/_integrations/project/name.py @@ -1,8 +1,8 @@ -from usethis._integrations.file.dir import get_project_name_from_dir -from usethis._integrations.file.pyproject_toml.errors import ( +from usethis._file.dir import get_project_name_from_dir +from usethis._file.pyproject_toml.errors import ( PyprojectTOMLProjectSectionError, ) -from usethis._integrations.file.pyproject_toml.name import get_name +from usethis._file.pyproject_toml.name import get_name def get_project_name() -> str: diff --git a/src/usethis/_integrations/pydantic/dump.py b/src/usethis/_integrations/pydantic/dump.py index 61c9831e..6f9f9273 100644 --- a/src/usethis/_integrations/pydantic/dump.py +++ b/src/usethis/_integrations/pydantic/dump.py @@ -9,7 +9,7 @@ if TYPE_CHECKING: from typing import TypeAlias - from usethis._integrations.file.yaml.io_ import YAMLLiteral + from usethis._file.yaml.io_ import YAMLLiteral ModelLiteral: TypeAlias = bool | int | float | str ModelRepresentation: TypeAlias = ( diff --git a/src/usethis/_integrations/readme/__init__.py b/src/usethis/_integrations/readme/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/usethis/_integrations/readme/path.py b/src/usethis/_integrations/readme/path.py new file mode 100644 index 00000000..aa01885d --- /dev/null +++ b/src/usethis/_integrations/readme/path.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from usethis._config import usethis_config +from usethis._console import warn_print +from usethis.errors import UsethisError + +if TYPE_CHECKING: + from pathlib import Path + + +class NonMarkdownREADMEError(UsethisError): + """Raised when the README file is not Markdown based on its extension.""" + + +def get_readme_path(): + path_readme_md = usethis_config.cpd() / "README.md" + path_readme = usethis_config.cpd() / "README" + + if path_readme_md.exists() and path_readme_md.is_file(): + return path_readme_md + elif path_readme.exists() and path_readme.is_file(): + return path_readme + + for path in usethis_config.cpd().glob("README*"): + if path.is_file() and path.stem == "README": + return path + + msg = "No README file found." + raise FileNotFoundError(msg) + + +def get_markdown_readme_path() -> Path: + path = get_readme_path() + + if path.name == "README.md": + pass + elif path.name == "README": + warn_print("Assuming 'README' file is Markdown.") + else: + msg = f"README file '{path.name}' is not Markdown based on its extension." + raise NonMarkdownREADMEError(msg) + + return path diff --git a/src/usethis/_integrations/sonarqube/config.py b/src/usethis/_integrations/sonarqube/config.py index fadd54dc..7c525cf6 100644 --- a/src/usethis/_integrations/sonarqube/config.py +++ b/src/usethis/_integrations/sonarqube/config.py @@ -5,14 +5,14 @@ from pydantic import TypeAdapter from usethis._config import usethis_config -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.project.layout import get_source_dir_str -from usethis._integrations.python.version import PythonVersion, PythonVersionParseError from usethis._integrations.sonarqube.errors import ( CoverageReportConfigNotFoundError, InvalidSonarQubeProjectKeyError, MissingProjectKeyError, ) +from usethis._python.version import PythonVersion, PythonVersionParseError def get_sonar_project_properties() -> str: diff --git a/src/usethis/_python/__init__.py b/src/usethis/_python/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/usethis/_integrations/python/version.py b/src/usethis/_python/version.py similarity index 100% rename from src/usethis/_integrations/python/version.py rename to src/usethis/_python/version.py diff --git a/src/usethis/_tool/base.py b/src/usethis/_tool/base.py index 6af8a4d4..706d8917 100644 --- a/src/usethis/_tool/base.py +++ b/src/usethis/_tool/base.py @@ -5,10 +5,13 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend from usethis._config import usethis_config from usethis._console import tick_print, warn_print from usethis._deps import add_deps_to_group, is_dep_in_any_group, remove_deps_from_group -from usethis._integrations.backend.dispatch import get_backend +from usethis._detect.ci.bitbucket import is_bitbucket_used +from usethis._detect.pre_commit import is_pre_commit_used +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.ci.bitbucket import schema as bitbucket_schema from usethis._integrations.ci.bitbucket.anchor import ( ScriptItemAnchor as BitbucketScriptItemAnchor, @@ -19,8 +22,6 @@ get_steps_in_default, remove_bitbucket_step_from_default, ) -from usethis._integrations.ci.bitbucket.used import is_bitbucket_used -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.pre_commit.hooks import ( add_repo, get_hook_ids, @@ -40,7 +41,7 @@ if TYPE_CHECKING: from pathlib import Path - from usethis._integrations.backend.uv.deps import Dependency + from usethis._backend.uv.deps import Dependency from usethis._integrations.pre_commit import schema as pre_commit_schema from usethis._io import KeyValueFileManager from usethis._tool.config import ConfigItem, ResolutionT @@ -248,7 +249,14 @@ def is_pre_commit_config_present(self) -> bool: return False def add_pre_commit_config(self) -> None: - """Add the tool's pre-commit configuration.""" + """Add the tool's pre-commit configuration. + + Only adds configuration if pre-commit is being used in the project. + """ + # Only add pre-commit configuration if pre-commit is being used + if not is_pre_commit_used(): + return + repos = self.get_pre_commit_repos() if not repos: @@ -647,8 +655,8 @@ def get_bitbucket_steps( def get_managed_bitbucket_step_names(self) -> list[str]: """These are the names of the Bitbucket steps that are managed by this tool. - They should be removed if they are not currently active according to `get_bitbucket_steps`. - They should also be removed if the tool is removed. + They should be removed if they are not currently active according to + `get_bitbucket_steps`. They should also be removed if the tool is removed. """ return [ step.name for step in self.get_bitbucket_steps() if step.name is not None @@ -663,12 +671,23 @@ def remove_bitbucket_steps(self) -> None: def update_bitbucket_steps(self, *, matrix_python: bool = True) -> None: """Add Bitbucket steps associated with this tool, and remove outdated ones. - Only runs if Bitbucket is used in the project. + Only runs if Bitbucket is used in the project. If pre-commit is being used, + this method short-circuits since the tool will run via pre-commit instead. Args: matrix_python: Whether to use a Python version matrix. When False, - only the current development version is used. + only the current development version is used. """ + # If pre-commit is being used and this is not the PreCommitTool itself, + # don't add Bitbucket steps (the tool will run via pre-commit instead) + if is_pre_commit_used(): + return + + self._unconditional_update_bitbucket_steps(matrix_python=matrix_python) + + def _unconditional_update_bitbucket_steps( + self, *, matrix_python: bool = True + ) -> None: if not is_bitbucket_used() or not self.is_used(): return @@ -679,6 +698,13 @@ def update_bitbucket_steps(self, *, matrix_python: bool = True) -> None: # Remove any old steps that are not active managed by this tool managed_names = self.get_managed_bitbucket_step_names() + + # Early return if there are no steps to add and no managed steps to clean up + # This avoids unnecessarily reading the Bitbucket YAML file (which would + # initialize an empty file with {}) + if not steps and not managed_names: + return + for step in get_steps_in_default(): if step.name in managed_names and not any( bitbucket_steps_are_equivalent(step, step_) for step_ in steps diff --git a/src/usethis/_tool/config.py b/src/usethis/_tool/config.py index d10669a2..8d97424f 100644 --- a/src/usethis/_tool/config.py +++ b/src/usethis/_tool/config.py @@ -7,8 +7,8 @@ from pydantic import BaseModel, InstanceOf from usethis._config import usethis_config +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._init import ensure_pyproject_toml -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._io import Key, KeyValueFileManager if TYPE_CHECKING: diff --git a/src/usethis/_tool/impl/codespell.py b/src/usethis/_tool/impl/codespell.py index 50edc6b7..cfa7c197 100644 --- a/src/usethis/_tool/impl/codespell.py +++ b/src/usethis/_tool/impl/codespell.py @@ -5,20 +5,20 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.detect import is_uv_used from usethis._config import usethis_config from usethis._config_file import DotCodespellRCManager from usethis._console import how_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.used import is_uv_used -from usethis._integrations.file.pyproject_toml.errors import PyprojectTOMLNotFoundError -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.pyproject_toml.requires_python import ( +from usethis._file.pyproject_toml.errors import PyprojectTOMLNotFoundError +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.requires_python import ( MissingRequiresPythonError, get_required_minor_python_versions, ) -from usethis._integrations.file.setup_cfg.io_ import SetupCFGManager +from usethis._file.setup_cfg.io_ import SetupCFGManager from usethis._integrations.pre_commit import schema as pre_commit_schema -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion from usethis._tool.base import Tool from usethis._tool.config import ConfigEntry, ConfigItem, ConfigSpec from usethis._tool.pre_commit import PreCommitConfig diff --git a/src/usethis/_tool/impl/coverage_py.py b/src/usethis/_tool/impl/coverage_py.py index 777f30bc..24dba880 100644 --- a/src/usethis/_tool/impl/coverage_py.py +++ b/src/usethis/_tool/impl/coverage_py.py @@ -5,6 +5,8 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.detect import is_uv_used from usethis._config import usethis_config from usethis._config_file import ( DotCoverageRCManager, @@ -12,10 +14,8 @@ ToxINIManager, ) from usethis._console import how_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.used import is_uv_used -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.setup_cfg.io_ import SetupCFGManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.setup_cfg.io_ import SetupCFGManager from usethis._integrations.project.layout import get_source_dir_str from usethis._tool.base import Tool from usethis._tool.config import ConfigEntry, ConfigItem, ConfigSpec diff --git a/src/usethis/_tool/impl/deptry.py b/src/usethis/_tool/impl/deptry.py index 9f383dcd..c38bea45 100644 --- a/src/usethis/_tool/impl/deptry.py +++ b/src/usethis/_tool/impl/deptry.py @@ -5,10 +5,10 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.detect import is_uv_used from usethis._console import how_print, info_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.used import is_uv_used -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.pre_commit import schema as pre_commit_schema from usethis._integrations.pre_commit.language import get_system_language from usethis._integrations.project.layout import get_source_dir_str diff --git a/src/usethis/_tool/impl/import_linter.py b/src/usethis/_tool/impl/import_linter.py index 066adc06..6e483fa7 100644 --- a/src/usethis/_tool/impl/import_linter.py +++ b/src/usethis/_tool/impl/import_linter.py @@ -7,14 +7,14 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.detect import is_uv_used from usethis._config import usethis_config from usethis._config_file import DotImportLinterManager from usethis._console import how_print, info_print, warn_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.used import is_uv_used -from usethis._integrations.file.ini.io_ import INIFileManager -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.setup_cfg.io_ import SetupCFGManager +from usethis._file.ini.io_ import INIFileManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.setup_cfg.io_ import SetupCFGManager from usethis._integrations.pre_commit import schema as pre_commit_schema from usethis._integrations.pre_commit.language import get_system_language from usethis._integrations.project.errors import ImportGraphBuildFailedError diff --git a/src/usethis/_tool/impl/mkdocs.py b/src/usethis/_tool/impl/mkdocs.py index 8bb0d7b6..b95e9037 100644 --- a/src/usethis/_tool/impl/mkdocs.py +++ b/src/usethis/_tool/impl/mkdocs.py @@ -5,10 +5,10 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.detect import is_uv_used from usethis._config_file import MkDocsYMLManager from usethis._console import how_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.used import is_uv_used from usethis._integrations.project.name import get_project_name from usethis._tool.base import Tool from usethis._tool.config import ConfigEntry, ConfigItem, ConfigSpec diff --git a/src/usethis/_tool/impl/pre_commit.py b/src/usethis/_tool/impl/pre_commit.py index 5e4b71a5..de72a94f 100644 --- a/src/usethis/_tool/impl/pre_commit.py +++ b/src/usethis/_tool/impl/pre_commit.py @@ -5,10 +5,10 @@ from typing_extensions import assert_never -from usethis._config import usethis_config +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.detect import is_uv_used from usethis._console import how_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.used import is_uv_used +from usethis._detect.pre_commit import is_pre_commit_used from usethis._integrations.ci.bitbucket import schema as bitbucket_schema from usethis._integrations.ci.bitbucket.anchor import ( ScriptItemAnchor as BitbucketScriptItemAnchor, @@ -20,7 +20,7 @@ from usethis._types.deps import Dependency if TYPE_CHECKING: - from usethis._integrations.python.version import PythonVersion + from usethis._python.version import PythonVersion _SYNC_WITH_UV_VERSION = "v0.5.0" # Manually bump this version when necessary @@ -51,9 +51,7 @@ def get_pre_commit_config(self) -> PreCommitConfig: assert_never(backend) def is_used(self) -> bool: - if usethis_config.disable_pre_commit: - return False - return super().is_used() + return is_pre_commit_used() def print_how_to_use(self) -> None: backend = get_backend() @@ -109,6 +107,17 @@ def get_bitbucket_steps( else: assert_never(backend) + def update_bitbucket_steps(self, *, matrix_python: bool = True) -> None: + """Add Bitbucket steps associated with pre-commit, and remove outdated ones. + + Only runs if Bitbucket is used in the project. + + Args: + matrix_python: Whether to use a Python version matrix. When False, + only the current development version is used. + """ + self._unconditional_update_bitbucket_steps(matrix_python=matrix_python) + def migrate_config_to_pre_commit(self) -> None: pass diff --git a/src/usethis/_tool/impl/pyproject_fmt.py b/src/usethis/_tool/impl/pyproject_fmt.py index 5396066b..b815c70c 100644 --- a/src/usethis/_tool/impl/pyproject_fmt.py +++ b/src/usethis/_tool/impl/pyproject_fmt.py @@ -4,10 +4,10 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.detect import is_uv_used from usethis._console import how_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.used import is_uv_used -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.pre_commit import schema as pre_commit_schema from usethis._tool.base import Tool from usethis._tool.config import ConfigEntry, ConfigItem, ConfigSpec @@ -15,7 +15,7 @@ from usethis._types.backend import BackendEnum from usethis._types.deps import Dependency -_PYPROJECT_FMT_VERSION = "v2.11.1" # Manually bump this version when necessary +_PYPROJECT_FMT_VERSION = "v2.15.1" # Manually bump this version when necessary class PyprojectFmtTool(Tool): diff --git a/src/usethis/_tool/impl/pyproject_toml.py b/src/usethis/_tool/impl/pyproject_toml.py index 1473ce76..33560bc7 100644 --- a/src/usethis/_tool/impl/pyproject_toml.py +++ b/src/usethis/_tool/impl/pyproject_toml.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING from usethis._console import how_print, info_print, instruct_print -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._tool.base import Tool from usethis._tool.impl.codespell import CodespellTool from usethis._tool.impl.coverage_py import CoveragePyTool @@ -18,7 +18,7 @@ from usethis._tool.impl.ruff import RuffTool if TYPE_CHECKING: - from usethis._integrations.backend.uv.deps import ( + from usethis._backend.uv.deps import ( Dependency, ) diff --git a/src/usethis/_tool/impl/pytest.py b/src/usethis/_tool/impl/pytest.py index 803e1719..6171f3b2 100644 --- a/src/usethis/_tool/impl/pytest.py +++ b/src/usethis/_tool/impl/pytest.py @@ -6,23 +6,23 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.detect import is_uv_used from usethis._config import usethis_config from usethis._config_file import DotPytestINIManager, PytestINIManager, ToxINIManager from usethis._console import how_print, info_print, instruct_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.used import is_uv_used +from usethis._detect.ci.bitbucket import is_bitbucket_used +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.setup_cfg.io_ import SetupCFGManager from usethis._integrations.ci.bitbucket import schema as bitbucket_schema from usethis._integrations.ci.bitbucket.anchor import ( ScriptItemAnchor as BitbucketScriptItemAnchor, ) from usethis._integrations.ci.bitbucket.steps import get_steps_in_default -from usethis._integrations.ci.bitbucket.used import is_bitbucket_used from usethis._integrations.environ.python import get_supported_minor_python_versions -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.setup_cfg.io_ import SetupCFGManager from usethis._integrations.project.build import has_pyproject_toml_declared_build_system from usethis._integrations.project.layout import get_source_dir_str -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion from usethis._tool.base import Tool from usethis._tool.config import ConfigEntry, ConfigItem, ConfigSpec from usethis._tool.rule import RuleConfig @@ -285,6 +285,9 @@ def update_bitbucket_steps(self, *, matrix_python: bool = True) -> None: A bespoke function is needed here to ensure we inform the user about the need to manually add the dependencies if they are not using a backend. + + Unlike other tools, pytest steps should always be added even when pre-commit + is used, because pytest is a test step, not a pre-commit hook. """ # Same early exit as the wrapped super() function if not is_bitbucket_used() or not self.is_used(): @@ -292,7 +295,9 @@ def update_bitbucket_steps(self, *, matrix_python: bool = True) -> None: # But otherwise if not early exiting, we are going to add steps so we might # need to inform the user - super().update_bitbucket_steps(matrix_python=matrix_python) + # Call _unconditional_update_bitbucket_steps directly to bypass the + # pre-commit check in the base class + self._unconditional_update_bitbucket_steps(matrix_python=matrix_python) backend = get_backend() diff --git a/src/usethis/_tool/impl/requirements_txt.py b/src/usethis/_tool/impl/requirements_txt.py index 261f52dd..6a059cc4 100644 --- a/src/usethis/_tool/impl/requirements_txt.py +++ b/src/usethis/_tool/impl/requirements_txt.py @@ -5,9 +5,9 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend from usethis._config import usethis_config from usethis._console import how_print -from usethis._integrations.backend.dispatch import get_backend from usethis._integrations.pre_commit import schema as pre_commit_schema from usethis._integrations.pre_commit.language import get_system_language from usethis._tool.base import Tool @@ -15,7 +15,7 @@ from usethis._types.backend import BackendEnum if TYPE_CHECKING: - from usethis._integrations.backend.uv.deps import ( + from usethis._backend.uv.deps import ( Dependency, ) diff --git a/src/usethis/_tool/impl/ruff.py b/src/usethis/_tool/impl/ruff.py index 54f80b3c..65ad8c37 100644 --- a/src/usethis/_tool/impl/ruff.py +++ b/src/usethis/_tool/impl/ruff.py @@ -5,16 +5,16 @@ from typing_extensions import assert_never +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.detect import is_uv_used from usethis._config import usethis_config from usethis._config_file import DotRuffTOMLManager, RuffTOMLManager from usethis._console import how_print, tick_print -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.used import is_uv_used +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.ci.bitbucket import schema as bitbucket_schema from usethis._integrations.ci.bitbucket.anchor import ( ScriptItemAnchor as BitbucketScriptItemAnchor, ) -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.pre_commit import schema as pre_commit_schema from usethis._tool.base import Tool from usethis._tool.config import ( @@ -31,7 +31,7 @@ from usethis._io import KeyValueFileManager from usethis._tool.rule import Rule, RuleConfig -_RUFF_VERSION = "v0.14.10" # Manually bump this version when necessary +_RUFF_VERSION = "v0.15.0" # Manually bump this version when necessary class RuffTool(Tool): diff --git a/src/usethis/_ui/interface/readme.py b/src/usethis/_ui/interface/readme.py index 33248d11..e01d71ee 100644 --- a/src/usethis/_ui/interface/readme.py +++ b/src/usethis/_ui/interface/readme.py @@ -12,6 +12,7 @@ def readme( backend: BackendEnum = backend_opt, badges: bool = typer.Option(False, "--badges", help="Add relevant badges"), ) -> None: + from usethis._backend.uv.detect import is_uv_used from usethis._config_file import files_manager from usethis._console import err_print from usethis._core.badge import ( @@ -22,7 +23,6 @@ def readme( get_uv_badge, ) from usethis._core.readme import add_readme - from usethis._integrations.backend.uv.used import is_uv_used from usethis._tool.impl.pre_commit import PreCommitTool from usethis._tool.impl.ruff import RuffTool from usethis.errors import UsethisError diff --git a/src/usethis/_ui/ruff.toml b/src/usethis/_ui/ruff.toml index d864d20c..22e36877 100644 --- a/src/usethis/_ui/ruff.toml +++ b/src/usethis/_ui/ruff.toml @@ -1,9 +1,11 @@ extend = "../../../pyproject.toml" lint.flake8-tidy-imports.banned-module-level-imports = [ + "usethis._backend", "usethis._config_file", "usethis._console", "usethis._core", + "usethis._detect", "usethis._init", "usethis._integrations", "usethis._tool", diff --git a/tests/conftest.py b/tests/conftest.py index 321620d7..b0a90d3b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,10 +6,10 @@ import pytest +from usethis._backend.uv.call import call_subprocess, call_uv_subprocess from usethis._config import usethis_config from usethis._console import _cached_warn_print, get_icon_mode -from usethis._integrations.backend.uv.call import call_subprocess, call_uv_subprocess -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd, is_offline from usethis._tool.impl.import_linter import _importlinter_warn_no_packages_found diff --git a/tests/test_install.py b/tests/test_install.py index 37fb92ae..f1f865ad 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -5,9 +5,9 @@ import pytest +from usethis._backend.uv.call import call_uv_subprocess from usethis._config import usethis_config -from usethis._integrations.backend.uv.call import call_uv_subprocess -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._subprocess import call_subprocess from usethis._test import change_cwd diff --git a/tests/test_suite.py b/tests/test_suite.py index 01c6d164..11d317c5 100644 --- a/tests/test_suite.py +++ b/tests/test_suite.py @@ -29,7 +29,9 @@ def test_skeleton_matches(): and not underscore_parent_path.exists() ): msg = ( - f"One of the following paths expected to exist by test suite structure:" + f"Since this test file exists:" + f"\n{test_py}" + f"One of the following paths is expected to exist:" f"\n{std_path}" f"\n{underscore_path}" f"\n{std_parent_path}" diff --git a/tests/usethis/_integrations/backend/test_dispatch.py b/tests/usethis/_backend/test_dispatch.py similarity index 91% rename from tests/usethis/_integrations/backend/test_dispatch.py rename to tests/usethis/_backend/test_dispatch.py index 59e3d055..47afa392 100644 --- a/tests/usethis/_integrations/backend/test_dispatch.py +++ b/tests/usethis/_backend/test_dispatch.py @@ -2,11 +2,11 @@ import pytest -import usethis._integrations.backend.uv.available +import usethis._backend.uv.available +from usethis._backend.dispatch import get_backend +from usethis._backend.uv.errors import UVSubprocessFailedError from usethis._config import usethis_config from usethis._config_file import files_manager -from usethis._integrations.backend.dispatch import get_backend -from usethis._integrations.backend.uv.errors import UVSubprocessFailedError from usethis._test import change_cwd from usethis._types.backend import BackendEnum @@ -50,7 +50,7 @@ def mock_call_uv_subprocess(*_, **__): raise UVSubprocessFailedError monkeypatch.setattr( - usethis._integrations.backend.uv.available, + usethis._backend.uv.available, "call_uv_subprocess", mock_call_uv_subprocess, ) diff --git a/tests/usethis/_integrations/backend/uv/test_available.py b/tests/usethis/_backend/uv/test_available.py similarity index 71% rename from tests/usethis/_integrations/backend/uv/test_available.py rename to tests/usethis/_backend/uv/test_available.py index 781db3be..41438ada 100644 --- a/tests/usethis/_integrations/backend/uv/test_available.py +++ b/tests/usethis/_backend/uv/test_available.py @@ -1,8 +1,8 @@ import pytest -import usethis._integrations.backend.uv.available -from usethis._integrations.backend.uv.available import is_uv_available -from usethis._integrations.backend.uv.errors import UVSubprocessFailedError +import usethis._backend.uv.available +from usethis._backend.uv.available import is_uv_available +from usethis._backend.uv.errors import UVSubprocessFailedError class TestIsUVAvailable: @@ -17,7 +17,7 @@ def mock_call_uv_subprocess(*_, **__): raise UVSubprocessFailedError monkeypatch.setattr( - usethis._integrations.backend.uv.available, + usethis._backend.uv.available, "call_uv_subprocess", mock_call_uv_subprocess, ) diff --git a/tests/usethis/_integrations/backend/uv/test_call.py b/tests/usethis/_backend/uv/test_call.py similarity index 88% rename from tests/usethis/_integrations/backend/uv/test_call.py rename to tests/usethis/_backend/uv/test_call.py index 95a28579..3d6a41a9 100644 --- a/tests/usethis/_integrations/backend/uv/test_call.py +++ b/tests/usethis/_backend/uv/test_call.py @@ -2,11 +2,11 @@ import pytest -import usethis._integrations.backend.uv.call +import usethis._backend.uv.call +from usethis._backend.uv.call import call_uv_subprocess +from usethis._backend.uv.errors import UVSubprocessFailedError from usethis._config import usethis_config -from usethis._integrations.backend.uv.call import call_uv_subprocess -from usethis._integrations.backend.uv.errors import UVSubprocessFailedError -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd @@ -35,7 +35,7 @@ def mock_call_subprocess(args: list[str], *, cwd: Path | None = None) -> str: return " ".join(args) monkeypatch.setattr( - usethis._integrations.backend.uv.call, + usethis._backend.uv.call, "call_subprocess", mock_call_subprocess, ) diff --git a/tests/usethis/_integrations/backend/uv/test_used.py b/tests/usethis/_backend/uv/test_detect.py similarity index 94% rename from tests/usethis/_integrations/backend/uv/test_used.py rename to tests/usethis/_backend/uv/test_detect.py index c88d460e..85b4f02a 100644 --- a/tests/usethis/_integrations/backend/uv/test_used.py +++ b/tests/usethis/_backend/uv/test_detect.py @@ -1,7 +1,7 @@ from pathlib import Path +from usethis._backend.uv.detect import is_uv_used from usethis._config_file import files_manager -from usethis._integrations.backend.uv.used import is_uv_used from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/backend/uv/test_init.py b/tests/usethis/_backend/uv/test_init.py similarity index 71% rename from tests/usethis/_integrations/backend/uv/test_init.py rename to tests/usethis/_backend/uv/test_init.py index 43018513..cb6aab62 100644 --- a/tests/usethis/_integrations/backend/uv/test_init.py +++ b/tests/usethis/_backend/uv/test_init.py @@ -1,7 +1,7 @@ from pathlib import Path -from usethis._integrations.backend.uv.init import opinionated_uv_init -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._backend.uv.init import opinionated_uv_init +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/backend/uv/test_link_mode.py b/tests/usethis/_backend/uv/test_link_mode.py similarity index 81% rename from tests/usethis/_integrations/backend/uv/test_link_mode.py rename to tests/usethis/_backend/uv/test_link_mode.py index 83a8d7fa..406903a3 100644 --- a/tests/usethis/_integrations/backend/uv/test_link_mode.py +++ b/tests/usethis/_backend/uv/test_link_mode.py @@ -1,8 +1,8 @@ from pathlib import Path -from usethis._integrations.backend.uv.link_mode import ensure_symlink_mode -from usethis._integrations.backend.uv.toml import UVTOMLManager -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._backend.uv.link_mode import ensure_symlink_mode +from usethis._backend.uv.toml import UVTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/backend/uv/test_lockfile.py b/tests/usethis/_backend/uv/test_lockfile.py similarity index 90% rename from tests/usethis/_integrations/backend/uv/test_lockfile.py rename to tests/usethis/_backend/uv/test_lockfile.py index 3ef58d49..ae22eba6 100644 --- a/tests/usethis/_integrations/backend/uv/test_lockfile.py +++ b/tests/usethis/_backend/uv/test_lockfile.py @@ -3,8 +3,8 @@ import pytest -import usethis._integrations.backend.uv.lockfile -from usethis._integrations.backend.uv.lockfile import ensure_uv_lock +import usethis._backend.uv.lockfile +from usethis._backend.uv.lockfile import ensure_uv_lock from usethis._test import change_cwd @@ -34,7 +34,7 @@ def mock_call_uv_subprocess(args: list[str], *, change_toml: bool) -> str: return "" monkeypatch.setattr( - usethis._integrations.backend.uv.lockfile, + usethis._backend.uv.lockfile, "call_uv_subprocess", mock_call_uv_subprocess, ) @@ -69,7 +69,7 @@ def mock_call_uv_subprocess(args: list[str], *, change_toml: bool) -> str: return "" monkeypatch.setattr( - usethis._integrations.backend.uv.lockfile, + usethis._backend.uv.lockfile, "call_uv_subprocess", mock_call_uv_subprocess, ) @@ -108,7 +108,7 @@ def mock_call_uv_subprocess(*_: Any, **__: Any) -> str: return "" monkeypatch.setattr( - usethis._integrations.backend.uv.lockfile, + usethis._backend.uv.lockfile, "call_uv_subprocess", mock_call_uv_subprocess, ) diff --git a/tests/usethis/_integrations/backend/uv/test_python.py b/tests/usethis/_backend/uv/test_python.py similarity index 93% rename from tests/usethis/_integrations/backend/uv/test_python.py rename to tests/usethis/_backend/uv/test_python.py index a3f04046..91589534 100644 --- a/tests/usethis/_integrations/backend/uv/test_python.py +++ b/tests/usethis/_backend/uv/test_python.py @@ -1,12 +1,12 @@ from pathlib import Path -from usethis._integrations.backend.uv.python import ( +from usethis._backend.uv.python import ( _parse_python_version_from_uv_output, get_available_uv_python_versions, get_supported_uv_minor_python_versions, ) -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.python.version import PythonVersion +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._python.version import PythonVersion from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/backend/uv/test_version.py b/tests/usethis/_backend/uv/test_version.py similarity index 88% rename from tests/usethis/_integrations/backend/uv/test_version.py rename to tests/usethis/_backend/uv/test_version.py index 7145e164..f28d2344 100644 --- a/tests/usethis/_integrations/backend/uv/test_version.py +++ b/tests/usethis/_backend/uv/test_version.py @@ -3,9 +3,9 @@ import pytest +from usethis._backend.uv.errors import UVSubprocessFailedError +from usethis._backend.uv.version import FALLBACK_UV_VERSION, get_uv_version from usethis._config import usethis_config -from usethis._integrations.backend.uv.errors import UVSubprocessFailedError -from usethis._integrations.backend.uv.version import FALLBACK_UV_VERSION, get_uv_version from usethis._integrations.ci.github.errors import GitHubTagError from usethis._integrations.ci.github.tags import get_github_latest_tag from usethis._test import change_cwd @@ -52,7 +52,7 @@ def mock_call_uv_subprocess(*_, **__) -> str: raise UVSubprocessFailedError monkeypatch.setattr( - "usethis._integrations.backend.uv.version.call_uv_subprocess", + "usethis._backend.uv.version.call_uv_subprocess", mock_call_uv_subprocess, ) diff --git a/tests/usethis/_core/test_author.py b/tests/usethis/_core/test_author.py index 4ad0f9ec..f5b5780c 100644 --- a/tests/usethis/_core/test_author.py +++ b/tests/usethis/_core/test_author.py @@ -3,8 +3,8 @@ import pytest from usethis._core.author import add_author -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.toml.errors import ( +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.toml.errors import ( TOMLValueInvalidError, TOMLValueMissingError, ) diff --git a/tests/usethis/_core/test_core_badge.py b/tests/usethis/_core/test_core_badge.py index a7b0d635..fe9cba79 100644 --- a/tests/usethis/_core/test_core_badge.py +++ b/tests/usethis/_core/test_core_badge.py @@ -3,7 +3,7 @@ import pytest from usethis._core.badge import Badge, add_badge, is_badge, remove_badge -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd diff --git a/tests/usethis/_core/test_core_ci.py b/tests/usethis/_core/test_core_ci.py index 3206a800..58dfcb37 100644 --- a/tests/usethis/_core/test_core_ci.py +++ b/tests/usethis/_core/test_core_ci.py @@ -2,7 +2,7 @@ import pytest -import usethis._integrations.python.version +import usethis._python.version from usethis._config import usethis_config from usethis._config_file import files_manager from usethis._core.ci import use_ci_bitbucket @@ -13,8 +13,8 @@ use_pyproject_fmt, use_ruff, ) +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.ci.bitbucket.steps import get_steps_in_default -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd from usethis._types.backend import BackendEnum @@ -612,7 +612,7 @@ def test_matrix_disabled_creates_single_step( """Test that --no-matrix-python creates only one test step for current version.""" # Arrange monkeypatch.setattr( - usethis._integrations.python.version, + usethis._python.version, "_get_python_version", lambda: "3.10.0", ) @@ -642,7 +642,7 @@ def test_matrix_disabled_with_none_backend( """Test that --no-matrix-python works with backend=none.""" # Arrange monkeypatch.setattr( - usethis._integrations.python.version, + usethis._python.version, "_get_python_version", lambda: "3.11.0", ) diff --git a/tests/usethis/_core/test_core_readme.py b/tests/usethis/_core/test_core_readme.py index 66e0c062..68905a14 100644 --- a/tests/usethis/_core/test_core_readme.py +++ b/tests/usethis/_core/test_core_readme.py @@ -3,7 +3,7 @@ import pytest from usethis._core.readme import add_readme -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd diff --git a/tests/usethis/_core/test_core_tool.py b/tests/usethis/_core/test_core_tool.py index 6bf858af..6e35ea1a 100644 --- a/tests/usethis/_core/test_core_tool.py +++ b/tests/usethis/_core/test_core_tool.py @@ -6,8 +6,11 @@ import pytest -import usethis._integrations.backend.dispatch -import usethis._integrations.python.version +import usethis._backend.dispatch +import usethis._python.version +from usethis._backend.uv.call import call_uv_subprocess +from usethis._backend.uv.link_mode import ensure_symlink_mode +from usethis._backend.uv.toml import UVTOMLManager from usethis._config import usethis_config from usethis._config_file import RuffTOMLManager, files_manager from usethis._core.ci import use_ci_bitbucket @@ -26,13 +29,10 @@ use_tool, ) from usethis._deps import add_deps_to_group, get_deps_from_group, is_dep_satisfied_in -from usethis._integrations.backend.uv.call import call_uv_subprocess -from usethis._integrations.backend.uv.link_mode import ensure_symlink_mode -from usethis._integrations.backend.uv.toml import UVTOMLManager -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.pre_commit.hooks import _HOOK_ORDER, get_hook_ids from usethis._integrations.pre_commit.yaml import PreCommitConfigYAMLManager -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion from usethis._test import change_cwd from usethis._tool.all_ import ALL_TOOLS from usethis._tool.impl.pre_commit import _SYNC_WITH_UV_VERSION @@ -2906,7 +2906,7 @@ def test_no_backend( # Set the Python version to 3.10 monkeypatch.setattr( - usethis._integrations.python.version, + usethis._python.version, "_get_python_version", lambda: "3.10.0", ) @@ -3118,7 +3118,7 @@ def test_remove_no_backend( ): # Set the Python version to 3.10 monkeypatch.setattr( - usethis._integrations.python.version, + usethis._python.version, "_get_python_version", lambda: "3.10.0", ) @@ -3355,7 +3355,7 @@ def test_none_backend( """) monkeypatch.setattr( # Do this rather than set Backend.none to test the auto behaviour messages - usethis._integrations.backend.dispatch, + usethis._backend.dispatch, "is_uv_available", lambda *_, **__: False, ) diff --git a/tests/usethis/_core/test_status.py b/tests/usethis/_core/test_status.py index 76adb9fa..601aaeec 100644 --- a/tests/usethis/_core/test_status.py +++ b/tests/usethis/_core/test_status.py @@ -3,7 +3,7 @@ import pytest from usethis._core.status import use_development_status -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd from usethis._types.status import DevelopmentStatusEnum diff --git a/tests/usethis/_integrations/file/ini/test_ini_io_.py b/tests/usethis/_file/ini/test_ini_io_.py similarity index 99% rename from tests/usethis/_integrations/file/ini/test_ini_io_.py rename to tests/usethis/_file/ini/test_ini_io_.py index eae10db0..dd99b957 100644 --- a/tests/usethis/_integrations/file/ini/test_ini_io_.py +++ b/tests/usethis/_file/ini/test_ini_io_.py @@ -4,7 +4,7 @@ import pytest from configupdater import ConfigUpdater -from usethis._integrations.file.ini.errors import ( +from usethis._file.ini.errors import ( INIDecodeError, ININestingError, ININotFoundError, @@ -15,7 +15,7 @@ UnexpectedINIIOError, UnexpectedINIOpenError, ) -from usethis._integrations.file.ini.io_ import INIFileManager +from usethis._file.ini.io_ import INIFileManager from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/file/pyproject_toml/test_name.py b/tests/usethis/_file/pyproject_toml/test_name.py similarity index 94% rename from tests/usethis/_integrations/file/pyproject_toml/test_name.py rename to tests/usethis/_file/pyproject_toml/test_name.py index 9d7fa40c..3a8f35f6 100644 --- a/tests/usethis/_integrations/file/pyproject_toml/test_name.py +++ b/tests/usethis/_file/pyproject_toml/test_name.py @@ -2,13 +2,13 @@ import pytest -from usethis._integrations.file.pyproject_toml.errors import ( +from usethis._file.pyproject_toml.errors import ( PyprojectTOMLProjectDescriptionError, PyprojectTOMLProjectNameError, PyprojectTOMLProjectSectionError, ) -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.pyproject_toml.name import get_description, get_name +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.name import get_description, get_name from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/file/pyproject_toml/test_project.py b/tests/usethis/_file/pyproject_toml/test_project.py similarity index 89% rename from tests/usethis/_integrations/file/pyproject_toml/test_project.py rename to tests/usethis/_file/pyproject_toml/test_project.py index 4c1b63db..913bd1c8 100644 --- a/tests/usethis/_integrations/file/pyproject_toml/test_project.py +++ b/tests/usethis/_file/pyproject_toml/test_project.py @@ -2,12 +2,12 @@ import pytest -from usethis._integrations.file.pyproject_toml.errors import ( +from usethis._file.pyproject_toml.errors import ( PyprojectTOMLNotFoundError, PyprojectTOMLProjectSectionError, ) -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.pyproject_toml.project import get_project_dict +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.project import get_project_dict from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/file/pyproject_toml/test_pyproject_toml_io_.py b/tests/usethis/_file/pyproject_toml/test_pyproject_toml_io_.py similarity index 98% rename from tests/usethis/_integrations/file/pyproject_toml/test_pyproject_toml_io_.py rename to tests/usethis/_file/pyproject_toml/test_pyproject_toml_io_.py index 1d620a1a..951b31fc 100644 --- a/tests/usethis/_integrations/file/pyproject_toml/test_pyproject_toml_io_.py +++ b/tests/usethis/_file/pyproject_toml/test_pyproject_toml_io_.py @@ -4,7 +4,7 @@ import pytest from tomlkit import TOMLDocument -from usethis._integrations.file.pyproject_toml.errors import ( +from usethis._file.pyproject_toml.errors import ( PyprojectTOMLDecodeError, PyprojectTOMLNotFoundError, PyprojectTOMLValueAlreadySetError, @@ -12,7 +12,7 @@ UnexpectedPyprojectTOMLIOError, UnexpectedPyprojectTOMLOpenError, ) -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/file/pyproject_toml/test_remove.py b/tests/usethis/_file/pyproject_toml/test_remove.py similarity index 78% rename from tests/usethis/_integrations/file/pyproject_toml/test_remove.py rename to tests/usethis/_file/pyproject_toml/test_remove.py index df1cdd14..d2f50b16 100644 --- a/tests/usethis/_integrations/file/pyproject_toml/test_remove.py +++ b/tests/usethis/_file/pyproject_toml/test_remove.py @@ -2,8 +2,8 @@ import pytest -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.pyproject_toml.remove import remove_pyproject_toml +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.remove import remove_pyproject_toml from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/file/pyproject_toml/test_requires_python.py b/tests/usethis/_file/pyproject_toml/test_requires_python.py similarity index 96% rename from tests/usethis/_integrations/file/pyproject_toml/test_requires_python.py rename to tests/usethis/_file/pyproject_toml/test_requires_python.py index 8142f0bd..9d9561bf 100644 --- a/tests/usethis/_integrations/file/pyproject_toml/test_requires_python.py +++ b/tests/usethis/_file/pyproject_toml/test_requires_python.py @@ -3,14 +3,14 @@ import pytest from packaging.specifiers import SpecifierSet -from usethis._integrations.file.pyproject_toml.errors import PyprojectTOMLNotFoundError -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.pyproject_toml.requires_python import ( +from usethis._file.pyproject_toml.errors import PyprojectTOMLNotFoundError +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.requires_python import ( MissingRequiresPythonError, get_required_minor_python_versions, get_requires_python, ) -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/file/pyproject_toml/test_valid.py b/tests/usethis/_file/pyproject_toml/test_valid.py similarity index 96% rename from tests/usethis/_integrations/file/pyproject_toml/test_valid.py rename to tests/usethis/_file/pyproject_toml/test_valid.py index 35bf989e..ba6aa6b1 100644 --- a/tests/usethis/_integrations/file/pyproject_toml/test_valid.py +++ b/tests/usethis/_file/pyproject_toml/test_valid.py @@ -1,7 +1,7 @@ from pathlib import Path -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.pyproject_toml.valid import ensure_pyproject_validity +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.valid import ensure_pyproject_validity from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/file/toml/test_toml_io_.py b/tests/usethis/_file/toml/test_toml_io_.py similarity index 99% rename from tests/usethis/_integrations/file/toml/test_toml_io_.py rename to tests/usethis/_file/toml/test_toml_io_.py index e455b1c2..11a062f5 100644 --- a/tests/usethis/_integrations/file/toml/test_toml_io_.py +++ b/tests/usethis/_file/toml/test_toml_io_.py @@ -5,12 +5,12 @@ import tomlkit.api import tomlkit.items -from usethis._integrations.file.toml.errors import ( +from usethis._file.toml.errors import ( TOMLValueAlreadySetError, TOMLValueInvalidError, TOMLValueMissingError, ) -from usethis._integrations.file.toml.io_ import TOMLFileManager +from usethis._file.toml.io_ import TOMLFileManager from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/file/yaml/test_update.py b/tests/usethis/_file/yaml/test_update.py similarity index 98% rename from tests/usethis/_integrations/file/yaml/test_update.py rename to tests/usethis/_file/yaml/test_update.py index d74df88f..ebcb1a48 100644 --- a/tests/usethis/_integrations/file/yaml/test_update.py +++ b/tests/usethis/_file/yaml/test_update.py @@ -2,8 +2,8 @@ from ruamel.yaml.comments import CommentedMap, CommentedSeq -from usethis._integrations.file.yaml.io_ import edit_yaml -from usethis._integrations.file.yaml.update import ( +from usethis._file.yaml.io_ import edit_yaml +from usethis._file.yaml.update import ( lcs_list_update, update_ruamel_yaml_map, ) diff --git a/tests/usethis/_integrations/file/yaml/test_yaml_io_.py b/tests/usethis/_file/yaml/test_yaml_io_.py similarity index 99% rename from tests/usethis/_integrations/file/yaml/test_yaml_io_.py rename to tests/usethis/_file/yaml/test_yaml_io_.py index 85023f4c..81890623 100644 --- a/tests/usethis/_integrations/file/yaml/test_yaml_io_.py +++ b/tests/usethis/_file/yaml/test_yaml_io_.py @@ -16,7 +16,7 @@ from ruamel.yaml.scalarstring import FoldedScalarString, LiteralScalarString from ruamel.yaml.timestamp import TimeStamp -from usethis._integrations.file.yaml.errors import ( +from usethis._file.yaml.errors import ( UnexpectedYAMLIOError, UnexpectedYAMLOpenError, UnexpectedYAMLValueError, @@ -25,7 +25,7 @@ YAMLValueAlreadySetError, YAMLValueMissingError, ) -from usethis._integrations.file.yaml.io_ import YAMLDocument, YAMLFileManager, edit_yaml +from usethis._file.yaml.io_ import YAMLDocument, YAMLFileManager, edit_yaml from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/ci/bitbucket/test_bitbucket_config.py b/tests/usethis/_integrations/ci/bitbucket/test_bitbucket_config.py index e58d467f..398d5eee 100644 --- a/tests/usethis/_integrations/ci/bitbucket/test_bitbucket_config.py +++ b/tests/usethis/_integrations/ci/bitbucket/test_bitbucket_config.py @@ -1,6 +1,8 @@ from pathlib import Path from usethis._config_file import files_manager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.yaml.io_ import edit_yaml from usethis._integrations.ci.bitbucket import schema from usethis._integrations.ci.bitbucket.anchor import ScriptItemAnchor from usethis._integrations.ci.bitbucket.config import ( @@ -11,8 +13,6 @@ add_bitbucket_step_in_default, ) from usethis._integrations.ci.bitbucket.yaml import BitbucketPipelinesYAMLManager -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.yaml.io_ import edit_yaml from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/ci/bitbucket/test_bitbucket_schema.py b/tests/usethis/_integrations/ci/bitbucket/test_bitbucket_schema.py index c4ab8135..095a0c99 100644 --- a/tests/usethis/_integrations/ci/bitbucket/test_bitbucket_schema.py +++ b/tests/usethis/_integrations/ci/bitbucket/test_bitbucket_schema.py @@ -5,11 +5,11 @@ import requests from requests.exceptions import RequestException -from usethis._integrations.ci.bitbucket import schema -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.pyproject_toml.requires_python import ( +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.requires_python import ( get_requires_python, ) +from usethis._integrations.ci.bitbucket import schema from usethis._test import change_cwd, is_offline diff --git a/tests/usethis/_integrations/ci/bitbucket/test_used.py b/tests/usethis/_integrations/ci/bitbucket/test_used.py index 81941aa1..5845694a 100644 --- a/tests/usethis/_integrations/ci/bitbucket/test_used.py +++ b/tests/usethis/_integrations/ci/bitbucket/test_used.py @@ -1,6 +1,6 @@ from pathlib import Path -from usethis._integrations.ci.bitbucket.used import is_bitbucket_used +from usethis._detect.ci.bitbucket import is_bitbucket_used from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/environ/test_python.py b/tests/usethis/_integrations/environ/test_python.py index ed483df1..842f471a 100644 --- a/tests/usethis/_integrations/environ/test_python.py +++ b/tests/usethis/_integrations/environ/test_python.py @@ -4,7 +4,7 @@ from usethis._config_file import files_manager from usethis._integrations.environ.python import get_supported_minor_python_versions -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion from usethis._test import change_cwd from usethis._types.backend import BackendEnum @@ -17,11 +17,11 @@ def test_with_requires_python_range( # Arrange monkeypatch.setattr( - "usethis._integrations.environ.python.get_backend", + "usethis._backend.dispatch.get_backend", lambda: BackendEnum.none, ) monkeypatch.setattr( - "usethis._integrations.python.version.PythonVersion.from_interpreter", + "usethis._python.version.PythonVersion.from_interpreter", lambda: PythonVersion(major="3", minor="10", patch=None), ) (tmp_path / "pyproject.toml").write_text( @@ -54,11 +54,11 @@ def test_with_single_version( # Arrange monkeypatch.setattr( - "usethis._integrations.environ.python.get_backend", + "usethis._backend.dispatch.get_backend", lambda: BackendEnum.none, ) monkeypatch.setattr( - "usethis._integrations.python.version.PythonVersion.from_interpreter", + "usethis._python.version.PythonVersion.from_interpreter", lambda: PythonVersion(major="3", minor="10", patch=None), ) (tmp_path / "pyproject.toml").write_text( diff --git a/tests/usethis/_integrations/pre_commit/test_pre_commit_schema.py b/tests/usethis/_integrations/pre_commit/test_pre_commit_schema.py index 497bb344..b14a1755 100644 --- a/tests/usethis/_integrations/pre_commit/test_pre_commit_schema.py +++ b/tests/usethis/_integrations/pre_commit/test_pre_commit_schema.py @@ -5,8 +5,8 @@ import requests from requests.exceptions import RequestException -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.pyproject_toml.requires_python import ( +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.requires_python import ( get_requires_python, ) from usethis._integrations.pre_commit import schema diff --git a/tests/usethis/_integrations/sonarqube/test_sonarqube_config.py b/tests/usethis/_integrations/sonarqube/test_sonarqube_config.py index 90adda9f..1057d6cd 100644 --- a/tests/usethis/_integrations/sonarqube/test_sonarqube_config.py +++ b/tests/usethis/_integrations/sonarqube/test_sonarqube_config.py @@ -2,10 +2,9 @@ import pytest +from usethis._backend.uv.python import uv_python_pin +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._init import ensure_pyproject_toml -from usethis._integrations.backend.uv.python import uv_python_pin -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.python.version import PythonVersion from usethis._integrations.sonarqube.config import ( _validate_project_key, get_sonar_project_properties, @@ -15,6 +14,7 @@ InvalidSonarQubeProjectKeyError, MissingProjectKeyError, ) +from usethis._python.version import PythonVersion from usethis._test import change_cwd diff --git a/tests/usethis/_integrations/python/test_version.py b/tests/usethis/_python/test_version.py similarity index 99% rename from tests/usethis/_integrations/python/test_version.py rename to tests/usethis/_python/test_version.py index b3b8385c..94fb5c68 100644 --- a/tests/usethis/_integrations/python/test_version.py +++ b/tests/usethis/_python/test_version.py @@ -4,7 +4,7 @@ import pytest -from usethis._integrations.python.version import ( +from usethis._python.version import ( PythonVersion, PythonVersionParseError, ) diff --git a/tests/usethis/_tool/impl/test_codespell.py b/tests/usethis/_tool/impl/test_codespell.py index 3616b847..f03bca3f 100644 --- a/tests/usethis/_tool/impl/test_codespell.py +++ b/tests/usethis/_tool/impl/test_codespell.py @@ -8,7 +8,7 @@ from usethis._integrations.ci.github.errors import GitHubTagError from usethis._integrations.ci.github.tags import get_github_latest_tag from usethis._integrations.pre_commit import schema -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion from usethis._test import change_cwd from usethis._tool.impl.codespell import CodespellTool from usethis._types.backend import BackendEnum @@ -253,7 +253,7 @@ def test_no_pyproject_toml_3pt10( ): # Arrange - no pyproject.toml file, using interpreter version monkeypatch.setattr( - "usethis._integrations.python.version.PythonVersion.from_interpreter", + "usethis._python.version.PythonVersion.from_interpreter", lambda: PythonVersion(major="3", minor="10", patch=None), ) # Act @@ -269,7 +269,7 @@ def test_no_pyproject_toml_3pt11( ): # Arrange - no pyproject.toml file, using interpreter version monkeypatch.setattr( - "usethis._integrations.python.version.PythonVersion.from_interpreter", + "usethis._python.version.PythonVersion.from_interpreter", lambda: PythonVersion(major="3", minor="11", patch=None), ) # Act diff --git a/tests/usethis/_tool/impl/test_coverage_py.py b/tests/usethis/_tool/impl/test_coverage_py.py index a7ef384e..cbba4ad7 100644 --- a/tests/usethis/_tool/impl/test_coverage_py.py +++ b/tests/usethis/_tool/impl/test_coverage_py.py @@ -1,7 +1,7 @@ from pathlib import Path from usethis._config_file import DotCoverageRCTOMLManager, files_manager -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd from usethis._tool.impl.coverage_py import CoveragePyTool diff --git a/tests/usethis/_tool/impl/test_deptry.py b/tests/usethis/_tool/impl/test_deptry.py index cb4b8ace..2ea21668 100644 --- a/tests/usethis/_tool/impl/test_deptry.py +++ b/tests/usethis/_tool/impl/test_deptry.py @@ -3,7 +3,7 @@ import pytest from usethis._config_file import files_manager -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.pre_commit import schema from usethis._test import change_cwd from usethis._tool.config import ConfigEntry, ConfigItem diff --git a/tests/usethis/_tool/impl/test_pytest.py b/tests/usethis/_tool/impl/test_pytest.py index be3e1772..fc6605df 100644 --- a/tests/usethis/_tool/impl/test_pytest.py +++ b/tests/usethis/_tool/impl/test_pytest.py @@ -5,9 +5,9 @@ import usethis._tool.impl.pytest from usethis._config import usethis_config from usethis._config_file import files_manager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.ci.bitbucket.steps import add_placeholder_step_in_default -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion from usethis._test import change_cwd from usethis._tool.impl.pytest import PytestTool from usethis._types.backend import BackendEnum @@ -222,7 +222,7 @@ def test_no_backend( usethis._tool.impl.pytest, "get_backend", lambda: BackendEnum.none ) monkeypatch.setattr( - "usethis._integrations.python.version.PythonVersion.from_interpreter", + "usethis._python.version.PythonVersion.from_interpreter", lambda: PythonVersion(major="3", minor="10", patch=None), ) (tmp_path / "bitbucket-pipelines.yml").touch() diff --git a/tests/usethis/_tool/impl/test_ruff.py b/tests/usethis/_tool/impl/test_ruff.py index e5282717..8ef13af8 100644 --- a/tests/usethis/_tool/impl/test_ruff.py +++ b/tests/usethis/_tool/impl/test_ruff.py @@ -6,9 +6,9 @@ from usethis._config import usethis_config from usethis._config_file import DotRuffTOMLManager, RuffTOMLManager, files_manager from usethis._core.tool import use_ruff +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.ci.github.errors import GitHubTagError from usethis._integrations.ci.github.tags import get_github_latest_tag -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._integrations.pre_commit import schema from usethis._test import change_cwd from usethis._tool.impl.ruff import RuffTool diff --git a/tests/usethis/_tool/test_base.py b/tests/usethis/_tool/test_base.py index e02f38c5..1f76c9a3 100644 --- a/tests/usethis/_tool/test_base.py +++ b/tests/usethis/_tool/test_base.py @@ -7,8 +7,8 @@ from usethis._config_file import files_manager from usethis._console import how_print from usethis._deps import add_deps_to_group -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager -from usethis._integrations.file.setup_cfg.io_ import SetupCFGManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.setup_cfg.io_ import SetupCFGManager from usethis._integrations.pre_commit import schema from usethis._integrations.pre_commit.hooks import _PLACEHOLDER_ID, get_hook_ids from usethis._io import KeyValueFileManager @@ -539,7 +539,7 @@ def print_how_to_use(self) -> None: nrc_tool = NoRepoConfigsTool() # Act - with change_cwd(uv_init_dir): + with change_cwd(uv_init_dir), files_manager(): nrc_tool.add_pre_commit_config() # Assert @@ -583,6 +583,10 @@ def get_pre_commit_config(self) -> PreCommitConfig: inform_how_to_use_on_migrate=False, ) + # To indicate that we are, in fact, using pre-commit (otherwise the + # add-tool method is no-op + (uv_init_dir / ".pre-commit-config.yaml").touch() + mrt_tool = MultiRepoTool() # Act @@ -606,8 +610,11 @@ def test_file_created(self, tmp_path: Path): # Arrange tool = MyTool() - # Act with change_cwd(tmp_path), files_manager(): + # Ensure pre-commit is considered used + add_deps_to_group([Dependency(name="pre-commit")], "dev") + + # Act tool.add_pre_commit_config() # Assert @@ -618,7 +625,7 @@ def test_file_not_created(self, tmp_path: Path): tool = DefaultTool() # Act - with change_cwd(tmp_path): + with change_cwd(tmp_path), files_manager(): tool.add_pre_commit_config() # Assert @@ -630,8 +637,12 @@ def test_add_successful( # Arrange tool = MyTool() - # Act with change_cwd(tmp_path), files_manager(): + # Ensure pre-commit is considered used + add_deps_to_group([Dependency(name="pre-commit")], "dev") + capfd.readouterr() + + # Act tool.add_pre_commit_config() # Assert @@ -752,8 +763,11 @@ def test_two_hooks_one_repo(self, tmp_path: Path): # Arrange th_tool = TwoHooksTool() + # Ensure pre-commit is considered used + (tmp_path / ".pre-commit-config.yaml").touch() + # Act - with change_cwd(tmp_path): + with change_cwd(tmp_path), files_manager(): # Currently, multiple hooks are not supported. # If we do ever support it, this with-raises block and # test skip can be removed. Instead, we will need to write this test. diff --git a/tests/usethis/_ui/interface/test_init.py b/tests/usethis/_ui/interface/test_init.py index 6ec85879..126fdfa5 100644 --- a/tests/usethis/_ui/interface/test_init.py +++ b/tests/usethis/_ui/interface/test_init.py @@ -5,7 +5,7 @@ from usethis._config import usethis_config from usethis._integrations.pre_commit.hooks import get_hook_ids from usethis._integrations.pre_commit.yaml import PreCommitConfigYAMLManager -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion from usethis._test import CliRunner, change_cwd from usethis._ui.app import app diff --git a/tests/usethis/_ui/interface/test_interface_ci.py b/tests/usethis/_ui/interface/test_interface_ci.py index 1ae19e34..1f2ea0a7 100644 --- a/tests/usethis/_ui/interface/test_interface_ci.py +++ b/tests/usethis/_ui/interface/test_interface_ci.py @@ -2,9 +2,9 @@ import pytest -import usethis._integrations.python.version +import usethis._python.version from usethis._config import usethis_config -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import CliRunner, change_cwd from usethis._ui.app import app as main_app from usethis._ui.interface.ci import app @@ -87,7 +87,7 @@ def test_maximal_config_none_backend( """ monkeypatch.setattr( - usethis._integrations.python.version, + usethis._python.version, "_get_python_version", lambda: "3.10.0", ) @@ -165,7 +165,7 @@ def test_no_matrix_python_flag( """Test that --no-matrix-python flag creates single test step via CLI.""" # Arrange monkeypatch.setattr( - usethis._integrations.python.version, + usethis._python.version, "_get_python_version", lambda: "3.10.0", ) diff --git a/tests/usethis/_ui/interface/test_spellcheck.py b/tests/usethis/_ui/interface/test_spellcheck.py index c49b48ba..7a6e6948 100644 --- a/tests/usethis/_ui/interface/test_spellcheck.py +++ b/tests/usethis/_ui/interface/test_spellcheck.py @@ -2,7 +2,7 @@ from usethis._config_file import files_manager from usethis._deps import get_deps_from_group -from usethis._integrations.python.version import PythonVersion +from usethis._python.version import PythonVersion from usethis._test import CliRunner, change_cwd from usethis._types.deps import Dependency from usethis._ui.app import app diff --git a/tests/usethis/_ui/interface/test_tool.py b/tests/usethis/_ui/interface/test_tool.py index 5a1ed23d..0367ca96 100644 --- a/tests/usethis/_ui/interface/test_tool.py +++ b/tests/usethis/_ui/interface/test_tool.py @@ -2,10 +2,10 @@ import pytest +from usethis._backend.uv.call import call_uv_subprocess from usethis._config import usethis_config from usethis._config_file import files_manager -from usethis._integrations.backend.uv.call import call_uv_subprocess -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._subprocess import SubprocessFailedError, call_subprocess from usethis._test import CliRunner, change_cwd from usethis._tool.all_ import ALL_TOOLS diff --git a/tests/usethis/test_config.py b/tests/usethis/test_config.py index 5df320a1..cfc3b256 100644 --- a/tests/usethis/test_config.py +++ b/tests/usethis/test_config.py @@ -2,8 +2,8 @@ import pytest +from usethis._backend.uv.call import call_uv_subprocess from usethis._config import UsethisConfig, usethis_config -from usethis._integrations.backend.uv.call import call_uv_subprocess from usethis._test import change_cwd from usethis._types.backend import BackendEnum from usethis.errors import ForbiddenBackendError diff --git a/tests/usethis/test_deps.py b/tests/usethis/test_deps.py index 9a8b68e1..383e1a0a 100644 --- a/tests/usethis/test_deps.py +++ b/tests/usethis/test_deps.py @@ -3,7 +3,12 @@ import pytest from packaging.requirements import InvalidRequirement -import usethis._integrations.backend.uv.deps +import usethis._backend.uv.deps +from usethis._backend.uv.errors import ( + UVDepGroupError, + UVSubprocessFailedError, +) +from usethis._backend.uv.toml import UVTOMLManager from usethis._config import usethis_config from usethis._config_file import files_manager from usethis._deps import ( @@ -18,12 +23,7 @@ register_default_group, remove_deps_from_group, ) -from usethis._integrations.backend.uv.errors import ( - UVDepGroupError, - UVSubprocessFailedError, -) -from usethis._integrations.backend.uv.toml import UVTOMLManager -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd from usethis._types.backend import BackendEnum from usethis._types.deps import Dependency @@ -577,7 +577,7 @@ def mock_call_uv_subprocess(*_, **__): raise UVSubprocessFailedError monkeypatch.setattr( - usethis._integrations.backend.uv.deps, + usethis._backend.uv.deps, "call_uv_subprocess", mock_call_uv_subprocess, ) @@ -615,6 +615,17 @@ def test_none_backend(self, tmp_path: Path, capfd: pytest.CaptureFixture[str]): assert not err assert out == "☐ Add the test dependency 'pytest'.\n" + def test_no_pyproject_toml(self, tmp_path: Path): + # Act + with change_cwd(tmp_path), PyprojectTOMLManager(): + add_deps_to_group([Dependency(name="pytest")], "test") + + # Assert + assert get_deps_from_group("test") == [Dependency(name="pytest")] + + content = (tmp_path / "pyproject.toml").read_text() + assert "[dependency-groups]" in content + class TestRemoveDepsFromGroup: @pytest.mark.usefixtures("_vary_network_conn") @@ -752,7 +763,7 @@ def mock_call_uv_subprocess(*_, **__): raise UVSubprocessFailedError monkeypatch.setattr( - usethis._integrations.backend.uv.deps, + usethis._backend.uv.deps, "call_uv_subprocess", mock_call_uv_subprocess, ) diff --git a/tests/usethis/test_init.py b/tests/usethis/test_init.py index 2902ccb4..4a236905 100644 --- a/tests/usethis/test_init.py +++ b/tests/usethis/test_init.py @@ -3,17 +3,17 @@ import pytest -import usethis._integrations.backend.uv.call +import usethis._backend.uv.call +from usethis._backend.uv.errors import UVInitError, UVSubprocessFailedError from usethis._config import usethis_config from usethis._config_file import files_manager +from usethis._file.pyproject_toml.errors import PyprojectTOMLInitError +from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._init import ( ensure_pyproject_toml, project_init, write_simple_requirements_txt, ) -from usethis._integrations.backend.uv.errors import UVInitError, UVSubprocessFailedError -from usethis._integrations.file.pyproject_toml.errors import PyprojectTOMLInitError -from usethis._integrations.file.pyproject_toml.io_ import PyprojectTOMLManager from usethis._test import change_cwd from usethis._types.backend import BackendEnum @@ -41,7 +41,7 @@ def mock_call_uv_subprocess(*_: Any, **__: Any) -> None: raise UVSubprocessFailedError monkeypatch.setattr( - usethis._integrations.backend.uv.call, + usethis._backend.uv.call, "call_uv_subprocess", mock_call_uv_subprocess, ) @@ -184,7 +184,7 @@ def mock_call_uv_subprocess(*_: Any, **__: Any) -> None: raise UVSubprocessFailedError monkeypatch.setattr( - usethis._integrations.backend.uv.call, + usethis._backend.uv.call, "call_uv_subprocess", mock_call_uv_subprocess, )