Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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"
Comment thread
DarkaMaul marked this conversation as resolved.
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`)
Comment thread
DarkaMaul marked this conversation as resolved.
Outdated

## 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
Comment thread
DarkaMaul marked this conversation as resolved.
Outdated

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 thread
ekilmer marked this conversation as resolved.
Outdated

.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