Skip to content
copier-templatesPublic

About

A set of templates that allow to bootstrap a Python project structure with uv and other modern Python tools.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

44 Commits

Folders and files

Repository files navigation

tests python uv ruff copier mise prek License

🎯 Python Project Template

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.


πŸ“‹ Prerequisites

The following tools must be installed:

  • Python 3.12 or newer
  • uv or pipx (if you want to install Copier in an isolated environment, accessible everywhere)
  • copier

πŸš€ Usage

  1. Install Copier (if not already):

    pipx install copier

    or

    uv tool install copier
  2. Generate files in your target repository (interactive):

    copier copy /path/to/copier-template /path/to/your-repo

    Replace /path/to/copier-template with the path to this template, and /path/to/your-repo with your target repository.

    Python version: the template asks which Python version the project targets (python_version, default 3.12, choices are released CPython 3.12+ versions). The answer pins mise.toml, requires-python, and Ruff's target-version. Maintainers refresh the choices from endoflife.date with mise run update:python-versions.

    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
  3. 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-license.yml).

    Note: This template uses conditional tasks (see Tasks Behavior), which require the --trust flag:

    copier copy --trust --data-file examples/config-basic.yml /path/to/copier-template /path/to/your-repo
  4. (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
  5. Review the generated files:

    cd /path/to/your-repo
    git status

    The template runs uv sync after rendering and commits the result, including uv.lock: an Init commit in a new directory, or chore: scaffold from copier template inside an existing repository.

⚠️ Tasks Behavior with Different Copier Commands

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:

Render Process

Exclusions are evaluated during rendering, before tasks run. This means:

  • Optional files are never created when excluded
  • uv sync installs the project and generates or refreshes uv.lock
  • _tasks run after rendering: uv python install, uv sync, mise trust, then tasks/lifecycle.py for the Git commit and mise run install and mise run ci

Initial Project Generation (copier copy)

When you generate a new project with conditions set:

  • βœ… include_license=true β†’ LICENSE file is rendered
  • βœ… include_license=false β†’ LICENSE is excluded, never created
  • βœ… github_integration=true β†’ .github/ workflow files are rendered
  • βœ… github_integration=false β†’ .github/ is excluded, never created

Project Updates (copier update)

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

Safe Update Commands

Recommended - Keeps all previous answers:

copier update --defaults --trust  
# Your github_integration and include_license settings remain unchanged

Safe if no customizations in affected directories:

copier update --trust --data github_integration=false
# OK if you haven't customized .github/ subdirectories

Dangerous Update Scenario ⚠️

# 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!

Safe Alternative for Disabling Features

If you need to disable GitHub integration after customizing .github/:

  1. Manually back up your customizations:

    cp -r .github/workflows /tmp/my-workflows-backup
    cp LICENSE /tmp/my-license-backup  # if customized
  2. Run the update to disable features:

    copier update --trust --data github_integration=false
  3. Restore your customizations:

    cp -r /tmp/my-workflows-backup /path/to/.github/workflows
  4. Commit your changes:

    git add .github/
    git commit -m "Restore custom workflows"

πŸ“¦ Answers Files Explained

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).

βš™οΈ Internal Template Artifacts Not Copied

Only templates/ is copied into generated projects (_subdirectory: templates in copier.yaml). Helper folders (examples/, tests/, docs/, scripts/, tasks/) 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 /dest

For advanced configuration (tasks, multiple templates) see the Copier docs sections: tasks, migrations, applying multiple templates.

✏️ Customization

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.

πŸ§ͺ Testing the Template

This template includes comprehensive validation tests to ensure generated projects meet expected standards.

▢️ Running Tests

# Run all validation tests
mise run test

⚑ Test Performance

Tests use session-scoped fixtures for optimal performance:

  • Session-scoped registry: projects.render("<variant>") renders each project once per test session, reused across all test modules
  • Tasks only where they add coverage: content-only variants render with skip_tasks=True
  • All tests read-only: No mutations to generated projects ensures fixture reuse is safe

πŸ“ Test Files

Test files are located in tests/; docs/architecture.md describes each module. Fixtures live in tests/conftest.py, and the render variants and shared loaders in tests/_projects.py.

🧹 TOML Formatting (taplo)

All *.toml files (pyproject.toml, mise.toml, prek.toml, cliff.toml) are formatted with taplo (taplo = "0.10.0", pinned in mise.toml), enforced by the taplo-format prek hook β€” it autofixes on every commit, so there is no mise run task for it. Run it manually via mise run hooks, or check with mise x taplo -- taplo format --check <file>.

Style rule: one array item per line, always. Taplo has no force-expand switch (array_auto_collapse = false does not stick and trailing commas do not protect), so taplo.toml sets column_width = 1, which deterministically expands every array instead of collapsing the ones that fit (empty arrays stay [\n]; comments and key order are preserved β€” verified against taplo 0.10.0, see the comment in taplo.toml).

To discover available settings: mise x taplo -- taplo config schema prints the full JSON schema (formatting, include/exclude, per-path rule overrides). The Copier payload (templates/pyproject.toml.jinja, rendered mise.toml) emits taplo-clean output, so generated projects pass the hook with no extra step.

πŸ”„ Prek Auto-Update

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:

  1. Checks out with fetch-depth: 0 (avoids shallow-clone push issues).
  2. Runs prek update on prek.toml β€” the single config, shipped verbatim into projects generated from this Copier template.
  3. Detects changes via git diff --quiet (step output changes, not env vars); commits and pushes only when something changed β€” no empty commits.

πŸ§ͺ Testing it locally

Local testing is automated β€” see scripts/README.md.

πŸ“š Local Documentation

Project-specific guides and workflows:

Topic Location
CI/CD Pipeline docs/ci.md
Development Setup docs/development.md
Template Architecture docs/architecture.md

πŸ”— External References

For framework and tool documentation:

Python packaging

Tools

About

A set of templates that allow to bootstrap a Python project structure with uv and other modern Python tools.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages