templatepy¶
Copier template for modern Python packages.
Stack¶
| Tool | Role |
|---|---|
| uv | Dependency management, virtual environments, running tools |
| hatchling + hatch-vcs | Build backend; version derived from git tags |
| commitizen | Conventional Commits enforcement; auto version bump on merge |
| ruff | Linting and formatting |
| mypy | Static type checking |
| pytest + pytest-cov | Testing with coverage |
| MkDocs Material | Documentation (this site) |
Requirements¶
Create a new project¶
copier copy gh:larsrollik/templatepy my-new-project
cd my-new-project
git init && git add -A && git commit -m "chore: initial commit from templatepy"
uv sync --extra dev
uv run pre-commit install --hook-type pre-commit --hook-type commit-msg
Template questions¶
| Question | Choices / default | Notes |
|---|---|---|
project_name |
free text | Display name, e.g. My Package |
project_slug |
free text | Import name, e.g. my_package |
project_description |
free text | One line; quote if it contains : |
author_name |
free text | |
author_email |
free text | |
github_username |
free text | GitHub org or personal account |
github_repo |
free text | Repository name on GitHub |
year |
free text | Copyright year |
python_requires |
3.10-3.14, default 3.13 |
Minimum supported Python version; controls requires-python and test matrix |
license_type |
noncommercial / bsd3 |
noncommercial for MSW-core packages; bsd3 for standalone hardware drivers |
private_repo_deps |
false / true, default false |
Set true only if the repo has private GitHub dependencies; injects a git auth step in CI. All standard repos use false (PyPI deps only). |
private_repo_auth |
app / pat, default app (asked only when private_repo_deps=true) |
How CI authenticates to clone the private deps. app = org-owned GitHub App, per-run token via actions/create-github-app-token@v3 using client-id (org secrets CI_APP_CLIENT_ID + CI_APP_PRIVATE_KEY, names configurable; the App needs Contents:read on the private repos). pat = the PRIVATE_REPO_ACCESS_TOKEN secret. See Private repo auth. |
test_on_windows |
false / true, default false |
Adds windows-latest to the CI test matrix (for path-sensitive or Windows-targeted packages). |
enable_pypi_publishing |
false / true, default false |
Adds the PyPI publish step (+ OIDC id-token) to release.yml. Off still makes GitHub/Forgejo releases. See PyPI publishing. |
enable_docs_publishing |
false / true, default false |
Adds the docs.yml GitHub Pages / Forgejo deploy workflow. mkdocs.yml + docs deps are always generated regardless. |
enable_llm_pr_review |
false / true, default false |
Adds the optional pr-review.yml LLM-review workflow. Off omits it (and its placeholder check). |
Apply template updates to an existing project¶
Dry-run first (safe, writes nothing)¶
If the output is Keeping template version X.Y.Z, no changes are needed and the working tree is untouched.
Apply the update¶
Copier reads .copier-answers.yml to know the template source, baseline version, and all previous answers. Commit the result.
Changing an answer without running a full update¶
Edit .copier-answers.yml directly, commit the change, then run:
Copier uses the updated answers as the new baseline and applies only the incremental diff. This is the correct way to change python_requires, license_type, or any other question after initial generation.
Installing hooks after a rename or first sync¶
If git commit complains that pre-commit is not found (stale hook path after renaming the directory), reinstall:
Day-to-day commands¶
cz commit # structured commit (enforces Conventional Commits)
uv run pytest # run tests
uv run pre-commit run --all-files # run all lint checks manually
Version bumping and releasing are handled automatically on every merge to main: versioning.yml runs cz bump, pushes the tag (as the release-bot App when configured, else github-actions[bot]), and release.yml builds the GitHub release — and publishes to PyPI if enable_pypi_publishing. Manual override:
Project structure (generated)¶
my-project/
├── src/
│ └── my_project/
│ ├── __init__.py # version via importlib.metadata
│ └── py.typed # PEP 561 typed marker
├── tests/
│ ├── conftest.py
│ ├── unit/
│ └── integration/
├── .github/
│ └── workflows/
│ ├── ci.yml # lint on push; tests on PR to main
│ ├── versioning.yml # on merge to main: cz bump + tag (App bot or GITHUB_TOKEN)
│ ├── release.yml # on tag/dispatch: build → GitHub release (+ PyPI if enabled)
│ ├── pr-review.yml # optional — only if enable_llm_pr_review
│ └── docs.yml # optional — only if enable_docs_publishing
├── scripts/
│ ├── setup_repo.sh # guided: create repo → install App → protect main
│ └── setup_branch_protection.sh # install the main-protected ruleset
├── docs/
│ ├── index.md
│ └── repository-setup.md # App, permissions, secrets, protection
├── pyproject.toml
├── CITATION.cff # citation metadata for Zenodo + GitHub
├── .pre-commit-config.yaml
└── LICENSE