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
12 changes: 4 additions & 8 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
version: 2
updates:
# Keep Poetry dependencies up to date
- package-ecosystem: "pip"
# Keep uv dependencies up to date
- package-ecosystem: "uv"
directory: "/" # where pyproject.toml lives
schedule:
interval: "daily"
versioning-strategy: "increase" # allow minor/patch bumps
groups:
poetry-dependencies:
patterns:
- "*"
commit-message:
prefix: "chore(poetry)"
prefix: "chore(uv)"
labels:
- "dependencies"
- "poetry"
- "uv"

# Keep GitHub Actions up to date
- package-ecosystem: "github-actions"
Expand Down
70 changes: 35 additions & 35 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
# Set uv version
UV_VERSION: "0.9.4"

jobs:
pre-commit:
name: Pre-commit hooks (lint/format/spell/type, all files)
Expand All @@ -19,17 +23,16 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
python-version-file: ".python-version"

- name: Install Poetry
uses: snok/install-poetry@v1
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: 2.2.0
version: ${{ env.UV_VERSION }}
enable-cache: true

# Install dev deps so local hooks (e.g., pyright/pytest via poetry) can run
- name: Install dev dependencies
run: poetry install --no-interaction --with dev,docs
- name: Install dependencies
run: uv sync --all-groups

# Run the same hooks as locally; include push stage so pre-push hooks run too
- name: Run pre-commit (all files, commit & push stages)
Expand All @@ -54,24 +57,18 @@ jobs:
- name: Checkout
uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v6
- name: Install uv and set python version
uses: astral-sh/setup-uv@v6
with:
version: ${{ env.UV_VERSION }}
python-version: ${{ matrix.python }}
cache: pip
enable-cache: true

- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: 2.2.0
virtualenvs-create: true
virtualenvs-in-project: true

- name: Install dev dependencies
run: poetry install --no-interaction --with dev
- name: Install dependencies
run: uv sync --all-groups

- name: Run tests (full suite)
run: poetry run pytest --cov --cov-branch --cov-report=xml
run: uv run pytest --cov --cov-branch --cov-report=xml

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
Expand All @@ -87,19 +84,19 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
python-version-file: ".python-version"

- name: Install Poetry
uses: snok/install-poetry@v1
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: 2.2.0
version: ${{ env.UV_VERSION }}
enable-cache: true

- name: Install docs dependencies
run: poetry install --no-interaction --with docs
- name: Install dependencies
run: uv sync --group docs

- name: Build HTML (fail on warnings)
run: poetry run mkdocs build --strict
run: uv run mkdocs build --strict

- name: Upload built docs artifact
uses: actions/upload-artifact@v4
Expand All @@ -118,17 +115,20 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
python-version-file: ".python-version"

- name: Install Poetry
uses: snok/install-poetry@v1
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: 2.2.0
version: ${{ env.UV_VERSION }}
enable-cache: true

- name: Install dependencies
run: uv sync --all-groups

- name: Build and install wheel
run: |
poetry build
uv build
python -m venv pkgtest
source pkgtest/bin/activate
python -m pip install --upgrade pip
Expand Down
11 changes: 6 additions & 5 deletions .github/workflows/docs-pages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ jobs:
with:
python-version: "3.12"

- name: Install Poetry
uses: snok/install-poetry@v1
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: 2.2.0
# Install a specific version of uv.
version: "0.9.4"

- name: Install docs deps
run: poetry install --no-interaction --with docs
run: uv sync --group docs

