7.5 KiB
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
Moderate (Recommended)
[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
- Start gradual: Enable mypy gradually, module by module
- Use strict mode:
strict = truefor new projects - Add type hints everywhere: Functions, methods, variables
- Use Protocol for duck typing: Better than inheritance
- Leverage modern syntax: Use
listinstead ofList(Python 3.9+) - Install type stubs: For all third-party packages
- Run in CI: Fail builds on type errors
- 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"]