Skip to main content
rule

mypy — Cursor rule for Python type checks and setup

This rule guides mypy configuration and Python annotations. Use it when you want type issues caught before your code runs.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/mypy.mdc
Open on GitHub

What mypy does

The rule applies to files matching **/*.py. It recommends keeping mypy settings in pyproject.toml, pinning the checker version, and adding checks to CI or pre-commit. For an existing codebase, it suggests starting with permissive settings and tightening them gradually.

The guidance covers annotations: use object instead of Any, mark aliases with TypeAlias, write unions with |, and prefer built-in generic types. It also explains structural typing with Protocol and error suppressions that include a specific code. For large projects, it recommends dmypy for incremental checks.

To add the rule to Cursor, place it at .cursor/rules/mypy.mdc. Pin the mypy version in the project configuration; libraries without type stubs need installed or generated stubs.

What it helps with

  • configure mypy in pyproject.toml
  • introduce stricter checks gradually in an existing codebase
  • replace broad annotations with more precise types
  • suppress a specific mypy error using its code
  • check types for libraries without stubs

Example prompts for the agent

  • Review this Python file's type hints using the mypy rule.
  • Set up pyproject.toml for a gradual move to stricter mypy checks.
  • Replace broad annotations in this module and give each needed # type: ignore a specific code.

How to install mypy

Manually: put the files in place

.cursor/rules/mypy.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about mypy

How do I install the mypy rule in Cursor?

Add the rule at .cursor/rules/mypy.mdc. It applies to Python files matching **/*.py.

What does the mypy rule do?

It provides guidance on mypy configuration, Python annotations, and handling type errors. It also covers dmypy and stubs for third-party libraries.

How should I introduce mypy into an existing project?

Start with a minimal configuration in pyproject.toml, then enable stricter checks module by module or across the project. The rule recommends pinning the mypy version.

Source text

mypy.mdc · CC0-1.0
---
description: This guide provides definitive, actionable best practices for using mypy to ensure robust static type checking in Python projects, focusing on modern patterns and common pitfalls.
globs: **/*.py
---
# mypy Best Practices

mypy is the definitive static type checker for Python. Adhering to these guidelines ensures your codebase is robust, maintainable, and catches type errors before runtime.

## 1. Configuration & Integration

Always use a shared `pyproject.toml` for mypy configuration. This ensures consistency across the team and CI/CD. Pin your mypy version.

### 1.1. Centralized Configuration

**Always** configure mypy via `pyproject.toml`. This is the modern standard.

❌ BAD: `mypy.ini` or command-line flags only
✅ GOOD: `pyproject.toml`
```toml
# pyproject.toml
[tool.mypy]
python_version = "3.11"
warn_unused_ignores = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true
strict_optional = true
warn_redundant_casts = true
warn_return_any = true
warn_unreachable = true
# Enable strict mode for new code, or specific modules
# strict = true
```

### 1.2. CI/CD & Pre-commit Hooks

Integrate mypy into your CI/CD pipeline and as a pre-commit hook. Catch errors early.

```bash
# .pre-commit-config.yaml
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v4.5.0 # Use the latest stable version
  hooks:
    - id: check-added-large-files
    - id: check-yaml
    - id: end-of-file-fixer
    - id: trailing-whitespace
- repo: https://github.com/pre-commit/mirrors-mypy
  rev: v1.8.0 # Pin to the same version as your pyproject.toml
  hooks:
    - id: mypy
      args: [--no-strict-optional, --ignore-missing-imports] # Override global strict for pre-commit if needed
```

### 1.3. Incremental Adoption

For existing codebases, start with a minimal configuration and gradually enable stricter checks. Prioritize annotating widely imported modules.

```toml
# pyproject.toml (initial, less strict)
[tool.mypy]
python_version = "3.11"
ignore_missing_imports = true # Temporarily ignore missing stubs
allow_untyped_defs = true     # Allow unannotated functions
```
Then, enable stricter checks module by module or globally.

## 2. Type Hinting Best Practices

### 2.1. Prefer `object` over `Any`

Use `object` when a function truly accepts any value, especially for arguments. `Any` disables type checking.

❌ BAD:
```python
def process_data(data: Any) -> None:
    print(str(data))
```
✅ GOOD:
```python
def process_data(data: object) -> None:
    # mypy knows 'data' is an object, but not its specific type
    print(str(data))
```

### 2.2. Use `TypeAlias` for Type Aliases

Explicitly declare type aliases with `TypeAlias` for clarity.

❌ BAD:
```python
Vector = list[float]
```
✅ GOOD:
```python
from typing import TypeAlias

Vector: TypeAlias = list[float]
```

### 2.3. Concrete vs. Abstract Types

For arguments, prefer abstract types (protocols, ABCs) for flexibility. For return values, prefer concrete types for clear implementation.

❌ BAD (arguments too specific, returns too abstract):
```python
from typing import MutableMapping

def map_data(input: list[str]) -> MutableMapping[str, int]:
    return {s: len(s) for s in input}
```
✅ GOOD:
```python
from collections.abc import Iterable

def map_data(input: Iterable[str]) -> dict[str, int]:
    """Maps strings to their lengths."""
    return {s: len(s) for s in input}
```

### 2.4. Shorthand Union Syntax

Use `|` for unions and `None` as the last element.

❌ BAD:
```python
from typing import Union, Optional

def get_item(key: Union[str, int]) -> Optional[str]:
    ...
```
✅ GOOD:
```python
def get_item(key: str | int) -> str | None:
    ...
```

### 2.5. Built-in Generics

Use built-in generic types (`list`, `dict`, `set`) instead of `typing` aliases.

❌ BAD:
```python
from typing import List, Dict

def process_items(items: List[str]) -> Dict[str, int]:
    ...
```
✅ GOOD:
```python
def process_items(items: list[str]) -> dict[str, int]:
    ...
```

### 2.6. Protocols for Structural Typing

Define interfaces with `Protocol` for robust duck typing.

```python
from typing import Protocol

class SupportsQuack(Protocol):
    def quack(self) -> str:
        ...

def make_it_quack(duck: SupportsQuack) -> None:
    print(duck.quack())

class MyDuck:
    def quack(self) -> str:
        return "Quack!"

make_it_quack(MyDuck()) # ✅ Works, MyDuck implicitly satisfies SupportsQuack
```

## 3. Managing Type Errors

### 3.1. Specific `# type: ignore`

Only use `# type: ignore` as a last resort, always with a specific error code, and review regularly.

❌ BAD:
```python
result = some_untyped_function() # type: ignore
```
✅ GOOD:
```python
result = some_untyped_function() # type: ignore[no-untyped-call]
```

### 3.2. Strict Mode for New Code

Enable `strict = true` in `pyproject.toml` for all new modules or the entire codebase once mature.

```toml
# pyproject.toml
[tool.mypy]
strict = true
```

## 4. Performance Considerations

Use the mypy daemon (`dmypy`) for faster incremental checks in large codebases.

```bash
# Start the daemon once
dmypy start

# Run checks repeatedly
dmypy run -- foo.py bar.py

# Stop the daemon when done
dmypy stop
```

## 5. Third-Party Libraries

For libraries without type stubs, install community stubs or generate your own.

```bash
# Install community stubs
mypy --install-types
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Similar IDE rules

All IDE rules for Programming languages →

From the same set240

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor