---
paths:
  - "*/src/**/*.py"
  - "*/tests/**/*.py"
  - "*/pyproject.toml"
---

# uv Project Tooling

These projects use [uv](https://docs.astral.sh/uv/) for all Python tooling. uv replaces pip, pip-tools, poetry, and virtualenv in one tool.

## Running tools

Always prefix Python tool invocations with `uv run`. Never use bare `python -m`, `.venv/bin/`, or `python3 -m` variants.

```bash
uv run pytest tests/ -v
uv run mypy src/
uv run ruff check src/ tests/
uv run ruff format src/ tests/
uv run interrogate src/
```

`uv run` auto-creates and syncs the virtualenv, then runs the command inside it.

## Managing dependencies

```bash
uv add requests                 # add runtime dependency
uv add --dev pytest mypy        # add dev dependency (goes to [dependency-groups] dev)
uv remove requests              # remove a dependency
uv lock                         # regenerate lockfile without syncing
uv sync                         # sync .venv to match lockfile
uv tree                         # show dependency tree
```

Don't duplicate transitive deps — if package A depends on package B, only declare B in A's `[project]` dependencies. Downstream consumers get it transitively.

## Local path dependencies

When one repo depends on another locally (e.g. codeflash-python → codeflash-core):

```toml
[project]
dependencies = ["codeflash-core>=0.1.0"]

[tool.uv.sources]
codeflash-core = { path = "../codeflash-core" }
```

`[tool.uv.sources]` tells uv where to find the package during development. The abstract dependency in `[project]` is what gets published.

After changing a local dependency, rebuild it:

```bash
uv sync --reinstall-package codeflash-core
```

## Build backend

These projects use `uv_build` as the build backend:

```toml
[build-system]
requires = ["uv_build>=0.7.2,<0.8"]
build-backend = "uv_build"
```

## Dependency groups (PEP 735)

Dev dependencies go in `[dependency-groups]`, not `[project.optional-dependencies]`:

```toml
[dependency-groups]
dev = [
    "interrogate>=1.7.0",
    "mypy>=1.14",
    "pytest>=7.4",
    "ruff>=0.15.7",
]
```

`uv sync` installs the `dev` group by default. Use `uv sync --no-dev` to skip it.

## Other useful commands

```bash
uv lock --upgrade-package requests    # upgrade one dep
uv lock --upgrade                     # upgrade all deps
uv export --format requirements-txt   # export for non-uv consumers
uv cache clean                        # clear uv cache
uv python pin 3.12                    # pin Python version for directory
```

## pyproject.toml reference

```toml
[project]
name = "package-name"
version = "0.1.0"
requires-python = ">=3.9"
dependencies = [
    "attrs>=26.1.0",
]

[build-system]
requires = ["uv_build>=0.7.2,<0.8"]
build-backend = "uv_build"

[dependency-groups]
dev = [
    "interrogate>=1.7.0",
    "mypy>=1.14",
    "pytest>=7.4",
    "ruff>=0.15.7",
]

[tool.ruff]
src = ["src", "tests"]
line-length = 79
target-version = "py39"

[tool.ruff.lint]
select = ["ALL"]
ignore = [
    "A",        # shadowing is fine
    "ANN",      # Mypy is better at this
    "ARG",      # unused arguments are common w/ interfaces
    "COM",      # formatter takes care of that
    "D",        # we prefer our own docstring style
    "FIX",      # we don't want these
    "INP001",   # tests have no __init__.py
    "ISC001",   # conflicts with formatter
    "PD",       # not using pandas
    "RET504",   # unnecessary-assign is useful for readability
    "TD",       # we don't want these
    "TID252",   # relative imports are fine
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = [
    "PLR2004",  # magic values are fine in tests
    "S101",     # assert is fine in tests
    "SIM300",   # Yoda style is fine in tests (expected == actual)
    "SLF001",   # private member access is fine in tests
]

[tool.mypy]
strict = true
pretty = true

[tool.pytest.ini_options]
addopts = ["--strict-markers", "--strict-config", "--import-mode=importlib"]
testpaths = "tests"
xfail_strict = true

[tool.interrogate]
fail-under = 100
verbose = 2
```
