Initial commit

This commit is contained in:
Jev
2025-08-11 11:36:10 +02:00
commit 43a36cc733
12 changed files with 481 additions and 0 deletions

123
README.md Normal file
View File

@@ -0,0 +1,123 @@
# Python Library Template
A [Cookiecutter](https://github.com/cookiecutter/cookiecutter) template for creating modern Python libraries with uv, ruff, mypy, and pytest.
## Features
- **Modern Python setup** with Python 3.12+ support
- **uv** for fast dependency management
- **Ruff** for linting and formatting
- **MyPy** for type checking
- **Pytest** with coverage support
- **bump-my-version** for automated version management
- **Invoke** tasks for common operations
- **Pre-configured** development workflow
- **Type hints** support with py.typed marker
- **CLAUDE.md** guidance for Claude Code integration
## Quick Start
1. Install cookiecutter:
```bash
pipx install cookiecutter
```
2. Generate a new project:
```bash
cookiecutter https://github.com/your-username/python-lib-template
```
Or locally:
```bash
cookiecutter /path/to/python-lib-template
```
3. The template will prompt for project details and automatically:
- Initialize git repository
- Set up uv environment
- Run initial linting and formatting
- Execute tests to verify setup
## Template Variables
| Variable | Description | Example |
|----------|-------------|---------|
| `project_name` | Human-readable project name | "My Python Library" |
| `project_slug` | Repository/directory name | "my-python-library" |
| `package_name` | Python package name | "my_python_library" |
| `description` | Short project description | "A modern Python library" |
| `author_name` | Author's full name | "Your Name" |
| `author_email` | Author's email | "your.email@example.com" |
| `version` | Initial version | "0.1.0" |
| `python_version` | Minimum Python version | "3.12" |
## Generated Project Structure
```
your-project/
├── CLAUDE.md # Claude Code guidance
├── README.md # Project documentation
├── pyproject.toml # Project configuration
├── tasks.py # Invoke tasks
├── src/
│ └── your_package/
│ ├── __init__.py
│ ├── core.py
│ └── py.typed
├── tests/
│ └── test_your_package.py
└── examples/
└── basic_usage.py
```
## Development Workflow
The generated project includes these common tasks:
### Setup
```bash
uv sync
```
### Code Quality
```bash
uv run ruff check --fix # Linting
uv run ruff format # Formatting
uv run mypy . # Type checking
uv run invoke lint # All quality checks
```
### Testing
```bash
uv run pytest # Run tests
uv run invoke test # Run tests with coverage
```
### Version Management
```bash
uv run bump-my-version bump patch # 0.1.0 → 0.1.1
uv run bump-my-version bump minor # 0.1.0 → 0.2.0
uv run bump-my-version bump major # 0.1.0 → 1.0.0
```
## Template Development
To modify this template:
1. Edit files in `{{cookiecutter.project_slug}}/`
2. Update variables in `cookiecutter.json`
3. Modify the post-generation hook in `hooks/post_gen_project.py`
4. Test changes:
```bash
cookiecutter . --output-dir /tmp
```
## Requirements
- Python 3.12+
- uv (automatically installed during generation)
- git (for version control)
## License
MIT License - see LICENSE file for details.

11
cookiecutter.json Normal file
View File

@@ -0,0 +1,11 @@
{
"project_name": "My Python Library",
"project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '-').replace('_', '-') }}",
"package_name": "{{ cookiecutter.project_slug.replace('-', '_') }}",
"description": "A modern Python library",
"author_name": "Your Name",
"author_email": "your.email@example.com",
"version": "0.1.0",
"python_version": "3.12",
"year": "{% now 'utc', '%Y' %}"
}

61
hooks/post_gen_project.py Executable file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""Post-generation hook for the Python library template."""
import subprocess
import sys
from pathlib import Path
def run_command(cmd: list[str], description: str) -> None:
"""Run a command and handle errors."""
print(f"Running: {description}")
try:
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
if result.stdout:
print(result.stdout)
except subprocess.CalledProcessError as e:
print(f"Error running {description}: {e}")
if e.stderr:
print(f"Error output: {e.stderr}")
sys.exit(1)
def main():
"""Initialize the generated project."""
project_dir = Path.cwd()
print(f"Setting up project in: {project_dir}")
# Initialize git repository
run_command(["git", "init"], "git init")
# Sync dependencies with uv
run_command(["uv", "sync"], "uv sync")
# Run initial formatting and linting
run_command(["uv", "run", "ruff", "format", "src"], "ruff format")
run_command(["uv", "run", "ruff", "check", "--fix", "src"], "ruff check --fix")
# Run type checking
try:
run_command(["uv", "run", "mypy", "src"], "mypy type checking")
except SystemExit:
# mypy might fail on initial template, continue anyway
print("MyPy check failed - this is normal for initial template")
# Run tests to ensure everything works
try:
run_command(["uv", "run", "pytest"], "pytest")
except SystemExit:
print("Tests failed - you may need to adjust the generated code")
print("\n✅ Project setup complete!")
print("Next steps:")
print("1. Review and customize the generated files")
print("2. Add your actual dependencies to pyproject.toml")
print("3. Implement your library functionality in src/")
print("4. Update tests as needed")
print("5. Update README.md with proper documentation")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,69 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
`{{ cookiecutter.project_slug }}` is a Python library for {{ cookiecutter.description.lower() }}.
## Development Commands
### Setup
```bash
uv sync
```
### Code Quality
- **Linting and formatting**: `uv run ruff check --fix` and `uv run ruff format`
- **Type checking**: `uv run mypy .`
- **Combined linting**: `uv run invoke lint` (runs ruff check, ruff format --check, and mypy)
### Testing
- **Run tests**: `uv run pytest`
- **Run tests with coverage**: `uv run invoke test`
### Maintenance
- **Clean untracked files**: `uv run invoke clean` (interactive)
- **Version bumping**: `uv run bump-my-version bump [patch|minor|major]`
## Code Architecture
### Core Components (`src/{{ cookiecutter.package_name }}/core.py`)
The library implements the main functionality in the core module. Update this section with:
1. **Key Classes**: Describe main classes and their responsibilities
2. **Core Functions**: Document important functions and their purpose
3. **Data Flow**: Explain how data flows through the system
4. **Dependencies**: List and explain key dependencies
### Project Structure
```
src/{{ cookiecutter.package_name }}/
├── __init__.py # Package initialization
├── core.py # Main functionality
└── py.typed # Type hints marker
examples/
└── basic_usage.py # Usage examples
tests/
└── test_{{ cookiecutter.package_name }}.py # Test suite
```
## Development Notes
- Uses Python {{ cookiecutter.python_version }}+ with modern type hints (PEP 604)
- Configured with ruff for linting/formatting and mypy for type checking
- Built with uv for dependency management
- Includes invoke tasks for common operations
- Version {{ cookiecutter.version }} ({{ cookiecutter.year }})
## Implementation Guidelines
- Follow test-driven development practices
- Use descriptive function names and one-liner docstrings for non-trivial functions
- Keep files between 300500 lines where possible
- Don't duplicate code; build upon existing implementations
- Always use type hints as supported by Python {{ cookiecutter.python_version }}+

View File

@@ -0,0 +1,55 @@
# {{ cookiecutter.project_name }}
{{ cookiecutter.description }}
## Installation
```bash
pip install {{ cookiecutter.project_slug }}
```
## Development
This project uses `uv` for dependency management and the following tools:
### Setup
```bash
uv sync
```
### Code Quality
- **Ruff**: Linting and formatting
```bash
uv run ruff check --fix
uv run ruff format
```
- **MyPy**: Type checking
```bash
uv run mypy .
```
### Testing
```bash
uv run pytest
```
### Version Management
- **bump-my-version**: Automated version bumping with git tags
```bash
uv run bump-my-version bump patch # {{ cookiecutter.version }} → 0.1.1
uv run bump-my-version bump minor # {{ cookiecutter.version }} → 0.2.0
uv run bump-my-version bump major # {{ cookiecutter.version }} → 1.0.0
```
## Usage
```python
from {{ cookiecutter.package_name }} import hello
print(hello())
```
## License
MIT License - see LICENSE file for details.

View File

@@ -0,0 +1,22 @@
"""Basic usage example for {{ cookiecutter.project_name }}."""
from {{ cookiecutter.package_name }} import hello
from {{ cookiecutter.package_name }}.core import process_data, {{ cookiecutter.project_name.replace(' ', '').replace('-', '') }}
def main():
"""Demonstrate basic usage."""
# Basic hello
print(hello())
# Process some data
result = process_data("example data")
print(result)
# Use main class
instance = {{ cookiecutter.project_name.replace(' ', '').replace('-', '') }}("example")
print(instance.run())
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,51 @@
[project]
name = "{{ cookiecutter.project_slug }}"
version = "{{ cookiecutter.version }}"
description = "{{ cookiecutter.description }}"
readme = "README.md"
authors = [
{ name = "{{ cookiecutter.author_name }}", email = "{{ cookiecutter.author_email }}" }
]
requires-python = ">={{ cookiecutter.python_version }}"
dependencies = [
# Add your project dependencies here
]
[build-system]
requires = ["uv_build>=0.8.8,<0.9.0"]
build-backend = "uv_build"
[dependency-groups]
dev = [
"bump-my-version>=1.2.1",
"invoke>=2.2.0",
"mypy>=1.17.1",
"pytest>=8.4.1",
"pytest-cov>=6.1.0",
"ruff>=0.12.8",
]
[tool.bumpversion]
current_version = "{{ cookiecutter.version }}"
commit = true
tag = true
tag_name = "v{new_version}"
[[tool.bumpversion.files]]
filename = "pyproject.toml"
search = "version = \"{current_version}\""
replace = "version = \"{new_version}\""
#------------------ruff configuration----------------
[tool.ruff.lint]
extend-select = ["B", "I", "C4", "TID", "SIM", "PLE", "RUF"]
ignore = [
"D100", "D101", "D102", "D103", # Missing docstrings
"N806", "N803", # Invalid name patterns
"G201", # Logging f-string interpolation
"ARG001", # Unused function argument
"BLE001", # Blind except
]
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["ARG001"] # Allow unused arguments in tests

View File

@@ -0,0 +1,8 @@
"""{{ cookiecutter.project_name }} - {{ cookiecutter.description }}"""
__version__ = "{{ cookiecutter.version }}"
def hello() -> str:
"""Return a hello message."""
return "Hello from {{ cookiecutter.project_name }}!"

View File

@@ -0,0 +1,17 @@
"""Core functionality for {{ cookiecutter.project_name }}."""
def process_data(data: str) -> str:
"""Process input data and return result."""
return f"Processed: {data}"
class {{ cookiecutter.project_name.replace(' ', '').replace('-', '') }}:
"""Main class for {{ cookiecutter.project_name }}."""
def __init__(self, name: str = "default") -> None:
self.name = name
def run(self) -> str:
"""Run the main functionality."""
return f"Running {{ cookiecutter.project_name }} with {self.name}"

View File

@@ -0,0 +1,39 @@
# type: ignore
from invoke import task
@task
def clean(ctx):
"""
Remove all files and directories that are not under version control to ensure a pristine working environment.
Use caution as this operation cannot be undone and might remove untracked files.
"""
ctx.run("git clean -nfdx")
response = (
input("Are you sure you want to remove all untracked files? (y/n) [n]: ")
.strip()
.lower()
)
if response == "y":
ctx.run("git clean -fdx")
@task
def lint(ctx):
"""
Perform static analysis on the source code to check for syntax errors and enforce style consistency.
"""
ctx.run("ruff check src", pty=True)
ctx.run("ruff format --check src", pty=True)
ctx.run("mypy src", pty=True)
@task
def test(ctx):
"""
Run tests with coverage information.
"""
ctx.run("pytest --cov=src --cov-report=term-missing", pty=True)

View File

@@ -0,0 +1,25 @@
"""Tests for {{ cookiecutter.package_name }}."""
import pytest
from {{ cookiecutter.package_name }} import hello
from {{ cookiecutter.package_name }}.core import process_data, {{ cookiecutter.project_name.replace(' ', '').replace('-', '') }}
def test_hello():
"""Test hello function."""
result = hello()
assert "Hello from {{ cookiecutter.project_name }}" in result
def test_process_data():
"""Test process_data function."""
result = process_data("test")
assert result == "Processed: test"
def test_main_class():
"""Test main class."""
instance = {{ cookiecutter.project_name.replace(' ', '').replace('-', '') }}("test")
result = instance.run()
assert "Running {{ cookiecutter.project_name }} with test" in result