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 conditional tasks to manage optional features (LICENSE file, GitHub integration). Understanding their behavior is important for safe updates:
Tasks always run at the end of both copier copy and copier update operations, after all files are generated. This means:
- Files are created/updated first
uv syncinstalls the project and generates or refreshesuv.lock- Tasks execute based on your current answers
- Any deletions from tasks are permanent for that operation
When you generate a new project with conditions set:
- β
include_license=trueβ LICENSE file is kept - β
include_license=falseβLICENSEfile is removed after generation - β
github_integration=trueβ.github/workflow files are kept - β
github_integration=falseβ.github/directory is removed after generation
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: π΄ rm -rf .github DELETES ALL 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 .copier-answers.yml.jinja. |
examples/config-basic.yml |
Example data file passed with --data-file (excluded from rendered projects). |
The template excludes helper folders (examples/, my_tests/) from generated projects via _exclude in copier.yaml. This keeps consumer projects clean.
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 (*.jinja) 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 pre-commit.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 Pre-Commit 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/pip + ~/.cache/pre-commit 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 pre-commit 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
pre-commit autoupdateon.pre-commit-config.yamlβ 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
- Pre-commit Hooks: pre-commit Framework
- Ruff Formatter: Ruff Documentation
- UV Package Manager: UV Documentation