Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
### Environment Setup (REQUIRED FIRST STEP)
1. **ALWAYS run `uv sync` first** after cloning or before any development work. This installs all dependencies including dev, test, and doc groups.
2. Python version: Use 3.10.18 (specified in `.python-version`). The CI tests 3.10-3.14, but development uses oldest supported version.
3. uv version: Minimum 0.6.8 required (documented in README and `pyproject.toml`). Check with `uv --version`.
3. uv version: Minimum 0.8.18 required (documented in README and `pyproject.toml`). Check with `uv --version`.

### Running Commands
**Critical:** ALWAYS prefix Python commands with `uv run` to use the project environment:
Expand Down Expand Up @@ -166,7 +166,7 @@ See CONTRIBUTING.md Architecture section for detailed documentation and examples

**ci.yml** - Main CI pipeline (runs on push to main, PRs, and daily cron):
- Matrix testing: Python 3.10-3.14 × (ubuntu/macos/windows)
- Also tests min dependencies (3.10 + uv 0.6.8) and max dependencies (3.14 + latest)
- Also tests min dependencies (3.10 + uv 0.8.18) and max dependencies (3.14 + latest)
- Runs prek checks, pytest with coverage
- CodSpeed benchmarking (Python 3.13)
- Codecov upload (Python 3.14)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ jobs:
os: "ubuntu-latest"
- dependencies: "min"
python_version: "3.10"
uv_version: "0.6.8"
uv_version: "0.8.18"
checks: "false"
- dependencies: "max"
python_version: "3.14"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Inspired by an [**R** package of the same name](https://usethis.r-lib.org/index.
## 🧭 Installation

First, it is strongly recommended you [install the uv package manager](https://docs.astral.sh/uv/getting-started/installation/): this is a simple, documented process. If you're already using uv, make sure you're using at least
version v0.6.8 (run `uv --version` to check, and `uv self update` to upgrade).
version v0.8.18 (run `uv --version` to check, and `uv self update` to upgrade).

You can install usethis directly into the project environment:

Expand Down
6 changes: 3 additions & 3 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ pygments==2.19.1 \
--hash=sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f \
--hash=sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c
# via mkdocs-material
pymdown-extensions==10.14.3 \
--hash=sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9 \
--hash=sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b
pymdown-extensions==10.19.1 \
--hash=sha256:4969c691009a389fb1f9712dd8e7bd70dcc418d15a0faf70acb5117d022f7de8 \
--hash=sha256:e8698a66055b1dc0dca2a7f2c9d0ea6f5faa7834a9c432e3535ab96c0c4e509b
# via mkdocs-material
python-dateutil==2.9.0.post0 \
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
Expand Down
2 changes: 1 addition & 1 deletion docs/start/installation.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 🧭 Installation

First, it is strongly recommended you [install the uv package manager](https://docs.astral.sh/uv/getting-started/installation/): this is a simple, documented process. If you're already using uv, make sure you're using at least
version v0.6.8 (run `uv --version` to check, and `uv self update` to upgrade).
version v0.8.18 (run `uv --version` to check, and `uv self update` to upgrade).

You can install usethis directly into the project environment:

Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ doc = [
"mkdocs-material>=9.7.0",
]
uv = [
"uv>=0.6.8",
"uv>=0.8.18",
]

[tool.hatch.version]
Expand Down Expand Up @@ -195,7 +195,7 @@ rules.invalid-assignment = "ignore"
rules.possibly-missing-attribute = "ignore"

[tool.uv]
required-version = ">=0.6.8" # Sync with README
required-version = ">=0.8.18" # Sync with README
default-groups = [ "test", "dev", "doc" ]
link-mode = "symlink"

Expand Down
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -755,9 +755,9 @@ pyinstrument==5.1.1 \
--hash=sha256:f2d640230b71c6d9ac8f27a9c5cd07fc8a6acad9196d1e48d9c33658b176fb80 \
--hash=sha256:f4912edf01912792d2f44dfc43d3f041acc7ea634d0300b3394711963a431d1b \
--hash=sha256:fa254f269a72a007b5d02c18cd4b67081e0efabbd33e18acdbd5e3be905afa06
pymdown-extensions==10.14.3 \
--hash=sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9 \
--hash=sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b
pymdown-extensions==10.19.1 \
--hash=sha256:4969c691009a389fb1f9712dd8e7bd70dcc418d15a0faf70acb5117d022f7de8 \
--hash=sha256:e8698a66055b1dc0dca2a7f2c9d0ea6f5faa7834a9c432e3535ab96c0c4e509b
# via mkdocs-material
pyright==1.1.403 \
--hash=sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104 \
Expand Down
9 changes: 7 additions & 2 deletions src/usethis/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from contextlib import contextmanager
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Literal

from usethis._types.backend import BackendEnum

Expand Down Expand Up @@ -46,12 +46,13 @@ class UsethisConfig:
alert_only: bool = False
instruct_only: bool = False
backend: BackendEnum = BackendEnum(BACKEND_DEFAULT) # noqa: RUF009
inferred_backend: Literal[BackendEnum.uv, BackendEnum.none] | None = None
disable_pre_commit: bool = False
subprocess_verbose: bool = False
project_dir: Path | None = None

@contextmanager
def set( # noqa: PLR0913
def set( # noqa: PLR0913, PLR0915
self,
*,
offline: bool | None = None,
Expand All @@ -71,6 +72,7 @@ def set( # noqa: PLR0913
old_alert_only = self.alert_only
old_instruct_only = self.instruct_only
old_backend = self.backend
old_inferred_backend = self.inferred_backend
old_disable_pre_commit = self.disable_pre_commit
old_subprocess_verbose = self.subprocess_verbose
old_project_dir = self.project_dir
Expand Down Expand Up @@ -100,6 +102,8 @@ def set( # noqa: PLR0913
self.alert_only = alert_only
self.instruct_only = instruct_only
self.backend = backend
if backend is not BackendEnum.auto:
self.inferred_backend = backend
self.disable_pre_commit = disable_pre_commit
self.subprocess_verbose = subprocess_verbose
if isinstance(project_dir, str):
Expand All @@ -112,6 +116,7 @@ def set( # noqa: PLR0913
self.alert_only = old_alert_only
self.instruct_only = old_instruct_only
self.backend = old_backend
self.inferred_backend = old_inferred_backend
self.disable_pre_commit = old_disable_pre_commit
self.subprocess_verbose = old_subprocess_verbose
self.project_dir = old_project_dir
Expand Down
23 changes: 15 additions & 8 deletions src/usethis/_integrations/backend/dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,23 @@


def get_backend() -> Literal[BackendEnum.uv, BackendEnum.none]:
if usethis_config.backend is not BackendEnum.auto:
return usethis_config.backend
# Effectively we cache the inference, storing it in usethis_config.
if usethis_config.inferred_backend is not None:
return usethis_config.inferred_backend

if is_poetry_used():
if usethis_config.backend is not BackendEnum.auto:
usethis_config.inferred_backend = usethis_config.backend
elif is_poetry_used():
warn_print(
"This project is using Poetry, which is not fully supported by usethis."
)
return BackendEnum.none

if is_uv_used() or is_uv_available():
return BackendEnum.uv
usethis_config.inferred_backend = BackendEnum.none
elif is_uv_used():
usethis_config.inferred_backend = BackendEnum.uv
elif not (usethis_config.cpd() / "pyproject.toml").exists() and is_uv_available():
# If there's not likely to be a backend in use yet, and uv is available.
usethis_config.inferred_backend = BackendEnum.uv
else:
usethis_config.inferred_backend = BackendEnum.none

return BackendEnum.none
return usethis_config.inferred_backend
2 changes: 1 addition & 1 deletion src/usethis/_integrations/backend/uv/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from usethis._integrations.backend.uv.call import call_uv_subprocess
from usethis._integrations.backend.uv.errors import UVSubprocessFailedError

FALLBACK_UV_VERSION = "0.9.9"
FALLBACK_UV_VERSION = "0.9.18"


def get_uv_version() -> str:
Expand Down
5 changes: 3 additions & 2 deletions src/usethis/_integrations/pre_commit/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ def install_pre_commit_hooks() -> None:
if usethis_config.frozen:
if backend is BackendEnum.uv and is_uv_used():
instruct_print("Run 'uv run pre-commit install' to register pre-commit.")
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
instruct_print("Run 'pre-commit install' to register pre-commit.")
else:
assert_never(backend)
return

if backend is BackendEnum.uv:
Expand Down
3 changes: 2 additions & 1 deletion src/usethis/_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def call_subprocess(args: list[str], *, cwd: Path | None = None) -> str:
stderr = bmsg_stderr.decode()
stdout = bmsg_stdout.decode()

msg = "Failed to run uv subprocess:"
msg = "Failed to run subprocess:"
msg += f"\n {' '.join(args)}"
if stderr:
msg += f"\n{stderr=}"
if stdout:
Expand Down
5 changes: 3 additions & 2 deletions src/usethis/_tool/impl/codespell.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ def print_how_to_use(self) -> None:
how_print(
"Run 'uv run pre-commit run codespell --all-files' to run the Codespell spellchecker."
)
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print(
"Run 'pre-commit run codespell --all-files' to run the Codespell spellchecker."
)
else:
assert_never(backend)
elif install_method == "devdep" or install_method is None:
cmd = self.default_command()
how_print(f"Run '{cmd}' to run the Codespell spellchecker.")
Expand Down
12 changes: 8 additions & 4 deletions src/usethis/_tool/impl/coverage_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from pathlib import Path
from typing import TYPE_CHECKING

from typing_extensions import assert_never

from usethis._config import usethis_config
from usethis._config_file import (
CoverageRCManager,
Expand Down Expand Up @@ -42,16 +44,18 @@ def print_how_to_use(self) -> None:
how_print(
f"Run 'uv run pytest --cov' to run your tests with {self.name}."
)
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print(f"Run 'pytest --cov' to run your tests with {self.name}.")
else:
assert_never(backend)
elif backend is BackendEnum.uv and is_uv_used():
how_print(
f"Run 'uv run coverage help' to see available {self.name} commands."
)
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print(f"Run 'coverage help' to see available {self.name} commands.")
else:
assert_never(backend)

def get_test_deps(self, *, unconditional: bool = False) -> list[Dependency]:
from usethis._tool.impl.pytest import ( # to avoid circularity; # noqa: PLC0415
Expand Down
5 changes: 3 additions & 2 deletions src/usethis/_tool/impl/deptry.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ def print_how_to_use(self) -> None:
how_print(
f"Run 'uv run pre-commit run deptry --all-files' to run {self.name}."
)
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print(
f"Run 'pre-commit run deptry --all-files' to run {self.name}."
)
else:
assert_never(backend)
elif install_method == "devdep" or install_method is None:
cmd = self.default_command()
how_print(f"Run '{cmd}' to run deptry.")
Expand Down
5 changes: 3 additions & 2 deletions src/usethis/_tool/impl/import_linter.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,12 @@ def print_how_to_use(self) -> None:
how_print(
f"Run 'uv run pre-commit run import-linter --all-files' to run {self.name}."
)
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print(
f"Run 'pre-commit run import-linter --all-files' to run {self.name}."
)
else:
assert_never(backend)
elif install_method == "devdep" or install_method is None:
cmd = self.default_command()
how_print(f"Run '{cmd}' to run {self.name}.")
Expand Down
7 changes: 5 additions & 2 deletions src/usethis/_tool/impl/mkdocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from pathlib import Path
from typing import TYPE_CHECKING

from typing_extensions import assert_never

from usethis._config_file import MkDocsYMLManager
from usethis._console import how_print
from usethis._integrations.backend.dispatch import get_backend
Expand All @@ -29,10 +31,11 @@ def print_how_to_use(self) -> None:
if backend is BackendEnum.uv and is_uv_used():
how_print("Run 'uv run mkdocs build' to build the documentation.")
how_print("Run 'uv run mkdocs serve' to serve the documentation locally.")
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print("Run 'mkdocs build' to build the documentation.")
how_print("Run 'mkdocs serve' to serve the documentation locally.")
else:
assert_never(backend)

def get_doc_deps(self, *, unconditional: bool = False) -> list[Dependency]:
deps = [Dependency(name="mkdocs")]
Expand Down
7 changes: 4 additions & 3 deletions src/usethis/_tool/impl/pre_commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from usethis._types.backend import BackendEnum
from usethis._types.deps import Dependency

_SYNC_WITH_UV_VERSION = "v0.4.0" # Manually bump this version when necessary
_SYNC_WITH_UV_VERSION = "v0.5.0" # Manually bump this version when necessary


class PreCommitTool(Tool):
Expand Down Expand Up @@ -58,9 +58,10 @@ def print_how_to_use(self) -> None:
how_print(
"Run 'uv run pre-commit run --all-files' to run the hooks manually."
)
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print("Run 'pre-commit run --all-files' to run the hooks manually.")
else:
assert_never(backend)

def get_dev_deps(self, *, unconditional: bool = False) -> list[Dependency]:
return [Dependency(name="pre-commit")]
Expand Down
5 changes: 3 additions & 2 deletions src/usethis/_tool/impl/pyproject_fmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ def print_how_to_use(self) -> None:
how_print(
f"Run 'uv run pre-commit run pyproject-fmt --all-files' to run {self.name}."
)
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print(
f"Run 'pre-commit run pyproject-fmt --all-files' to run {self.name}."
)
else:
assert_never(backend)
elif install_method == "devdep" or install_method is None:
cmd = self.default_command()
how_print(f"Run '{cmd}' to run {self.name}.")
Expand Down
22 changes: 13 additions & 9 deletions src/usethis/_tool/impl/ruff.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from usethis._io import KeyValueFileManager
from usethis._tool.rule import Rule, RuleConfig

_RUFF_VERSION = "v0.14.4" # Manually bump this version when necessary
_RUFF_VERSION = "v0.14.9" # Manually bump this version when necessary


class RuffTool(Tool):
Expand Down Expand Up @@ -84,21 +84,23 @@ def print_how_to_use_linter(self) -> None:
how_print(
"Run 'uv run pre-commit run ruff --all-files' to run the Ruff linter."
)
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print(
"Run 'pre-commit run ruff --all-files' to run the Ruff linter."
)
else:
assert_never(backend)
elif install_method == "devdep" or install_method is None:
if backend is BackendEnum.uv and is_uv_used():
how_print(
"Run 'uv run ruff check --fix' to run the Ruff linter with autofixes."
)
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print(
"Run 'ruff check --fix' to run the Ruff linter with autofixes."
)
else:
assert_never(backend)
else:
assert_never(install_method)

Expand All @@ -113,15 +115,17 @@ def print_how_to_use_formatter(self) -> None:
how_print(
"Run 'uv run pre-commit run ruff-format' to run the Ruff formatter."
)
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print("Run 'pre-commit run ruff-format' to run the Ruff formatter.")
else:
assert_never(backend)
elif install_method == "devdep" or install_method is None:
if backend is BackendEnum.uv and is_uv_used():
how_print("Run 'uv run ruff format' to run the Ruff formatter.")
else:
assert backend in (BackendEnum.none, BackendEnum.uv)
elif backend in (BackendEnum.none, BackendEnum.uv):
how_print("Run 'ruff format' to run the Ruff formatter.")
else:
assert_never(backend)
else:
assert_never(install_method)

Expand Down
Loading