A Copier template for Python projects following modern best practices with Python 3.13+.
- Modern Python: Python 3.13+ with full type hints and strict type checking
- Fast Package Manager: Uses uv for dependency management
- Template Updates: Supports updating existing projects with
copier update - Custom Development CLI: Plugin-based
./devscript for common tasks - Comprehensive Testing: pytest with custom fixtures and coverage support
- Code Quality: Integrated ruff lint, ruff format, basedpyright, prek hooks
- Environment-Based Configuration: python-dotenv for environment variables
- Configurable Logging: Supports both human-readable and JSON structured logging
- License Options: Apache 2.0 or Proprietary
- uv - Fast Python package manager (used in generated projects)
- Copier - Template engine for project generation
# Using uv (recommended)
uv tool install --with jinja2-time copier# From GitHub
copier copy --trust gh:fernandoacorreia/python-project-template your-project-name
# From local directory
copier copy --trust /path/to/python-project-template your-project-nameYou will be prompted for:
- project_name: Human-readable project name (e.g., "My Project")
- project_slug: URL/package-friendly name with hyphens (auto-generated, e.g., "my-project")
- package_name: Python module name with underscores (auto-generated, e.g., "my_project")
- project_description: One-line description of your project (e.g., "A Python project following best practices")
- author_name: Your full name (e.g., "Your Name")
- author_email: Your email address (e.g., "[email protected]")
- python_version: Python version (e.g., "3.13")
- license: License type (Apache-2.0 or Proprietary)
To run Copier without prompts in scripts or automated workflows, use the --data flag to pass variables:
# Using --data flags
copier copy --trust --defaults \
--data project_name="My Project" \
--data project_slug="my-project" \
--data package_name="my_project" \
--data project_description="A Python project" \
--data author_name="John Doe" \
--data author_email="[email protected]" \
--data python_version="3.13" \
--data license="Apache-2.0" \
/path/to/python-project-template your-project-nameAlternatively, use a data file:
# Create answers.yml
cat > answers.yml <<EOF
project_name: My Project
project_slug: my-project
package_name: my_project
project_description: A Python project
author_name: John Doe
author_email: [email protected]
python_version: "3.13"
license: Apache-2.0
EOF
# Use --data-file
copier copy --trust --defaults --data-file answers.yml \
/path/to/python-project-template your-project-nameNote: The --defaults flag is required when running non-interactively to use default values for any variables not explicitly provided.
The template includes post-generation tasks that automatically:
- Initializes a Git repository
- Creates CLAUDE.md symlink to AGENTS.md
- Initializes virtual environment and installs dependencies (via
uv sync) - Initializes development tools (via
./dev init) - Creates an initial commit
After generation, activate your environment and start coding:
cd your-project-name
source .venv/bin/activate
./dev --helpTo update a project generated from this template:
cd your-project-name
copier updateCopier will prompt you for any new questions and update your project while preserving your customizations.
Your project's .copier-answers.yml file tracks:
- Template source (
_src_path) - Template version (
_commit) - Your answers to template questions
Keep this file in version control to enable future updates.
your-project/
├── src/
│ └── your_project/ # Main package (underscores, not hyphens)
│ ├── main.py # Application entry point
│ └── py.typed # PEP 561 type marker
├── tests/ # Test suite
│ ├── conftest.py # pytest fixtures
│ ├── main_test.py # Example tests
│ └── data/ # Test data files
├── scripts/ # Development scripts
│ ├── dev.py # Main CLI dispatcher
│ └── commands/ # Command modules
├── .copier-answers.yml # Copier template metadata
├── .env # Environment variable definitions
├── .gitignore # Git ignore rules
├── .pre-commit-config.yaml # Prek hooks configuration (pre-commit compatible)
├── .yamllint.yaml # YAML linting configuration
├── AGENTS.md # Claude Code instructions
├── CLAUDE.md -> AGENTS.md # Symlink for Claude Code
├── dev # Development CLI entry point (executable)
├── example.env # Environment variable template
├── LICENSE # Project license
├── pyproject.toml # Project metadata and dependencies
├── README.md # Project documentation
└── uv.lock # Dependency lock file
The generated project includes a ./dev script:
./dev --help # Show all available commandsTo customize this template for your needs:
- Edit
copier.ymlto add/modify variables - Update template files in
template/ - Add
.jinjasuffix to files that contain template variables - Modify
_tasksincopier.ymlfor custom post-generation tasks - Use Jinja2 syntax for dynamic content:
{{ variable_name }}
- Add to
copier.yml:
your_new_variable:
type: str
help: Description of your variable
default: default_value- Use in template files (file must have
.jinjasuffix):
# In any template file with .jinja suffix
MY_CONFIG = "{{ your_new_variable }}"- Plugin-based architecture for easy extensibility
- Each command is a separate module in
scripts/commands/ - Unknown arguments are forwarded to underlying tools (pytest, claude)
- Custom pytest fixtures:
test_data_dirandtemp_dir - Automatic cleanup of temporary directories
- Example tests demonstrating best practices
- Strict basedpyright checking for production code (
src/) - Relaxed checking for tests (
tests/) - PEP 561 compliant with
py.typedmarker
- Environment-based configuration via
.env - Two formats: pretty (human-readable) and JSON (structured)
- Custom
JSONFormatterfor production logging
This template itself is licensed under the Apache License 2.0. Projects generated from this template will use the license you select during project generation (Apache 2.0 or Proprietary).