Static type checking with Mypy for Python code quality. Use when writing or reviewing Python code to ensure type safety, catch bugs early, and maintain code quality through proper type annotations and Mypy configuration.
56
62%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./plugins/python-experts/skills/python-mypy/SKILL.mdThis skill automatically activates when writing Python code to ensure proper type annotations and compatibility with Mypy static type checking.
from typing import Optional
from collections.abc import Sequence
# Good: Complete type hints
def process_items(
items: list[str],
max_count: int | None = None,
debug: bool = False,
) -> dict[str, int]:
"""Process items and return counts."""
result: dict[str, int] = {}
# Implementation
return result
# Good: Generic types with TypeVar
from typing import TypeVar
T = TypeVar('T')
def first(items: Sequence[T]) -> T | None:
"""Get first item from sequence."""
return items[0] if items else Nonefrom typing import ClassVar
from dataclasses import dataclass
@dataclass
class User:
"""User model with type hints."""
id: int
name: str
email: str | None = None
active: bool = True
# Class variable
_registry: ClassVar[dict[int, 'User']] = {}
def __post_init__(self) -> None:
"""Register user after initialization."""
self._registry[self.id] = selffrom typing import Protocol
class Drawable(Protocol):
"""Protocol for drawable objects."""
def draw(self) -> str:
"""Draw the object."""
...
def render(obj: Drawable) -> None:
"""Render any drawable object."""
print(obj.draw())
# Any class with draw() method satisfies this
class Circle:
def draw(self) -> str:
return "○"
render(Circle()) # OK with Mypyfrom typing import TypedDict, NotRequired
class UserDict(TypedDict):
"""Structured user dictionary."""
id: int
name: str
email: NotRequired[str] # Optional key (Python 3.11+)
def create_user(data: UserDict) -> None:
"""Create user from typed dictionary."""
user_id: int = data["id"] # Type-safe access
# Mypy knows 'email' might not exist[mypy]
python_version = 3.11
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
disallow_any_generics = True
disallow_subclassing_any = True
disallow_untyped_calls = 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
warn_unreachable = True
strict_equality = True
show_error_codes = True
show_column_numbers = True
# Start strict, relax per-module if needed
[mypy-tests.*]
disallow_untyped_defs = False
[mypy-migrations.*]
ignore_errors = True
# Third-party without stubs
[mypy-some_library.*]
ignore_missing_imports = True[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
show_error_codes = true
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
[[tool.mypy.overrides]]
module = "migrations.*"
ignore_errors = true# Bad: Implicit Optional
def find_user(id: int) -> User: # Mypy error if can return None
return users.get(id) # dict.get returns User | None
# Good: Explicit Optional
def find_user(id: int) -> User | None:
return users.get(id)
# Good: Narrow type with assertion
def get_user(id: int) -> User:
user = users.get(id)
assert user is not None, f"User {id} not found"
return user # Mypy knows this is User, not Nonedef process(value: str | int) -> str:
"""Process value based on type."""
if isinstance(value, str):
# Mypy knows value is str here
return value.upper()
else:
# Mypy knows value is int here
return str(value * 2)from typing import Generic, TypeVar
T = TypeVar('T')
class Stack(Generic[T]):
"""Type-safe stack."""
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
# Usage
int_stack: Stack[int] = Stack()
int_stack.push(1) # OK
int_stack.push("x") # Mypy error!disallow_untyped_defs[mypy]
# Strict by default
disallow_untyped_defs = True
# Relax for legacy
[mypy-legacy.*]
disallow_untyped_defs = False
check_untyped_defs = True # Still check what we can# When third-party library lacks types
import untyped_library # type: ignore[import-untyped]
# When dealing with dynamic code (rare)
def dynamic_call() -> Any:
result = getattr(obj, method_name)() # type: ignore[misc]
return resultfrom typing import Generator
from contextlib import contextmanager
@contextmanager
def database_transaction() -> Generator[Connection, None, None]:
"""Type-safe context manager."""
conn = get_connection()
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()from collections.abc import Callable
def retry(
func: Callable[[int], str],
times: int = 3,
) -> str:
"""Retry a function that takes int and returns str."""
for _ in range(times):
try:
return func(42)
except Exception:
continue
raise RuntimeError("All retries failed")from typing import overload
@overload
def parse(data: str) -> dict[str, str]: ...
@overload
def parse(data: bytes) -> dict[str, bytes]: ...
def parse(data: str | bytes) -> dict[str, str] | dict[str, bytes]:
"""Parse data with type-specific return."""
if isinstance(data, str):
return {"parsed": data}
return {"parsed": data}from django.db import models
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from django.db.models.manager import RelatedManager
class Author(models.Model):
name = models.CharField(max_length=100)
if TYPE_CHECKING:
books: RelatedManager['Book']
class Book(models.Model):
title = models.CharField(max_length=200)
author: models.ForeignKey[Author] = models.ForeignKey(
Author,
on_delete=models.CASCADE,
related_name='books',
)[mypy]
plugins = mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = "myproject.settings"# Check all files
mypy .
# Check specific files/directories
mypy src/
# Show error codes
mypy --show-error-codes .
# Generate HTML report
mypy --html-report ./mypy-report .# .github/workflows/type-check.yml
- name: Type check with Mypy
run: |
pip install mypy
mypy --strict src/Any Unnecessarily# Bad: Any hides all type errors
def process(data: Any) -> Any:
return data.unknown_method() # No error!
# Good: Use specific types
def process(data: dict[str, int]) -> list[int]:
return list(data.values())# Bad: Blanket ignore
x = dangerous_call() # type: ignore
# Good: Specific ignore with reason
x = legacy_api_call() # type: ignore[misc] # TODO: Add types to legacy API# Bad: Mypy will catch this
def process(data: str) -> None:
encoded: bytes = data # Error!
# Good: Explicit conversion
def process(data: str) -> None:
encoded: bytes = data.encode('utf-8')# Check type coverage
mypy --html-report ./coverage .
# Show coverage stats
mypy --any-exprs-report ./coverage .| Skill | Purpose |
|---|---|
python-experts:python-style | Python coding standards |
python-experts:python-code-review | Code review guidelines |
python-experts:python-testing-expert | Testing patterns |
0ebe7ae
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.