|
| 1 | +# Template Architecture |
| 2 | + |
| 3 | +This document describes the structure and design of the Copier template. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The template is built using [Copier](https://copier.readthedocs.io/), a project templating tool that uses Jinja2 for dynamic content generation. |
| 8 | + |
| 9 | +## Configuration |
| 10 | + |
| 11 | +### `copier.yaml` |
| 12 | + |
| 13 | +The main template configuration file that defines: |
| 14 | +- **Questions**: Interactive prompts for template variables |
| 15 | +- **Conditional Files**: Using `_if_` prefix for dynamic file inclusion |
| 16 | +- **Exclusions**: Files/directories not copied to generated projects via `_exclude` |
| 17 | +- **Jinja Extensions**: Custom Jinja filters and extensions |
| 18 | +- **Tasks**: Post-generation operations (migrations, setup scripts) |
| 19 | + |
| 20 | +## Template Structure |
| 21 | + |
| 22 | +``` |
| 23 | +├── src/{{project_name}}/ # Main source code directory (templated) |
| 24 | +│ ├── __init__.py.jinja # Package initialization |
| 25 | +│ └── main.py.jinja # Main module |
| 26 | +├── tests/ # Test directory structure (templated) |
| 27 | +│ ├── __init__.py.jinja |
| 28 | +│ └── unit/ |
| 29 | +│ └── test_init.py.jinja |
| 30 | +├── pyproject.toml.jinja # Project configuration (templated) |
| 31 | +├── README.md.jinja # Project README (templated) |
| 32 | +├── Makefile.jinja # Development tasks (templated) |
| 33 | +├── .copier-answers.yml.jinja # Answers tracking (templated) |
| 34 | +├── {{ _copier_conf.answers_file }}.jinja # Answers file configuration |
| 35 | +│ |
| 36 | +├── samples/ # Example data files (EXCLUDED) |
| 37 | +│ └── config-basic.yml # Sample answers file |
| 38 | +├── my_tests/ # Test suite (EXCLUDED) |
| 39 | +│ ├── conftest.py |
| 40 | +│ ├── test_core_structure.py |
| 41 | +│ ├── test_exclusions.py |
| 42 | +│ └── test_conditional_sections.py |
| 43 | +├── copier.yaml # Template configuration |
| 44 | +└── README.md # Template documentation |
| 45 | +``` |
| 46 | + |
| 47 | +## Excluded Directories |
| 48 | + |
| 49 | +Files in these directories are **NOT** copied to generated projects: |
| 50 | + |
| 51 | +- `samples/` - Example configuration and data files |
| 52 | +- `my_tests/` - Template validation tests |
| 53 | +- `.git/` - Git metadata (Copier default) |
| 54 | +- `__pycache__/` - Python cache (Copier default) |
| 55 | + |
| 56 | +Exclusion is configured in `copier.yaml` via the `_exclude` key. |
| 57 | + |
| 58 | +## Templating Conventions |
| 59 | + |
| 60 | +### Variable Interpolation |
| 61 | + |
| 62 | +Use double-brace syntax for Jinja2 variables: |
| 63 | + |
| 64 | +```jinja |
| 65 | +Project: {{ project_name }} |
| 66 | +Author: {{ author_name }} |
| 67 | +Python: {{ python_version }} |
| 68 | +``` |
| 69 | + |
| 70 | +### Conditional Sections |
| 71 | + |
| 72 | +Use `_if_` prefix to conditionally include files: |
| 73 | + |
| 74 | +``` |
| 75 | +file_name_if_condition.py.jinja |
| 76 | +``` |
| 77 | + |
| 78 | +This file is only included when the condition evaluates to `true`. |
| 79 | + |
| 80 | +### File Extensions |
| 81 | + |
| 82 | +- `.jinja` - Files that require template rendering |
| 83 | +- No extension - Static files copied as-is |
| 84 | + |
| 85 | +Example: |
| 86 | +``` |
| 87 | +README.md.jinja # Rendered with Jinja2 |
| 88 | +LICENSE # Copied without processing |
| 89 | +``` |
| 90 | + |
| 91 | +## Testing |
| 92 | + |
| 93 | +The template includes comprehensive tests in `my_tests/`: |
| 94 | + |
| 95 | +| Test | Purpose | |
| 96 | +|------|---------| |
| 97 | +| `test_core_structure.py` | Validates project structure, required files, and directories | |
| 98 | +| `test_exclusions.py` | Ensures excluded files aren't rendered in generated projects | |
| 99 | +| `test_conditional_sections.py` | Tests conditional rendering based on configuration | |
| 100 | + |
| 101 | +### Test Fixtures |
| 102 | + |
| 103 | +Defined in `my_tests/conftest.py`: |
| 104 | + |
| 105 | +- **Session-scoped fixtures**: Generate test projects once per session, reused across tests |
| 106 | +- **Module-scoped wrappers**: Provide clean API with performance optimizations |
| 107 | +- **Read-only operations**: All tests are non-mutating to enable fixture reuse |
| 108 | + |
| 109 | +## Variable Types |
| 110 | + |
| 111 | +Variables are defined in `copier.yaml` with type hints: |
| 112 | + |
| 113 | +- **String**: Simple text values |
| 114 | +- **Boolean**: Yes/No choices |
| 115 | +- **Choice**: Dropdown selection from predefined options |
| 116 | +- **Integer**: Numeric values |
| 117 | +- **Float**: Decimal values |
| 118 | + |
| 119 | +## Answer Files |
| 120 | + |
| 121 | +### `.copier-answers.yml` |
| 122 | + |
| 123 | +Auto-maintained file created in generated projects. Used for: |
| 124 | +- Tracking user responses |
| 125 | +- Enabling future template updates via `copier update` |
| 126 | +- Never manually edited |
| 127 | + |
| 128 | +### Sample Configuration |
| 129 | + |
| 130 | +`samples/config-basic.yml` provides: |
| 131 | +- Example variable values |
| 132 | +- Used with `copier copy --data-file` for non-interactive generation |
| 133 | +- Excluded from generated projects |
| 134 | + |
| 135 | +## Best Practices |
| 136 | + |
| 137 | +1. **Always use relative paths** in template files |
| 138 | +2. **Keep templates DRY**: Use Jinja2 includes for shared content |
| 139 | +3. **Test conditionals thoroughly**: Edge cases matter |
| 140 | +4. **Document variables**: Include helpful descriptions in `copier.yaml` |
| 141 | +5. **Version templates**: Use Git tags for reproducible generations |
| 142 | +6. **Commit before testing**: Copier uses Git to detect files |
| 143 | +7. **Keep exclusions updated**: Remove deprecated files from templates |
| 144 | + |
| 145 | +## Common Tasks |
| 146 | + |
| 147 | +### Add a New Template Variable |
| 148 | + |
| 149 | +1. Add question in `copier.yaml`: |
| 150 | + ```yaml |
| 151 | + _questions: |
| 152 | + my_variable: |
| 153 | + type: str |
| 154 | + help: "Description of this variable" |
| 155 | + ``` |
| 156 | +
|
| 157 | +2. Use in template files: |
| 158 | + ```jinja |
| 159 | + {{ my_variable }} |
| 160 | + ``` |
| 161 | + |
| 162 | +3. Add test case in `my_tests/` |
| 163 | + |
| 164 | +### Create a Conditional File |
| 165 | + |
| 166 | +1. Rename file with `_if_` prefix: |
| 167 | + ``` |
| 168 | + optional_file_if_include_feature.py.jinja |
| 169 | + ``` |
| 170 | + |
| 171 | +2. Define condition in `copier.yaml` |
| 172 | + |
| 173 | +3. Add test to `test_conditional_sections.py` |
| 174 | + |
| 175 | +### Update Generated Projects |
| 176 | + |
| 177 | +Users can sync with template changes: |
| 178 | +```bash |
| 179 | +copier update --answers-file .copier-answers.yml |
| 180 | +``` |
| 181 | + |
| 182 | +## References |
| 183 | + |
| 184 | +- [Copier Documentation](https://copier.readthedocs.io/en/stable/) |
| 185 | +- [Jinja2 Template Documentation](https://jinja.palletsprojects.com/) |
| 186 | +- [Python Packaging Guide](https://packaging.python.org/) |
0 commit comments