From python-toolkit
Set up Python project with pyproject.toml, ruff, mypy, pre-commit, and uv
How this skill is triggered — by the user, by Claude, or both
Slash command
/python-toolkit:python-project-setupThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are helping the user set up a Python project with modern tooling and Infiquetra standards.
You are helping the user set up a Python project with modern tooling and Infiquetra standards.
For a new Infiquetra Python project, create this structure:
project-name/
├── src/
│ └── service/
│ ├── __init__.py
│ └── handler.py
├── tests/
│ ├── __init__.py
│ ├── conftest.py
│ └── unit/
│ └── __init__.py
├── pyproject.toml
├── .pre-commit-config.yaml
├── .gitignore
├── README.md
└── .python-version (optional)
Start with the minimal template from references/pyproject-template.md:
[project]
name = "your-service-name"
version = "0.1.0"
description = "Your service description"
requires-python = ">=3.12"
dependencies = [
"boto3>=1.34.0",
"aws-lambda-powertools>=2.30.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0.0",
"pytest-cov>=4.1.0",
"ruff>=0.2.0",
"mypy>=1.8.0",
"moto[all]>=5.0.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I", "N", "W", "B", "C4", "UP"]
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=src --cov-report=html --cov-fail-under=80"
[tool.mypy]
python_version = "3.12"
disallow_untyped_defs = true
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or with homebrew
brew install uv
# Verify installation
uv --version
# Create virtual environment and install dependencies
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install project with dev dependencies
uv pip install -e ".[dev]"
# Or with standard pip
pip install -e ".[dev]"
Create .pre-commit-config.yaml (see references/pre-commit-config.md):
default_language_version:
python: python3.12
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
additional_dependencies: [boto3-stubs]
- repo: https://github.com/PyCQA/bandit
rev: 1.7.6
hooks:
- id: bandit
args: [-r, src/]
Install hooks:
pip install pre-commit
pre-commit install
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual environments
venv/
.venv/
ENV/
env/
# Testing
.pytest_cache/
.coverage
htmlcov/
.tox/
# IDEs
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# AWS
.aws-sam/
samconfig.toml
# Type checking
.mypy_cache/
.dmypy.json
dmypy.json
# src/service/__init__.py
"""Infiquetra Service."""
__version__ = "0.1.0"
# src/service/handler.py
from aws_lambda_powertools import Logger, Tracer
from aws_lambda_powertools.utilities.typing import LambdaContext
logger = Logger()
tracer = Tracer()
@logger.inject_lambda_context
@tracer.capture_lambda_handler
def handler(event: dict, context: LambdaContext) -> dict:
"""Lambda handler."""
logger.info("Processing request")
return {
"statusCode": 200,
"body": {"message": "Hello from Infiquetra!"},
}
# tests/conftest.py
import os
import pytest
# Set AWS credentials for moto
os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
os.environ["AWS_ACCESS_KEY_ID"] = "testing"
os.environ["AWS_SECRET_ACCESS_KEY"] = "testing"
@pytest.fixture
def lambda_context():
"""Mock Lambda context."""
class MockContext:
function_name = "test-function"
aws_request_id = "test-request-id"
def get_remaining_time_in_millis(self) -> int:
return 300000
return MockContext()
# tests/unit/test_handler.py
from src.service.handler import handler
def test_handler_success(lambda_context):
"""Test handler returns 200."""
event = {}
response = handler(event, lambda_context)
assert response["statusCode"] == 200
assert "message" in response["body"]
Ruff replaces flake8, isort, black, and more. Essential rules:
[tool.ruff]
line-length = 100
target-version = "py312"
src = ["src", "tests"]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"F", # pyflakes
"I", # isort
"N", # pep8-naming
"W", # pycodestyle warnings
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
]
ignore = [
"E501", # Line too long (handled by formatter)
]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["S101"] # Allow assert in tests
Run ruff:
# Check for issues
ruff check .
# Auto-fix issues
ruff check . --fix
# Format code
ruff format .
Type checking configuration:
[tool.mypy]
python_version = "3.12"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
show_error_codes = true
pretty = true
# Allow third-party packages without stubs
[[tool.mypy.overrides]]
module = ["moto.*"]
ignore_missing_imports = true
Run mypy:
# Type check entire project
mypy src/
# Check specific file
mypy src/service/handler.py
Testing configuration:
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = [
"--cov=src",
"--cov-report=html",
"--cov-report=term-missing",
"--cov-fail-under=80",
"-ra",
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow tests",
]
Run tests:
# Run all tests with coverage
pytest
# Run only unit tests
pytest -m unit
# Run with verbose output
pytest -v
# Run specific test file
pytest tests/unit/test_handler.py
uv is a fast Python package installer (10-100x faster than pip):
# Install dependencies
uv pip install -e ".[dev]"
# Add new dependency
uv pip install requests
# Update pyproject.toml manually, then:
uv pip install -e ".[dev]"
# Install from requirements.txt
uv pip install -r requirements.txt
# Compile requirements
uv pip compile pyproject.toml -o requirements.txt
[project]
dependencies = [
"boto3>=1.34.0",
"aws-lambda-powertools>=2.30.0",
]
[project]
dependencies = [
"fastapi>=0.109.0",
"uvicorn>=0.27.0",
"pydantic>=2.5.0",
]
[project]
dependencies = [
"aws-cdk-lib>=2.120.0",
"constructs>=10.3.0",
]
After setup, verify everything works:
# 1. Check ruff
ruff check .
# 2. Check mypy
mypy src/
# 3. Run tests
pytest
# 4. Test pre-commit
pre-commit run --all-files
# 5. Verify coverage
coverage report
All checks should pass with 0 errors and 80%+ coverage.
Install project in editable mode:
pip install -e .
Install type stubs:
pip install boto3-stubs[essential] types-requests
Run hooks manually to see detailed errors:
pre-commit run ruff --all-files
pre-commit run mypy --all-files
View coverage report:
coverage html
open htmlcov/index.html
Add tests for uncovered lines.
After project setup:
npx claudepluginhub infiquetra/infiquetra-claude-plugins --plugin python-toolkitSets up Python projects with uv for deps/envs, ruff for linting/formatting, ty for types, pytest for testing. Use for new projects, scripts, or migrations from pip/Poetry.
Configures Python projects with uv, mise, ruff, basedpyright, and pytest. Use for setting up pyproject.toml, builds, environments, linting, formatting, type checking, and testing.
Configures Python projects with modern tooling (uv, ruff, ty). Use when creating projects, writing standalone scripts, or migrating from pip/Poetry/mypy/black.