Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion hooks/pre_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
namespace_import = "{{ cookiecutter.project_namespace_import }}"
if namespace_import and not re.match(NAMESPACE_REGEX, namespace_import):
print(f"ERROR: '{namespace_import}' is not a valid Python namespace import path!")
print(f" It must follow regex '{NAMESPACE_REGEX}', i.e. 'one_two' or 'one_two.three'")
print(
f" It must follow regex '{NAMESPACE_REGEX}', i.e. 'one_two' or 'one_two.three'"
)
sys.exit(1)


Expand Down
2 changes: 0 additions & 2 deletions {{cookiecutter.project_slug}}/.github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ jobs:
strategy:
matrix:
python:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
runs-on: ubuntu-latest
Expand Down
45 changes: 45 additions & 0 deletions {{cookiecutter.project_slug}}/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Pre-commit hooks for code quality
# See https://pre-commit.com for more information
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.2
hooks:
# Run the formatter
- id: ruff-format
# Run the linter
- id: ruff
args: [--fix]

- repo: local
hooks:
- id: pyright
name: pyright type check
entry: uv run pyright
language: system
types: [python]
pass_filenames: false
require_serial: true

{%- if cookiecutter.docstring_coverage %}
- id: interrogate
name: interrogate docstring coverage
entry: uv run interrogate -c pyproject.toml
language: system
types: [python]
pass_filenames: false
{%- endif %}

- id: pytest
name: pytest (fast tests only)
entry: uv run pytest -x --tb=short -k "not slow"
language: system
types: [python]
pass_filenames: false
require_serial: true
# Only run on pre-commit, not on push
stages: [pre-commit]

# Configuration
ci:
autofix_prs: true
autoupdate_schedule: weekly
167 changes: 167 additions & 0 deletions {{cookiecutter.project_slug}}/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# {{ cookiecutter.project_name }} - Claude Instructions

This document contains project-specific instructions for Claude when working on this codebase.

## Project Overview

{{ cookiecutter.project_description }}

## Code Standards

This project enforces strict code quality standards:

### Code Complexity Limits
- **Max 50 lines per function** - Split larger functions
- **Cyclomatic complexity ≤ 8** - Simplify complex logic
- **Max 5 positional parameters** - Use keyword arguments or dataclasses
- **Max 12 branches per function** - Extract to helper functions
- **Max 6 return statements** - Consolidate exit points

### Style Guidelines
- **Line length**: 100 characters max
- **Docstrings**: Google style on all public functions/classes
- **Type hints**: Required for all function signatures
- **Tests**: Must live beside code (`test_*.py` or `*_test.py`)

## Quick Commands

```bash
# Development setup
make dev

# Run all checks
make check # Runs lint + tests

# Code quality
make lint # ruff format --check + ruff check + pyright
make fix # Auto-fix formatting and lint issues
make typecheck # Run pyright type checker

# Testing
make test # Run pytest with coverage

# Development
{% if cookiecutter.entry_point -%}
make run ARGS="--help" # Run the CLI
{%- endif %}
make doc # Build documentation
```
## Project Structure
```
src/
└── {{ cookiecutter.__project_import.replace('.', '/') }}/
├── __init__.py
{%- if cookiecutter.entry_point %}
├── __main__.py # CLI entry point
├── _cli.py # CLI implementation
{%- endif %}
└── py.typed # Type checking marker

test/
└── test_*.py # Traditional test location
```
Tests can also live beside source files as `test_*.py` or `*_test.py`.
## General Python Guidelines
These are general preferences for Python development:
- **Web frameworks**: Prefer FastAPI over Flask for new projects
- **Data processing**: Consider Polars for performance-critical data operations
- **Async programming**: Use native async/await instead of threading
- **Type checking**: Always use type hints and run pyright
## Common Patterns
### Error Handling
```python
from typing import Result # If using result types

def process_data(path: str) -> Result[Data, str]:
"""Process data from file.
Args:
path: Path to data file.
Returns:
Result with Data on success, error message on failure.
"""
try:
# Implementation
return Ok(data)
except Exception as e:
return Err(f"Failed to process: {e}")
```
### Logging
```python
import logging

logger = logging.getLogger(__name__)
```
{%- if cookiecutter.entry_point %}
### CLI Arguments
Use the existing `_cli.py` structure:
```python
parser.add_argument(
"--verbose", "-v",
action="store_true",
help="Enable verbose output"
)
```
{%- endif %}
## Testing Guidelines
- Aim for 100% test coverage (enforced by CI)
- Use `pytest.mark.parametrize` for multiple test cases
- Mock external dependencies
- Test both success and error paths
### Test Markers
Mark slow tests to exclude them from pre-commit hooks:
```python
import pytest
@pytest.mark.slow
def test_integration_with_external_api():
"""This test won't run during pre-commit hooks."""
...
def test_fast_unit_test():
"""This test will run during pre-commit hooks."""
...
```
The pre-commit hook runs `pytest -k "not slow"` to skip slow tests.
## CI/CD
GitHub Actions run on every push/PR:
1. **Linting**: ruff format/check + pyright type checking
2. **Tests**: pytest with coverage
3. **Security**: zizmor workflow scanning
{%- if cookiecutter.documentation == "pdoc" %}
4. **Docs**: Auto-deploy to GitHub Pages
{%- endif %}
## Important Notes
1. **Never commit code that violates the quality standards** - refactor instead
2. **All public APIs need Google-style docstrings**
3. **Type hints are mandatory** - use `pyright`
4. **Tests can live beside code** - prefer colocated tests for better maintainability
## Project-Specific Instructions
<!-- Add any project-specific Claude instructions here -->
---
*This file helps Claude understand project conventions. Update it as patterns emerge.*
18 changes: 15 additions & 3 deletions {{cookiecutter.project_slug}}/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ all:

.PHONY: dev
dev: $(VENV)/pyvenv.cfg
uv run pre-commit install

{%- if cookiecutter.entry_point %}
.PHONY: run
Expand All @@ -59,17 +60,28 @@ $(VENV)/pyvenv.cfg: pyproject.toml
lint: $(VENV)/pyvenv.cfg
uv run ruff format --check && \
uv run ruff check && \
uv run mypy
uv run pyright

{%- if cookiecutter.docstring_coverage %}
uv run interrogate -c pyproject.toml .
{%- endif %}

.PHONY: reformat
reformat:
.PHONY: check
check: lint test

.PHONY: typecheck
typecheck: $(VENV)/pyvenv.cfg
uv run pyright
Comment on lines +69 to +71
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? Running pyright is included in the lint target


.PHONY: fix
fix:
uv run ruff format && \
uv run ruff check --fix

# Alias for backwards compatibility
.PHONY: reformat
reformat: fix

.PHONY: test tests
test tests: $(VENV)/pyvenv.cfg
uv run pytest --cov=$(PY_IMPORT) $(T) $(TEST_ARGS)
Expand Down
Loading