🔍 Python Type Checking with Mypy¶
A comprehensive guide to type checking in Python using Mypy
🚀 Quick Start¶
Installation¶
Basic Usage¶
Python
# example.py
def greet(name: str) -> str:
return f"Hello, {name}!"
# Run type checking
# mypy example.py
⚙️ Configuration¶
Core Settings¶
This Project's mypy.ini¶
INI
[mypy]
# ==============================================================================
# Global mypy Configuration
# ==============================================================================
# Python version: Specifies the target Python version for type checking.
# This ensures compatibility with the specified Python version's type system.
python_version = 3.11
# Strict mode: Enables all strict type checking options for maximum type safety.
# This is recommended for production code to catch potential type-related bugs early.
strict = True
# Exclusion patterns: Directories to exclude from type checking.
# - tests: Test files often have different type checking requirements
# - migrations: Database migration scripts typically don't benefit from type checking
# - docs: Documentation code examples don't need strict type checking
# - .venv: Virtual environment files should never be type checked
exclude = (?x)(
^tests/
| ^migrations/
| ^docs/
| .venv/
)
# Source directory: Sets the base directory for import resolution.
# This helps mypy correctly resolve imports within your project structure.
mypy_path = src
# Namespace packages: Enables support for PEP 420 namespace packages.
# Useful for projects with a distributed package structure across multiple directories.
namespace_packages = True
# Explicit package bases: Ensures clear package structure with defined base directories.
# Helps prevent import confusion in complex project structures.
explicit_package_bases = True
# ==============================================================================
# Plugin Configuration
# ==============================================================================
# Mypy plugins: Extends mypy's type checking capabilities for specific libraries.
# - pydantic.mypy: Adds support for Pydantic model type checking
# - sqlalchemy.ext.mypy.plugin: Enhances SQLAlchemy ORM type checking
plugins =
pydantic.mypy,
sqlalchemy.ext.mypy.plugin
# ==============================================================================
# Strict Type Checking Settings
# ==============================================================================
# Function definitions: Requires explicit type annotations for all function definitions.
# This ensures that function signatures are clear and well-documented.
disallow_untyped_defs = True
# Incomplete definitions: Prohibits functions with partial type annotations.
# Ensures consistency by requiring complete type information for all parameters and returns.
disallow_incomplete_defs = True
# Untyped function checking: Enables type checking for functions without annotations.
# Helps catch type errors even in functions that haven't been properly annotated yet.
check_untyped_defs = True
# Decorator annotations: Requires type annotations for decorators.
# Important for ensuring type safety when using custom decorators.
disallow_untyped_decorators = True
# Implicit Optional handling: Disables automatic Optional typing for parameters with None defaults.
# Forces explicit declaration of Optional types for better code clarity.
no_implicit_optional = True
# Redundant cast warnings: Flags unnecessary type casts to improve code cleanliness.
# Helps maintain clean code by removing unnecessary type casting operations.
warn_redundant_casts = True
# Unused ignore warnings: Flags unnecessary # type: ignore comments.
# Prevents accumulation of outdated type-ignore directives in the codebase.
warn_unused_ignores = True
# Missing return warnings: Warns about functions that don't return a value but should.
# Catches potential bugs where a function is expected to return a value.
warn_no_return = True
# Unreachable code: Warns about code that can never be executed.
# Identifies dead code that should be removed for maintenance clarity.
warn_unreachable = True
# Optional type checking: Enables strict checking of Optional types.
# Ensures proper handling of potentially None values throughout the codebase.
strict_optional = True
# Equality checking: Ensures proper equality checks between different types.
# Prevents subtle bugs from comparing incompatible types.
strict_equality = True
# Sequence concatenation: Enforces proper type checking when concatenating sequences.
# Prevents type errors when combining lists, tuples, or other sequence types.
strict_concatenate = True
# ==============================================================================
# Additional Production Safety Checks
# ==============================================================================
# Enhanced error detection: Enables specific error codes for production code safety.
# These additional checks help catch subtle bugs that might otherwise go unnoticed.
enable_error_code = [
"truthy-bool", # Prevents using non-boolean expressions in boolean contexts (if x: vs if x is True:)
"redundant-expr", # Identifies expressions that have no effect and can be removed
"unused-awaitable", # Catches forgotten await statements that could lead to unexpected behavior
"ignore-without-code", # Requires specific error codes with # type: ignore for better documentation
"possibly-undefined", # Identifies variables that might be undefined in some execution paths
"redundant-self", # Flags unnecessary self or cls parameters in methods
]
# ==============================================================================
# Error Reporting Configuration
# ==============================================================================
# Output formatting: Configures how mypy presents error messages for better usability.
# These settings make error messages more readable and actionable.
pretty = True # Uses a more readable format for error messages
show_error_context = True # Displays the code context where errors occur
show_column_numbers = True # Shows precise column positions for errors
show_error_codes = True # Includes error codes for easier reference and suppression
color_output = True # Uses colors in terminal output for better readability
error_summary = True # Provides a summary of all errors at the end of the report
# ==============================================================================
# Import Discovery Configuration
# ==============================================================================
# Import handling: Controls how mypy processes and validates imports.
# These settings determine how strictly mypy enforces type checking for imported modules.
ignore_missing_imports = False # By default, requires type stubs for all imports
follow_imports = normal # Normal import following behavior for type checking
follow_imports_for_stubs = True # Prioritizes type stubs over implementation modules
# ==============================================================================
# Third-party Library Configurations
# ==============================================================================
# Library-specific settings: Configures type checking behavior for external dependencies.
# Libraries without complete type annotations need special handling to prevent false positives.
[mypy-orjson.*]
ignore_missing_imports = True # Skips import verification for this library
ignore_errors = true # Ignores type errors within this library's code
[mypy-dotenv.*]
ignore_missing_imports = True # Skips import verification for this library
ignore_errors = true # Ignores type errors within this library's code
# ==============================================================================
# Database-related Configurations
# ==============================================================================
# ORM and database connectors: Special handling for database interaction libraries.
# These libraries often use dynamic attribute access that's difficult to type check.
[mypy-sqlalchemy.*]
ignore_missing_imports = True # SQLAlchemy uses complex metaprogramming that's hard to type check
[mypy-alembic.*]
ignore_missing_imports = True # Alembic migration tool lacks complete type annotations
[mypy-asyncpg.*]
ignore_missing_imports = True # AsyncPG PostgreSQL client lacks complete type annotations
[mypy-databases.*]
ignore_missing_imports = True # Databases library lacks complete type annotations
# ==============================================================================
# Web Framework Configurations
# ==============================================================================
# FastAPI and ASGI: Configuration for web framework components.
# Web frameworks often use complex dependency injection and routing that needs special handling.
[mypy-fastapi.*]
ignore_missing_imports = True # FastAPI uses Pydantic models and complex routing
[mypy-starlette.*]
ignore_missing_imports = True # Starlette (FastAPI foundation) lacks complete type annotations
[mypy-uvicorn.*]
ignore_missing_imports = True # ASGI server lacks complete type annotations
[mypy-gunicorn.*]
ignore_missing_imports = True # WSGI HTTP server lacks complete type annotations
# ==============================================================================
# HTTP Clients and Related Libraries
# ==============================================================================
# HTTP client libraries: Configuration for making network requests.
# These libraries often have complex async patterns or dynamic response handling.
[mypy-httpx.*]
ignore_missing_imports = True # Modern async HTTP client
[mypy-requests.*]
ignore_missing_imports = True # Popular synchronous HTTP client
[mypy-aiohttp.*]
ignore_missing_imports = True # Async HTTP client and server framework
# ==============================================================================
# Serialization/Validation Configurations
# ==============================================================================
# Data validation libraries: Settings for data parsing and validation tools.
# These libraries often use complex validation logic and dynamic field access.
[mypy-pydantic.*]
ignore_missing_imports = True # Data validation using Python type annotations
[mypy-pydantic_settings.*]
ignore_missing_imports = True # Settings management with Pydantic
[mypy-email_validator.*]
ignore_missing_imports = True # Email validation library used by Pydantic
# ==============================================================================
# Testing Frameworks
# ==============================================================================
# Test libraries: Configuration for testing tools and frameworks.
# Testing libraries use fixtures and assertions that can be complex to type check.
[mypy-pytest.*]
ignore_missing_imports = True # Popular Python testing framework
[mypy-hypothesis.*]
ignore_missing_imports = True # Property-based testing framework
# ==============================================================================
# Utility Libraries
# ==============================================================================
# Common utilities: Settings for general-purpose utility libraries.
# These provide infrastructure components that may use dynamic patterns.
[mypy-celery.*]
ignore_missing_imports = True # Distributed task queue
[mypy-redis.*]
ignore_missing_imports = True # Redis client library
# ==============================================================================
# Type Checking Adjustments for ORM Patterns
# ==============================================================================
# ORM compatibility: Relaxes certain type checks that conflict with ORM patterns.
# These adjustments allow for common ORM usage patterns while maintaining type safety.
allow_any_generics = True # Allows Any in generic types (needed for some ORM patterns)
allow_redefinition = True # Permits redefinition of names in certain contexts
# Disables specific error codes that often conflict with ORM and framework patterns
disable_error_code = [
"override", # Allows method overrides with signature differences (common in framework extensions)
"empty-body", # Permits empty function bodies (useful for abstract methods and protocol definitions)
"misc", # Disables miscellaneous checks that may be too strict for framework integration
"arg-type", # Relaxes argument type checking for legacy code and framework callbacks
]
# ==============================================================================
# Framework-specific Error Ignores
# ==============================================================================
# Special cases: Ignores errors in specific framework modules with complex metaprogramming.
# These modules use advanced Python features that are difficult for static type checkers.
[mypy-fastapi.dependencies.utils]
ignore_errors = True # FastAPI dependency resolution uses complex metaprogramming
[mypy-fastapi.routing]
ignore_errors = True # FastAPI routing system uses decorators and complex type patterns
[mypy-sqlalchemy.orm.decl_api]
ignore_errors = True # SQLAlchemy declarative API uses metaclasses and dynamic attributes
# ==============================================================================
# Cache Backends
# ==============================================================================
# Caching libraries: Configuration for memory and distributed cache systems.
# These libraries often use dynamic key/value access patterns.
[mypy-cachetools.*]
ignore_missing_imports = True # In-memory caching utilities
[mypy-aiocache.*]
ignore_missing_imports = True # Async caching framework
# ==============================================================================
# Background Tasks and Scheduling
# ==============================================================================
# Task processing: Settings for background job processing libraries.
# These systems often use decorators and dynamic task registration.
[mypy-apscheduler.*]
ignore_missing_imports = True # Advanced Python scheduler
[mypy-dramatiq.*]
ignore_missing_imports = True # Background task processing system
🔒 Strict Mode Features¶
- ✅
disallow_untyped_defs
: All functions must have type annotations - ✅
strict_optional
: No implicit Optional types - ✅
check_untyped_defs
: Type check all functions - ✅
disallow_untyped_decorators
: Require decorator type annotations
📝 Type Checking Guidelines¶
✨ Best Practices¶
Function Annotations¶
Python
# ✅ Good
def calculate_total(
prices: list[float], tax_rate: float
) -> float:
return sum(prices) * (1 + tax_rate)
# ❌ Bad
def calculate_total(prices, tax_rate):
return sum(prices) * (1 + tax_rate)
Optional Types¶
Python
# ✅ Good
from typing import Optional
def get_user(user_id: Optional[int] = None) -> str:
return "guest" if user_id is None else f"user_{user_id}"
# ❌ Bad
def get_user(user_id=None) -> str:
return "guest" if user_id is None else f"user_{user_id}"
🔧 Common Patterns¶
🏭 Working with Classes¶
Python
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
def render(entity: Drawable) -> None:
entity.draw()
🔄 Generic Types¶
Python
from typing import TypeVar, Sequence
T = TypeVar("T")
def first_element(seq: Sequence[T]) -> T:
if not seq:
raise ValueError("Empty sequence")
return seq[0]
❗ Troubleshooting¶
Common Errors¶
🚫 Missing Return Type¶
Python
# Error: Function is missing a return type annotation
def add(a: int, b: int): # ❌
return a + b
# Fixed:
def add(a: int, b: int) -> int: # ✅
return a + b
🚫 Optional Type Mismatch¶
Python
# Error: Incompatible return value type
def get_name(user_id: int) -> str: # ❌
user = find_user(user_id)
return user.name if user else None
# Fixed:
def get_name(user_id: int) -> Optional[str]: # ✅
user = find_user(user_id)
return user.name if user else None
📚 Resources¶
Official Resources¶
Additional Resources¶
IDE Integration¶
- 💻 VS Code: Built-in support
- 📝 PyCharm: Native support
- 🔧 Vim/Neovim: Via ALE or Syntastic
- ✨ Sublime Text: Via SublimeLinter-contrib-mypy
💡 Pro Tip: Use
dmypy run
for faster incremental type checking during development.