This template provides a standardized Python project structure with sensible defaults, tailored to use in Python projects. It streamlines project setup and encourages best practices from the start.
The following tools must be installed:
- Python 3.10 or newer
- uv or pipx (if you want to install Copier in an isolated environment, accessible everywhere)
- copier
-
Install Copier (if not already):
pipx install copier
or
uv tool install copier
-
Generate files in your target repository (interactive):
copier copy /path/to/copier-template /path/to/your-repo
Replace
/path/to/copier-templatewith the path to this template, and/path/to/your-repowith your target repository.Note: If you're testing with unreleased template changes, use
--vcs-ref=HEAD:copier copy --vcs-ref=HEAD /path/to/copier-template /path/to/your-repo
-
Generate non-interactively using the provided sample data file: A sample data file is provided at
examples/config-basic.yml.copier copy --data-file examples/config-basic.yml /path/to/copier-template /path/to/your-repo
You can duplicate and modify that file to create variants (e.g.,
data-with-ruff.yml).Note: This template uses conditional tasks (see Tasks Behavior), which require the
--trustflag:copier copy --trust --data-file examples/config-basic.yml /path/to/copier-template /path/to/your-repo
-
(Optional) Override specific values at the CLI: You can still override one or more answers on the fly:
copier copy --trust --data-file examples/config-basic.yml -d project_name=override_name /path/to/template /dest
-
Review the generated files:
cd /path/to/your-repo git statusThe template runs
uv syncafter bootstrapping and commits the generateduv.lockin the initial Git commit.
This template uses templated _exclude to manage optional features (LICENSE file, GitHub integration, src vs main.py, py.typed). Understanding their behavior is important for safe updates:
Exclusions are evaluated during rendering, before tasks run. This means:
- Optional files are never created when excluded
uv syncinstalls the project and generates or refreshesuv.lock- Remaining
_tasks(uv sync,git init,mise trust) run after rendering
When you generate a new project with conditions set:
- β
include_license=trueβ LICENSE file is rendered - β
include_license=falseβLICENSEis excluded, never created - β
github_integration=trueβ.github/workflow files are rendered - β
github_integration=falseβ.github/is excluded, never created
Tasks are executed again during updates. This is important:
- β
Safe with
--defaults(keeps your previous answers as-is) - β Safe if you keep boolean settings unchanged
β οΈ Dangerous if you change boolean settings when you have customizations
Recommended - Keeps all previous answers:
copier update --defaults --trust
# Your github_integration and include_license settings remain unchangedSafe if no customizations in affected directories:
copier update --trust --data github_integration=false
# OK if you haven't customized .github/ subdirectories# Initial generation WITH GitHub integration
copier copy --trust --vcs-ref=HEAD \
--data project_name=myproject \
--data github_integration=true \
/template /myproject
# Later: You customize workflows in .github/workflows/
# Then: Update but accidentally change github_integration to false
copier update --trust --data github_integration=false
# Result: π΄ excluded .github paths are removed, DELETING CUSTOM WORKFLOWS!If you need to disable GitHub integration after customizing .github/:
-
Manually back up your customizations:
cp -r .github/workflows /tmp/my-workflows-backup cp LICENSE /tmp/my-license-backup # if customized -
Run the update to disable features:
copier update --trust --data github_integration=false
-
Restore your customizations:
cp -r /tmp/my-workflows-backup /path/to/.github/workflows
-
Commit your changes:
git add .github/ git commit -m "Restore custom workflows"
| File | Purpose |
|---|---|
.copier-answers.yml |
Auto-maintained answers file for future updates (never edit manually). Now explicitly generated because the template includes templates/{{ _copier_conf.answers_file }}.jinja. |
examples/config-basic.yml |
Example data file passed with --data-file (excluded from rendered projects). |
Only templates/ is copied into generated projects (_subdirectory: templates in copier.yaml). Helper folders (examples/, my_tests/, docs/, scripts/) stay outside templates/ so consumer projects stay clean. License partials under templates/licenses/ are excluded from copy via _exclude.
To create a new variant template from existing data file configuration:
copier copy --data-file /path/to/config-basic.yml /path/to/template /destFor advanced configuration (tasks, migrations, multiple templates) see the Copier docs sections: tasks, migrations, applying multiple templates.
You can edit the template files under templates/ (*.jinja plus verbatim payload like templates/prek.toml and templates/.github/workflows/*.yml) to fit your team's standards. See Copier documentation for advanced templating and options.
This template includes comprehensive validation tests to ensure generated projects meet expected standards.
# Run all validation tests
mise run testTests use session-scoped fixtures for optimal performance:
- Session-scoped fixtures: Generated projects created once per test session, reused across all test modules
- Module-scoped wrappers: Clean test API with ~50% faster execution vs module scope alone
- All tests read-only: No mutations to generated projects ensures fixture reuse is safe
Test files are located in my_tests/ folder.
| File | Purpose |
|---|---|
test_core_structure.py |
Validates required files, directories, and project structure |
test_exclusions.py |
Ensures excluded files (copier.yaml, etc.) are not rendered |
test_conditional_sections.py |
Tests conditional rendering based on dependencies (e.g., Ruff) |
See my_tests/conftest.py for fixture definitions.
The prek.yml workflow keeps hook
versions fresh without manual effort.
| Aspect | Detail |
|---|---|
| Schedule | Monthly, 06:00 UTC on the 1st (0 6 1 * *) |
| Manual run | Actions tab β Scheduled Prek Auto-Update β Run workflow (workflow_dispatch) |
| Runner | ubuntu-latest with latest stable Python 3 (3.x floating spec, resolved on every run) |
| Permissions | contents: write so GITHUB_TOKEN can push the bump commit |
| Caching | ~/.cache/prek via one actions/cache step β hook envs are reused instead of rebuilt each run |
| Safety | timeout-minutes: 10; concurrency group serializes overlapping scheduled/manual runs |
| Commit | chore(deps): auto-update prek hooks via github-actions[bot], only when the config actually changed |
How it works:
- Checks out with
fetch-depth: 0(avoids shallow-clone push issues). - Runs
prek updateonprek.tomlβ the single config, shipped verbatim into projects generated from this Copier template. - Detects changes via
git diff --quiet(step outputchanges, not env vars); commits and pushes only when something changed β no empty commits.
Local testing is automated β see scripts/README.md.
Project-specific guides and workflows:
| Topic | Location |
|---|---|
| CI/CD Pipeline | docs/ci.md |
| Development Setup | docs/development.md |
| Template Architecture | docs/architecture.md |
For framework and tool documentation:
- Overview of Python Packaging
- Python classifiers: PyPI Classifiers
- Python Packaging: PEP 621 - pyproject.toml
- System Package Data Exchange: SPDX License List
- Writing your pyproject.toml
- Copier Framework: Copier Documentation
- Git Hooks: prek Documentation
- Ruff Formatter: Ruff Documentation
- UV Package Manager: UV Documentation