Files
gh-jwplatta-prompt-library-…/skills/python-code-quality/mypy-configuration.md
2025-11-30 08:30:04 +08:00

7.5 KiB

Mypy Type Checking Configuration

Mypy is a static type checker for Python that helps catch bugs before runtime.

Basic Configuration

Add to pyproject.toml:

[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
strict_equality = true
show_error_codes = true
show_column_numbers = true

[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false

[[tool.mypy.overrides]]
module = "third_party_package.*"
ignore_missing_imports = true

Strictness Levels

Minimal (Getting Started)

[tool.mypy]
python_version = "3.11"
warn_return_any = false
warn_unused_configs = true
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
check_untyped_defs = true

Strict (Maximum Safety)

[tool.mypy]
strict = true
python_version = "3.11"

Common Configuration Options

Type Checking Strictness

# Require type annotations
disallow_untyped_defs = true          # Functions must have type hints
disallow_untyped_calls = true         # Can't call untyped functions
disallow_incomplete_defs = true       # All params must be typed

# Optional handling
no_implicit_optional = true           # Optional must be explicit
strict_optional = true                # None checking enabled

# Any type usage
disallow_any_unimported = false       # Allow Any from untyped imports
disallow_any_expr = false             # Forbid Any in expressions
disallow_any_decorated = false        # Forbid Any in decorators
disallow_any_explicit = false         # Forbid explicit Any

Warning Messages

warn_return_any = true                # Warn on returning Any
warn_redundant_casts = true           # Warn on unnecessary casts
warn_unused_ignores = true            # Warn on unused type: ignore
warn_unused_configs = true            # Warn on unused config options
warn_no_return = true                 # Warn on missing returns
warn_unreachable = true               # Warn on unreachable code

Error Reporting

show_error_codes = true               # Show error codes in messages
show_column_numbers = true            # Show column numbers
pretty = true                         # Pretty print errors
color_output = true                   # Colorize output
error_summary = true                  # Show error summary

Type Hints Guide

Basic Types

from typing import Any, Optional, Union

# Simple types
def greet(name: str) -> str:
    return f"Hello, {name}"

# Optional (can be None)
def find_user(user_id: int) -> Optional[User]:
    return user_map.get(user_id)

# Modern Optional syntax (Python 3.10+)
def find_user(user_id: int) -> User | None:
    return user_map.get(user_id)

# Union types
def process(value: Union[int, str]) -> bool:
    return True

# Modern Union syntax (Python 3.10+)
def process(value: int | str) -> bool:
    return True

Collections

from typing import List, Dict, Set, Tuple

# Lists
def process_items(items: List[str]) -> List[int]:
    return [len(item) for item in items]

# Modern syntax (Python 3.9+)
def process_items(items: list[str]) -> list[int]:
    return [len(item) for item in items]

# Dictionaries
def get_config() -> Dict[str, Any]:
    return {"key": "value"}

# Modern syntax
def get_config() -> dict[str, Any]:
    return {"key": "value"}

# Sets
def unique_items(items: Set[int]) -> int:
    return len(items)

# Tuples (fixed size)
def get_coordinates() -> Tuple[float, float]:
    return (1.0, 2.0)

# Tuples (variable size)
def get_numbers() -> Tuple[int, ...]:
    return (1, 2, 3, 4, 5)

Callable Types

from typing import Callable

# Function that takes int and returns str
Handler = Callable[[int], str]

def process(handler: Handler) -> None:
    result = handler(42)

# Multiple parameters
Callback = Callable[[str, int], bool]

# No parameters
Factory = Callable[[], MyClass]

Generic Types

from typing import TypeVar, Generic

T = TypeVar('T')

class Container(Generic[T]):
    def __init__(self, value: T) -> None:
        self.value = value

    def get(self) -> T:
        return self.value

# Usage
container: Container[int] = Container(42)
value: int = container.get()

Protocol Types

from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None: ...

def render(item: Drawable) -> None:
    item.draw()

# Any class with draw() method works
class Circle:
    def draw(self) -> None:
        print("Drawing circle")

render(Circle())  # Type checks!

Commands Reference

# Check all files
mypy src

# Check specific file
mypy src/module.py

# Strict mode
mypy --strict src

# Show error codes
mypy --show-error-codes src

# Generate type stubs
stubgen -p mypackage -o stubs

# Check against installed packages
mypy --install-types
mypy --non-interactive --install-types

Common Errors and Fixes

Error: "Argument has incompatible type"

# Problem
def greet(name: str) -> str:
    return f"Hello, {name}"

greet(123)  # Error: Argument 1 has incompatible type "int"

# Fix: Pass correct type
greet("World")

# Or: Convert type
greet(str(123))

Error: "Function is missing a return statement"

# Problem
def get_value() -> int:
    if condition:
        return 42
    # Missing return!

# Fix: Add return for all paths
def get_value() -> int:
    if condition:
        return 42
    return 0

Error: "Need type annotation"

# Problem
items = []  # Error: Need type annotation

# Fix: Add type annotation
items: list[str] = []

# Or: Initialize with values
items = ["a", "b", "c"]

Error: "Incompatible return type"

# Problem
def get_name() -> str:
    return None  # Error: Incompatible return

# Fix: Use Optional
def get_name() -> str | None:
    return None

Ignoring Errors

# Ignore single line
result = unsafe_function()  # type: ignore

# Ignore with reason
result = unsafe_function()  # type: ignore[arg-type]

# Ignore entire file
# mypy: ignore-errors

Third-Party Package Stubs

Installing Type Stubs

# Install stubs for requests
uv add --dev types-requests

# Common stubs
uv add --dev types-requests
uv add --dev types-redis
uv add --dev types-PyYAML

Handling Missing Stubs

[[tool.mypy.overrides]]
module = "untyped_package.*"
ignore_missing_imports = true

VSCode Integration

Add to .vscode/settings.json:

{
  "python.linting.mypyEnabled": true,
  "python.linting.enabled": true,
  "python.analysis.typeCheckingMode": "basic"
}

Best Practices

  1. Start gradual: Enable mypy gradually, module by module
  2. Use strict mode: strict = true for new projects
  3. Add type hints everywhere: Functions, methods, variables
  4. Use Protocol for duck typing: Better than inheritance
  5. Leverage modern syntax: Use list instead of List (Python 3.9+)
  6. Install type stubs: For all third-party packages
  7. Run in CI: Fail builds on type errors
  8. Document with types: Types serve as documentation

Incremental Adoption

# Start with specific directories
[tool.mypy]
files = ["src/critical_module"]
strict = true

# Gradually expand
files = ["src/critical_module", "src/api"]

# Eventually cover everything
files = ["src"]