This document describes the structure and design of the Copier template.
The template is built using Copier, a project templating tool that uses Jinja2 for dynamic content generation.
The main template configuration file that defines:
- Questions: Interactive prompts for template variables
- Exclusions: Files/directories not copied to generated projects via
_exclude - Jinja Extensions: Custom Jinja filters and extensions
- Tasks: Post-generation operations (migrations, setup scripts)
├── src/{{project_name}}/ # Main source code directory (templated)
│ ├── __init__.py.jinja # Package initialization
│ └── main.py.jinja # Main module
├── tests/ # Test directory structure (templated)
│ ├── __init__.py.jinja
│ └── unit/
│ └── test_init.py.jinja
├── pyproject.toml.jinja # Project configuration (templated)
├── README.md.jinja # Project README (templated)
├── Makefile.jinja # Development tasks (templated)
├── .copier-answers.yml.jinja # Answers tracking (templated)
├── {{ _copier_conf.answers_file }}.jinja # Answers file configuration
│
├── examples/ # Example data files (EXCLUDED)
│ └── config-basic.yml # Sample answers file
├── my_tests/ # Test suite (EXCLUDED)
│ ├── conftest.py
│ ├── test_core_structure.py
│ ├── test_exclusions.py
│ └── test_conditional_sections.py
├── copier.yaml # Template configuration
└── README.md # Template documentation
Files in these directories are NOT copied to generated projects:
examples/- Example configuration and data filesmy_tests/- Template validation tests.git/- Git metadata (Copier default)__pycache__/- Python cache (Copier default)
Exclusion is configured in copier.yaml via the _exclude key.
Use double-brace syntax for Jinja2 variables:
Project: {{ project_name }}
Author: {{ author_name }}
Python: {{ python_version }}Make files and directories conditional by templating their names with Jinja expressions. For example, generate a file only when use_precommit is true:
{% if use_precommit %}.pre-commit-config.yaml{% endif %}.jinja
Important: keep the template suffix (by default .jinja) outside the Jinja expression. If the suffix is inside the condition the file won't be treated as a template and will be copied verbatim.
Concrete example:
{% if enable_feature %}optional_feature.py{% endif %}.jinja
See my_tests/test_conditional_sections.py for expected behavior and the official Copier docs ("Conditional files and directories") for more details.
.jinja- Files that require template rendering- No extension - Static files copied as-is
Example:
README.md.jinja # Rendered with Jinja2
LICENSE # Copied without processing
The template includes comprehensive tests in my_tests/:
| Test | Purpose |
|---|---|
test_core_structure.py |
Validates project structure, required files, and directories |
test_exclusions.py |
Ensures excluded files aren't rendered in generated projects |
test_conditional_sections.py |
Tests conditional rendering based on configuration |
Defined in my_tests/conftest.py:
- Session-scoped fixtures: Generate test projects once per session, reused across tests
- Module-scoped wrappers: Provide clean API with performance optimizations
- Read-only operations: All tests are non-mutating to enable fixture reuse
Variables are defined in copier.yaml with type hints:
- String: Simple text values
- Boolean: Yes/No choices
- Choice: Dropdown selection from predefined options
- Integer: Numeric values
- Float: Decimal values
Auto-maintained file created in generated projects. Used for:
- Tracking user responses
- Enabling future template updates via
copier update - Never manually edited
examples/config-basic.yml provides:
- Example variable values
- Used with
copier copy --data-filefor non-interactive generation - Excluded from generated projects
- Always use relative paths in template files
- Keep templates DRY: Use Jinja2 includes for shared content
- Test conditionals thoroughly: Edge cases matter
- Document variables: Include helpful descriptions in
copier.yaml - Version templates: Use Git tags for reproducible generations
- Commit before testing: Copier uses Git to detect files
- Keep exclusions updated: Remove deprecated files from templates
-
Add question in
copier.yaml:_questions: my_variable: type: str help: "Description of this variable"
-
Use in template files:
{{ my_variable }} -
Add test case in
my_tests/
Users can sync with template changes:
copier update --answers-file .copier-answers.yml