python-project · git:20260816.2914172 · 2026-08-16 · sha256 190c479dbf0b13c2

python-project git:20260816.2914172A

Immutable. This exact content never changes and is served at /api/v1/blob/190c479dbf0b13c2.

---
name: python-project
description: Scaffold and modernize a Python project with uv. Sets up the src/ layout; a pyproject.toml on uv's native uv_build backend; runtime deps plus a dev dependency group (PEP 735) pinning developer tooling like ruff, ty (or pyright), and pytest; a committed uv.lock and pinned interpreter; a thin __main__ entry point; and a Makefile whose pr target runs the full local gate. Use when: creating a new Python project, package, library, or CLI with uv; creating or configuring a pyproject.toml; choosing a build backend (uv_build) or dependency layout; restructuring an existing repo to the src/ layout; migrating from pip/requirements.txt, poetry, or pipenv onto uv; setting up a dev dependency group or pinning Python dev tools (ruff, ty or pyright, pytest); or adding Makefile targets (pr, fmt, lint, typecheck, test, build) to a Python codebase. Not for writing functions, type hints, tests, or publishing to PyPI — those are separate Python skills.
license: MIT
---

# Scaffold a Python project

Creates a Python repository with the src/ layout, uv-managed dependencies, pinned developer
tooling, and a Makefile gate. Apply the `python-style`, `python-typing`, and `python-testing`
skills while filling in real code, and wire releases with the `python-release` skill.

## 1. Initialize with uv

`uv` is the only tool for interpreters, virtual environments, dependencies, locking, building, and
publishing — do not reach for pip, pipenv, poetry, conda, pyenv, or pipx alongside it.

```sh
uv init --package myapp   # packaged app with a CLI entry point and [build-system]
uv init --lib myapp       # importable library (same src/ layout, no console script)
```

Both flavors produce the src/ layout and a `[build-system]`; the bare `uv init` (flat, no build
system) is only for throwaway scripts. uv writes `pyproject.toml` and `src/myapp/`, and the first
`uv sync`/`uv run` writes the `uv.lock` you commit.

## 2. Lay out the tree

```text
pyproject.toml
uv.lock              # committed — the reproducible, cross-platform resolution
.python-version      # pins the interpreter uv provisions for `uv run`
README.md            # referenced by `readme` in pyproject.toml
src/myapp/__init__.py
src/myapp/__main__.py
tests/
Makefile
```

- **src/ layout, never flat.** Tests run against the installed package, so a missing
  `__init__.py` or a packaging mistake fails locally instead of being masked by an
  import-from-cwd. Copy [templates/python-version](templates/python-version) to `.python-version`.
- One module per concern, named for what it provides (`store`, `client`, `config`) — never
  `utils`, `common`, or `helpers`.

## 3. Configure pyproject.toml on the uv_build backend

Copy [templates/pyproject.toml](templates/pyproject.toml). The choices that matter:

- **`[build-system]` uses uv's native backend** — `requires = ["uv_build>=0.11.21,<0.12"]`,
  `build-backend = "uv_build"`. It is fast and needs no configuration for the src/ layout; the
  upper bound keeps builds reproducible as the backend evolves.
- **`requires-python` is the floor** (`>=3.13`); `.python-version` pins the interpreter used
  during development. Runtime dependencies use lower bounds (`>=`), never `==` — `uv.lock` does
  the exact pinning.
- The `[tool.ruff]`, `[tool.ty.environment]` (or `[tool.pyright]`), and `[tool.pytest.ini_options]`
  blocks ship in the same file; their rationale lives in the `python-style`, `python-typing`, and
  `python-testing` skills.

## 4. Pin developer tooling in a dev dependency group

```sh
uv add --dev ruff ty pytest hypothesis
```

This lands in `[dependency-groups] dev` (PEP 735) — **not** `[project.optional-dependencies]`.
Dependency groups are for developing the project and are never published in the wheel; optional
dependencies are runtime extras for consumers. `uv sync` installs the group; `uv run <tool>`
executes it from the project environment, so there are no global installs to drift. Swap `ty` for
`pyright` (`uv add --dev pyright`) if the project type-checks with pyright instead — see the
`python-typing` skill.

## 5. Create the Makefile gate

Copy [templates/Makefile](templates/Makefile). Its `pr` target runs the full local gate in order —
`fmt lint typecheck test build` — every tool through `uv run` so the locked versions are used. It
mirrors the CI workflow (see `python-release`) so a green `make pr` means a green CI.

## 6. Keep the entry point thin

Copy the [`__main__.py` template](templates/__main__.py). `main(argv)` returns an `int` exit code and
`raise SystemExit(main())` runs under `if __name__ == "__main__"`; the real work lives in
importable modules so tests call functions directly instead of shelling out. Register the console
script under `[project.scripts]`.

## 7. Finish

- Write the first module and its tests (`python-style`, `python-typing`, `python-testing` skills).
- Document modules and public APIs as you go (`python-docs` skill).
- Wire releases, CI, and Renovate with the `python-release` skill.
- Run `make pr` and make sure it passes before committing.