- name: Build & deploy with MkDocs
# --force overwrites existing site; --clean removes stale files
run: poetry run mkdocs gh-deploy --force --clean
run: uv run --group docs mkdocs gh-deploy --force --clean
19 changes: 9 additions & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ repos:
rev: v2.3.0
hooks:
- id: codespell
args: ["--skip", "poetry.lock,dist,build,.venv,*.min.js"]
args: ["--skip", "uv.lock,dist,build,.venv,*.min.js"]
exclude: |
(?x)(
^docs/_build/|
Expand All @@ -52,7 +52,7 @@ repos:
hooks:
- id: pyright
name: pyright (type check)
entry: poetry run pyright
entry: uv run pyright
language: system
pass_filenames: false
stages: [pre-push]
Expand All @@ -62,7 +62,7 @@ repos:
hooks:
- id: mkdocs-build
name: mkdocs build (check docs)
entry: poetry run mkdocs build --config-file mkdocs.yml --strict
entry: uv run --group docs mkdocs build --config-file mkdocs.yml --strict
language: system
pass_filenames: false
stages: [pre-push]
Expand All @@ -73,17 +73,16 @@ repos:
hooks:
- id: pytest
name: pytest (quick)
entry: poetry run pytest -q -m "not slow" --doctest-modules --doctest-glob="*.py" --maxfail=1 --disable-warnings
entry: uv run pytest -q -m "not slow" --doctest-modules --doctest-glob="*.py" --maxfail=1 --disable-warnings
language: system
types: [python]
pass_filenames: false
stages: [pre-push]


# --- Poetry: manage dependencies and virtualenv ---
- repo: https://github.com/python-poetry/poetry
rev: 2.2.1
# --- uv: check environment is in sync ---
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
rev: 0.9.4
hooks:
- id: poetry-check
- id: poetry-lock
- id: poetry-install
- id: uv-lock
52 changes: 17 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
[![Docs (GitHub Pages)](https://github.com/gemmadanks/python-project-template/actions/workflows/docs-pages.yaml/badge.svg)](https://github.com/gemmadanks/python-project-template/actions/workflows/docs-pages.yaml)
[![Docs (RTD)](https://app.readthedocs.org/projects/python-project-template/badge/?version=latest)](https://gemmadanks-python-project-template.readthedocs.io/en/latest/)
[![Dependabot](https://img.shields.io/github/issues-search?query=repo%3Agemmadanks%2Fpython-project-template%20is%3Apr%20author%3Aapp%2Fdependabot%20is%3Aopen&label=Dependabot%20PRs)](https://github.com/gemmadanks/python-project-template/issues?q=is%3Apr%20is%3Aopen%20author%3Aapp%2Fdependabot)
[![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white)](https://conventionalcommits.org)
[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)

A comprehensive, opinionated template for modern Python projects -- featuring Poetry packaging, Ruff for linting and formatting, justfile, pytest testing with code coverage upload to codecov, MkDocs documentation with configuration for Read The Docs, pre-commit hooks, GitHub Actions CI, GitHub issue and pull request templates, architectural decision record (ADR) templates and automated semantic releases.
A comprehensive, opinionated template for modern Python projects -- featuring uv packaging, Ruff for linting and formatting, justfile, pytest testing with code coverage upload to codecov, MkDocs documentation with configuration for Read The Docs, pre-commit hooks, GitHub Actions CI, GitHub issue and pull request templates, architectural decision record (ADR) templates and automated semantic releases.

The goal is to help you start writing code immediately without having to spend time deciding what tools or conventions to use.

Expand All @@ -37,7 +37,7 @@ The goal is to help you start writing code immediately without having to spend t

- Python project directory structure
- README template with badges
- Packaging and dependency management via [Poetry](https://python-poetry.org/): [pyproject.toml](pyproject.toml)
- Packaging and dependency management via [uv](https://docs.astral.sh/uv/): [pyproject.toml](pyproject.toml)
- Linting and formatting via [Ruff](https://docs.astral.sh/ruff/): [.pre-commit-config.yaml](.pre-commit-config.yaml)
- Testing framework using [pytest](https://docs.pytest.org/en/stable/)
- CI using [GitHub Actions](https://docs.github.com/en/actions): [.github/workflows/ci.yaml](.github/workflows/ci.yaml)
Expand All @@ -57,11 +57,18 @@ The goal is to help you start writing code immediately without having to spend t

## 📦 Installation

1. [Install uv](https://docs.astral.sh/uv/getting-started/installation/)
2. Clone and install the project using uv:
```bash
git clone https://github.com/gemmadanks/python-project-template
cd python-project-template
poetry install --with dev,docs
uv sync --all-groups
```
3. Install pre-commit hooks (only needs to be done once)
```bash
just pre-commit-install
```
Hook definitions: [.pre-commit-config.yaml](.pre-commit-config.yaml)

## 🏁 Quickstart

Expand All @@ -76,31 +83,17 @@ Several common tasks have been added as recipes to a [justfile](justfile) in the
[Installing just](https://just.systems/man/en/packages.html) allows you to run the following:

```bash
just install # poetry install
just test # full test suite
just install # uv sync
just test # run quick (non-slow) tests
just lint # ruff check
just format # ruff format
just type-check # pyright type-check
just docs-serve # live docs
just docs-build # build docs
just pre-commit # run all pre-commit hooks
just clean # remove generated files and folders
```

## 🪝 Pre-commit / Pre-push

Install hooks once:

```bash
just pre-commit-install # installs default (commit-stage) hooks
```

Run on all files (CI equivalent):

```bash
just pre-commit
```

Hook definitions: [.pre-commit-config.yaml](.pre-commit-config.yaml)

## 📚 Documentation

- Configuration: [mkdocs.yml](mkdocs.yml)
Expand Down Expand Up @@ -154,8 +147,8 @@ Managed by release-please: ([conventional commits](https://www.conventionalcommi
| ├── pull_request_template.md # Pull request template
│ └── dependabot.yml # Dependency update automation
├── .pre-commit-config.yaml # Pre-commit hook definitions
├── pyproject.toml # Project metadata + dependencies (Poetry)
├── poetry.lock # Locked dependency versions (Poetry)
├── pyproject.toml # Project metadata + dependencies (uv)
├── uv.lock # Locked dependency versions (uv)
├── README.md # Project overview (you are here)
├── mkdocs.yml # MkDocs configuration
├── CITATION.cff # Citation metadata
Expand Down Expand Up @@ -187,15 +180,4 @@ If used in research, cite via [CITATION.cff](CITATION.cff).

BSD-3-Clause – see [LICENSE](LICENSE).

## ✅ Health Checklist

| Area | Command |
|-----------------|----------------------------------------|
| Lint | `poetry run ruff check` |
| Format | `poetry run ruff format` |
| Types (pre-push)| `poetry run pyright` (via hook) |
| Tests | `poetry run pytest` |
| Docs | `poetry run mkdocs build --strict` |


Happy coding! 🚀
57 changes: 57 additions & 0 deletions docs/architecture/adr/002-manage-dependencies-with-uv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# ADR-002: Manage dependencies with uv
| | |
| ---| ---|
| **Status** | 🟢 Accepted |
| **Created** | 2025-10-18 |
| **Last Updated** | 2025-10-18 |
| **Deciders** | Gemma Danks |
| **Tags** | packaging |

---

## Context

Every software project needs a way to manage dependencies. This allows reproducible, consistent installs across operating systems and machines. The Python ecosystem has several options that have evolved over time. It is important to choose a dependency manager that is fast, easy to use in CI, well supported by the community, uses metadata in the pyproject.toml file (i.e. [PEP 621 compliant](https://peps.python.org/pep-0621/)) and provides a good developer experience.

## Problem Statement

What dependency manager is best for our project?

## Options Considered

| Option | Description | Developer Experience | Speed | Reproducibility | Adoption | CI | PEP 621 | Overall score | Notes |
|----------|-------------|-------------|-----------------|-------------| ----- | ------|------| ------|------|
| **Weight** | - | 2 | 2 | 2 | 1| 1 | 1 | - | - |
| **uv** | New, fast replacement for multiple tools, built in Rust by creators of ruff. | ✅ | ✅ | ✅ | ⚠️ | ✅ | ✅ | 26 | Very fast. Probably the future standard. Also manages python versions. |
| **Poetry** | Well established packaging manager with wide adoption. | ✅ | ⚠️ | ✅ | ✅ | ✅ | ✅ | 25 | Mature, widely used but slower. |
| **PDM** | Light-weight, standards-compliant, written in Python. | ✅ | ⚠️ | ✅ | ⚠️ | ✅ | ✅ | 24 | Good option, not as fast or popular as uv. |
| **mamba** | Reimplementation of conda in C++. | ⚠️ | ✅ | ✅ | ✅ | ✅ | ❌ | 23 | Fast but not PEP 621 compliant |
| **conda** | Binary package manager, widely used for scientific software. | ⚠️ | ⚠️ | ✅ | ✅ | ✅ | ❌ | 21 | Not as fast and not PEP 621 compliant |
| **pipenv** | Simplified packaging management tool. | ⚠️ | ⚠️ | ✅ | ⚠️ | ✅ | ❌ | 20 | Not PEP 621 compliant. |
| **pip + venv** | Standard library tools. | ✅ | ⚠️ | ❌ | ✅ | ✅ | ⚠️ | 20 | Not suitable for complex environments |
|
| **spack** | HPC-oriented packaging manager. Supports full stack. | ❌ | ⚠️ | ✅ | ⚠️ | ⚠️ | ❌ | 17 | Best for multi-language environments on HPC clusters. |

✅ = 3 (good), ⚠️ = 2 (acceptable), ❌ = 1 (poor)

## Decision Outcome

We will use uv since it is extremely fast and likely to become the new standard. It provides a good developer experience and replaces multiple tools. Performance is particularly important for CI. Poetry and PDM are good alternatives. Poetry is more mature and widely adopted.

## Consequences

Using uv will simplify Python and dependency management. It is extremely fast and so will speed up continuous integration, reducing waiting time substantially where installing dependencies is the bottleneck. It also manages Python versions and is PEP 621 compliant. This tool is likely to become the new standard.

A risk is that this is under active development and is not yet widely adopted. Alternatives to fall back on include Poetry or PDM. This ADR should be revisited in one year since development in this area is ongoing and adoption of particular tools is in a state of flux.

## Confirmation

The project README will document the usage of uv. CI workflows will use uv and the uv.lock file will be placed under version control.

## Links

| Type | Links |
| -----| ------|
| **ADRs** | |
| **Issues** | |
| **PRs** | |
1 change: 1 addition & 0 deletions docs/architecture/adr/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
| ADR | Status | Summary |
|-----|---------|----------|
| [ADR-001: Use Architecture Decision Records](001-use-architectural-decision-records.md) | 🟢 Accepted | Use ADRs to explain the rationale behind architecturally significant design choices for future developers and AI assistants |
| [ADR-002: Manage dependencies with uv](002-manage-dependencies-with-uv.md) | 🟢 Accepted | Use uv to manage project dependencies |
Loading