From python-experts
Sets up Mypy static type checking for Python projects: generates mypy.ini/pyproject.toml config, installs Mypy/plugins (Django support), adds IDE/CI/CD/pre-commit setups. Use for new Python projects needing types.
npx claudepluginhub jpoutrin/product-forge --plugin python-expertsThis skill uses the workspace's default tool permissions.
**Category**: Python Development
Enforces Mypy static type checking for Python code with annotation patterns, strict configurations, and best practices to ensure type safety and catch bugs early.
Sets up Python code quality toolchain with ruff linting/formatting, mypy type checking, complexity gating, dead code detection, pre-commit hooks, pyproject.toml config, and Makefile targets.
Performs Python type checking with ty, Astral's fast type checker (10-100x faster than mypy/Pyright). Configures rules in pyproject.toml and sets up projects with uv or pip.
Share bugs, ideas, or general feedback.
Category: Python Development
/mypy-setup [--strict] [--django] [--format=<ini|toml>] [--install]
| Argument | Required | Description |
|---|---|---|
--strict | No | Use strict type checking configuration |
--django | No | Add Django-specific configuration and plugin |
--format=<ini|toml> | No | Config format (default: ini) |
--install | No | Install Mypy and required plugins |
Set up Mypy static type checking in a Python project:
When this command is run, Claude Code should:
STRICT_MODE = true if --strict specified
DJANGO_MODE = true if --django specified
FORMAT = value after --format= (default: "ini")
INSTALL = true if --install specified
Scan the project to determine:
# Check for Django
HAS_DJANGO=false
if [ -f "manage.py" ] || grep -q "django" requirements.txt 2>/dev/null; then
HAS_DJANGO=true
echo "✓ Django project detected"
fi
# Check for existing config
HAS_CONFIG=false
if [ -f "mypy.ini" ] || [ -f ".mypy.ini" ] || grep -q "\[tool.mypy\]" pyproject.toml 2>/dev/null; then
HAS_CONFIG=true
echo "⚠ Existing Mypy configuration found"
fi
# Check Python version
PYTHON_VERSION=$(python --version | cut -d' ' -f2 | cut -d'.' -f1,2)
echo "Python version: $PYTHON_VERSION"
if [ "$INSTALL" = true ]; then
echo "Installing Mypy..."
# Use uv if available, otherwise pip
if command -v uv &> /dev/null; then
PKG_MANAGER="uv pip install"
else
PKG_MANAGER="pip install"
fi
# Install Mypy
$PKG_MANAGER mypy
# Install Django plugin if needed
if [ "$DJANGO_MODE" = true ] || [ "$HAS_DJANGO" = true ]; then
$PKG_MANAGER django-stubs mypy-django-plugin
fi
# Install common type stubs
$PKG_MANAGER types-requests types-PyYAML types-redis
echo "✓ Mypy installed successfully"
fi
if [ "$FORMAT" = "ini" ]; then
cat > mypy.ini << 'EOF'
[mypy]
# Python version
python_version = 3.11
# Import discovery
namespace_packages = True
explicit_package_bases = True
ignore_missing_imports = False
# Platform configuration
platform = linux
# Disallow dynamic typing
disallow_any_generics = True
disallow_subclassing_any = True
# Disallow untyped code
disallow_untyped_calls = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
check_untyped_defs = True
disallow_untyped_decorators = True
# None and Optional handling
no_implicit_optional = True
strict_optional = True
# Warnings
warn_redundant_casts = True
warn_unused_ignores = True
warn_no_return = True
warn_return_any = True
warn_unreachable = True
# Strictness flags
strict_equality = True
strict_concatenate = True
# Error messages
show_error_codes = True
show_column_numbers = True
show_error_context = True
pretty = True
# Miscellaneous
warn_unused_configs = True
# Per-module options for gradual typing
[mypy-tests.*]
disallow_untyped_defs = False
[mypy-migrations.*]
ignore_errors = True
EOF
if [ "$STRICT_MODE" = true ]; then
# Already strict by default
echo "✓ Created strict mypy.ini"
else
# Relax some settings for gradual adoption
cat >> mypy.ini << 'EOF'
# Relaxed settings for gradual adoption
# Remove these overrides as you add type hints
[mypy]
disallow_untyped_calls = False
disallow_untyped_defs = False
disallow_incomplete_defs = False
EOF
echo "✓ Created mypy.ini (gradual mode)"
echo " Gradually enable stricter checks as you add type hints"
fi
# Add Django configuration
if [ "$DJANGO_MODE" = true ] || [ "$HAS_DJANGO" = true ]; then
cat >> mypy.ini << 'EOF'
# Django configuration
[mypy]
plugins = mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = "config.settings" # Update with your settings module
EOF
echo "✓ Added Django plugin configuration"
echo " Update django_settings_module in mypy.ini"
fi
fi
if [ "$FORMAT" = "toml" ]; then
# Check if pyproject.toml exists
if [ ! -f "pyproject.toml" ]; then
cat > pyproject.toml << 'EOF'
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
EOF
fi
# Add Mypy configuration
cat >> pyproject.toml << 'EOF'
[tool.mypy]
python_version = "3.11"
namespace_packages = true
explicit_package_bases = true
ignore_missing_imports = false
# Strictness
disallow_any_generics = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
# None/Optional
no_implicit_optional = true
strict_optional = true
# Warnings
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_return_any = true
warn_unreachable = true
# Output
show_error_codes = true
show_column_numbers = true
show_error_context = true
pretty = true
# Per-module options
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
[[tool.mypy.overrides]]
module = "migrations.*"
ignore_errors = true
EOF
if [ "$DJANGO_MODE" = true ] || [ "$HAS_DJANGO" = true ]; then
cat >> pyproject.toml << 'EOF'
[tool.mypy]
plugins = ["mypy_django_plugin.main"]
[tool.django-stubs]
django_settings_module = "config.settings"
EOF
fi
echo "✓ Created [tool.mypy] section in pyproject.toml"
fi
For user-specific defaults:
cat > ~/.mypy.ini << 'EOF'
[mypy]
# User-wide Mypy defaults
warn_unused_ignores = True
show_error_codes = True
pretty = True
EOF
if [ -f ".gitignore" ]; then
if ! grep -q ".mypy_cache" .gitignore; then
echo "" >> .gitignore
echo "# Mypy cache" >> .gitignore
echo ".mypy_cache/" >> .gitignore
echo "✓ Added .mypy_cache to .gitignore"
fi
fi
mkdir -p .vscode
if [ ! -f ".vscode/settings.json" ]; then
cat > .vscode/settings.json << 'EOF'
{
"python.linting.enabled": true,
"python.linting.mypyEnabled": true,
"python.linting.mypyArgs": [
"--show-error-codes",
"--show-column-numbers"
]
}
EOF
echo "✓ Created .vscode/settings.json for Mypy"
else
echo "⚠ .vscode/settings.json already exists"
echo " Add Mypy configuration manually if needed"
fi
if [ -f ".pre-commit-config.yaml" ]; then
# Check if Mypy already configured
if ! grep -q "mirrors-mypy" .pre-commit-config.yaml; then
cat >> .pre-commit-config.yaml << 'EOF'
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
args: [--show-error-codes]
additional_dependencies:
- types-requests
- types-PyYAML
EOF
if [ "$DJANGO_MODE" = true ] || [ "$HAS_DJANGO" = true ]; then
cat >> .pre-commit-config.yaml << 'EOF'
- django-stubs
- mypy-django-plugin
EOF
fi
echo "✓ Added Mypy to .pre-commit-config.yaml"
else
echo "⚠ Mypy already in .pre-commit-config.yaml"
fi
else
# Create new pre-commit config
cat > .pre-commit-config.yaml << 'EOF'
repos:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
args: [--show-error-codes]
additional_dependencies:
- types-requests
- types-PyYAML
EOF
if [ "$DJANGO_MODE" = true ] || [ "$HAS_DJANGO" = true ]; then
cat >> .pre-commit-config.yaml << 'EOF'
- django-stubs
- mypy-django-plugin
EOF
fi
echo "✓ Created .pre-commit-config.yaml with Mypy"
fi
mkdir -p .github/workflows
cat > .github/workflows/type-check.yml << 'EOF'
name: Type Check
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
mypy:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mypy
EOF
if [ "$DJANGO_MODE" = true ] || [ "$HAS_DJANGO" = true ]; then
cat >> .github/workflows/type-check.yml << 'EOF'
pip install django-stubs mypy-django-plugin
EOF
fi
cat >> .github/workflows/type-check.yml << 'EOF'
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Type check with Mypy
run: mypy .
- name: Upload Mypy report
if: failure()
uses: actions/upload-artifact@v4
with:
name: mypy-report
path: .mypy_cache/
EOF
echo "✓ Created .github/workflows/type-check.yml"
Mypy Setup Complete!
====================
Configuration:
• Config file: mypy.ini
• Mode: Strict
• Django plugin: Enabled
• Python version: 3.11
Files Created:
✓ mypy.ini
✓ .vscode/settings.json
✓ .pre-commit-config.yaml
✓ .github/workflows/type-check.yml
Next Steps:
1. Review and adjust mypy.ini settings
2. Update Django settings module path (if applicable)
3. Run: /mypy-check to test configuration
4. Install pre-commit: pre-commit install
5. Add type hints to your code gradually
Gradual Adoption Strategy:
1. Start with new files (keep disallow_untyped_defs = True)
2. Add type hints to critical paths (services, models)
3. Enable stricter checks per module:
[mypy-myapp.services.*]
disallow_untyped_defs = True
4. Expand coverage over time
Resources:
• Mypy docs: https://mypy.readthedocs.io/
• Cheat sheet: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html
• Django stubs: https://github.com/typeddjango/django-stubs
Run '/mypy-check' to verify your setup!
If no arguments provided, ask user questions:
Mypy Setup Wizard
=================
1. Configuration format?
[1] mypy.ini (recommended)
[2] pyproject.toml
2. Strictness level?
[1] Strict (recommended for new projects)
[2] Gradual (for existing projects)
3. Project type?
[1] Django
[2] FastAPI
[3] Generic Python
4. Install Mypy and plugins?
[Y/n]
5. Setup IDE integration (VS Code)?
[Y/n]
6. Add pre-commit hook?
[Y/n]
7. Create GitHub Actions workflow?
[Y/n]
# Basic setup (interactive)
/mypy-setup
# Strict mode with mypy.ini
/mypy-setup --strict
# Django project with installation
/mypy-setup --django --install
# Use pyproject.toml format
/mypy-setup --format=toml
# Full setup for Django
/mypy-setup --strict --django --format=toml --install
[mypy]
python_version = 3.11
warn_return_any = True
warn_unused_configs = True
show_error_codes = True
# Start here, gradually enable more
[mypy]
python_version = 3.11
disallow_untyped_defs = True
warn_return_any = True
warn_unused_configs = True
no_implicit_optional = True
show_error_codes = True
[mypy-tests.*]
disallow_untyped_defs = False
[mypy]
python_version = 3.11
strict = True
show_error_codes = True
| Skill | Purpose |
|---|---|
python-experts:python-mypy | Mypy type checking patterns |
python-experts:python-style | Python coding standards |
| Command | Purpose |
|---|---|
/mypy-check | Run type checking |
/python-experts:review-django-commands | Review Django commands |
# Update in mypy.ini
[mypy.plugins.django-stubs]
django_settings_module = "your_project.settings"
# Install missing type stubs
mypy --install-types
Start with:
[mypy]
ignore_missing_imports = True
check_untyped_defs = False
Then gradually remove these.