Mypy
相关工具推荐
| 工具 | 说明 |
|---|---|
| Ruff | 高性能 Python linter,可替代 Flake8 并集成部分 mypy 功能 |
| Pylint | Python 代码质量检查工具,侧重代码风格与逻辑缺陷 |
| Bandit | Python 安全漏洞扫描工具 |
| Pre-commit | 管理 Git pre-commit 钩子的通用框架 |
1. 简介
Mypy 是 Python 生态中最主流的可选静态类型检查器(Optional Static Type Checker),由 Jukka Lehtosalo 创建,并得到 Python 之父 Guido van Rossum 的支持。Mypy 遵循 PEP 484 类型注解标准,能够在不运行代码的情况下,通过静态分析检测类型错误,帮助开发者在开发阶段捕获潜在的类型相关 Bug。
- 主要检查语言:Python
- 主要检查能力:Python 静态类型检查,在不运行代码的情况下检测类型错误;涵盖静态类型检查
- 核心检查原理:基于 PEP 484 类型注解标准,支持渐进式类型系统
- 检查规则/选项:约 50+ 个错误码(分为"默认启用"与"可选启用"两大类),默认启用错误码列表
- GitHub 仓库:https://github.com/python/mypy(20,474 stars)
- 开源协议:MIT
- 最新稳定版本:v1.15.0
- 运行环境要求:Python 3.9+(运行 mypy);可检查 Python 3.9+ 代码
- 误报率:低
2. 官方文档
3. 社区优秀实践
3.1 Flask(Pallets 团队)
Flask 是 Python 生态中最流行的轻量级 Web 框架之一,由 Pallets 团队维护。Flask 在 pyproject.toml 中配置了严格的 mypy 检查,并通过 tox 运行 mypy 和 pyright 双重类型检查。
- 仓库地址:pallets/flask
- 配置文件:
pyproject.toml中的[tool.mypy]节
# 来自 pallets/flask 的 mypy 配置(简化)
[tool.mypy]
python_version = "3.10"
files = ["src", "tests/type_check"]
show_error_codes = true
pretty = true
strict = true
[[tool.mypy.overrides]]
module = [
"asgiref.*",
"dotenv.*",
"cryptography.*",
"importlib_metadata",
]
ignore_missing_imports = true
实践要点:
- 启用
strict = true严格模式,对所有源码进行最高级别类型检查 - 通过
[[tool.mypy.overrides]]对缺少类型存根的第三方库单独配置ignore_missing_imports - 在 tox 的
typing环境中同时运行mypy和pyright,实现双重保障
3.2 pandas(pandas-dev 团队)
pandas 是 Python 数据科学领域最核心的库之一,代码库规模庞大。pandas 在 pyproject.toml 中配置了详细的 mypy 规则,体现了大型项目渐进式引入严格类型检查的策略。
- 仓库地址:pandas-dev/pandas
- 配置文件:
pyproject.toml中的[tool.mypy]节
# 来自 pandas-dev/pandas 的 mypy 配置(简化)
[tool.mypy]
mypy_path = "typings"
files = ["pandas", "typings"]
ignore_missing_imports = true
python_version = "3.11"
platform = "linux-64"
# 严格类型检查
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
# None 和 Optional 处理
no_implicit_optional = true
strict_optional = true
# 警告配置
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
enable_error_code = "ignore-without-code"
实践要点:
- 逐项启用严格选项而非直接使用
strict = true,便于团队逐步迁移 - 使用
mypy_path指定自定义类型存根目录 - 启用
enable_error_code = "ignore-without-code",要求所有# type: ignore注释必须附带错误码
3.3 Zulip
Zulip 是一个大型开源团队协作平台,Python 代码占比超过 57%,是 mypy 早期采用者之一。Zulip 在 CI 中集成 mypy 检查,类型错误会阻断合并请求。
- 仓库地址:zulip/zulip
- 生态参考项目:Zulip 是大型 Python 项目引入 mypy 的标杆案例
4. 工具配置说明
4.1 配置文件说明
Mypy 支持多种配置文件格式,按优先级从高到低排列。mypy 不会合并多个配置文件,只会使用找到的第一个配置文件。
| 配置文件 | 格式 | 用途 |
|---|---|---|
--config-file 命令行参数 |
任意 | 显式指定,最高优先级 |
mypy.ini |
INI | 项目根目录,传统方式 |
.mypy.ini |
INI | 隐藏文件形式 |
pyproject.toml([tool.mypy] 节) |
TOML | 现代 Python 项目推荐 |
setup.cfg([mypy] 节) |
INI | 与 setuptools 集成 |
~/.config/mypy/config |
INI | 用户全局配置 |
4.2 配置文件详解
mypy 配置文件控制类型检查严格度、错误码启用、模块覆盖与排除规则。pyproject.toml 的 [tool.mypy] 节为现代推荐形式。
配置项说明:
| 配置项 | 类型 | 说明 |
|---|---|---|
python_version |
string | 目标 Python 版本,如 "3.10" |
strict |
bool | 启用严格模式(等价于同时启用 disallow_untyped_defs、disallow_any_unimported、no_implicit_optional、warn_return_any、warn_unused_ignores、warn_redundant_casts、disallow_subclassing_any 等子选项) |
show_error_codes |
bool | 显示错误码,便于精确抑制 |
warn_return_any |
bool | 警告返回 Any 类型的函数 |
warn_unused_configs |
bool | 警告未使用的配置节 |
warn_unused_ignores |
bool | 警告无效果的 # type: ignore 注释 |
warn_redundant_casts |
bool | 警告冗余的类型转换 |
pretty |
bool | 美化错误输出 |
check_untyped_defs |
bool | 检查无类型注解的函数体 |
disallow_untyped_defs |
bool | 禁止无类型注解的函数定义 |
disallow_any_generics |
bool | 禁止使用泛型 Any(如 list[Any]) |
disallow_any_unimported |
bool | 禁止从无类型模块导入 Any |
disallow_untyped_calls |
bool | 禁止调用无类型注解的函数 |
disallow_untyped_decorators |
bool | 禁止无类型注解的装饰器 |
disallow_subclassing_any |
bool | 禁止继承 Any 类型的类 |
no_implicit_optional |
bool | 不允许隐式 Optional(默认参数值为 None 时必须显式声明) |
exclude |
string[] | 排除的目录或文件 glob 列表 |
enable_error_code |
string[] | 启用的可选错误码列表,如 truthy-bool、ignore-without-code、redundant-expr、unused-awaitable、possibly-undefined |
[[tool.mypy.overrides]] |
list | 按模块覆盖规则,每项含 module(模块名 glob)和该模块下的配置项 |
推荐配置示例(新项目,严格模式):
适用于新项目或已完成类型注解迁移的项目,提供最高级别的类型安全保障。
# pyproject.toml
[tool.mypy]
# === 基础配置 ===
python_version = "3.10" # 目标 Python 版本
strict = true # 启用严格模式(包含以下所有子选项)
show_error_codes = true # 显示错误码,便于精确抑制
warn_return_any = true # 警告返回 Any 类型的函数
warn_unused_configs = true # 警告未使用的配置节
warn_unused_ignores = true # 警告无效果的 # type: ignore 注释
warn_redundant_casts = true # 警告冗余的类型转换
pretty = true # 美化错误输出
# === 排除目录 ===
exclude = [
"tests/",
"migrations/",
".venv/",
"build/",
"dist/",
]
# === 启用可选错误码 ===
enable_error_code = [
"truthy-bool", # 警告隐式布尔转换(如 if x: 其中 x 非 bool)
"ignore-without-code", # 要求 # type: ignore 必须指定错误码
"redundant-expr", # 警告冗余表达式
"unused-awaitable", # 警告未 await 的协程
]
strict = true 等价于同时启用以下选项:
| 选项 | 说明 |
|---|---|
disallow_untyped_defs |
禁止无类型注解的函数定义 |
disallow_any_unimported |
禁止从无类型注解的模块导入 Any 类型 |
no_implicit_optional |
不允许隐式 Optional(默认参数值为 None 时必须显式声明) |
warn_return_any |
警告返回 Any 类型的函数 |
warn_unused_ignores |
警告无效果的 type: ignore 注释 |
warn_redundant_casts |
警告冗余的类型转换 |
disallow_subclassing_any |
禁止继承 Any 类型的类 |
推荐配置示例(现有项目渐进式迁移):
适用于正在逐步引入类型注解的现有项目,在保证类型安全的同时降低迁移成本。
# pyproject.toml
[tool.mypy]
python_version = "3.10"
show_error_codes = true
warn_return_any = true
warn_unused_configs = true
warn_unused_ignores = true
# 渐进式:检查无类型注解的函数(但不强制要求有注解)
check_untyped_defs = true
# 渐进式:禁止新增无类型注解的函数
disallow_untyped_defs = true
# 排除尚未迁移的旧代码
exclude = [
"legacy/",
"migrations/",
".venv/",
]
# 旧模块暂时忽略缺失导入
[[tool.mypy.overrides]]
module = "legacy.*"
ignore_missing_imports = true
check_untyped_defs = false
# 测试文件放宽限制
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
推荐配置示例(库/框架开发):
适用于开发供他人使用的 Python 库,要求最严格的类型安全。
# pyproject.toml
[tool.mypy]
python_version = "3.10"
strict = true
show_error_codes = true
warn_return_any = true
warn_unused_configs = true
warn_unused_ignores = true
warn_redundant_casts = true
pretty = true
# 库开发额外选项
disallow_any_generics = true # 禁止使用泛型 Any(如 list[Any])
disallow_any_unimported = true # 禁止从无类型模块导入 Any
disallow_untyped_calls = true # 禁止调用无类型注解的函数
disallow_untyped_decorators = true # 禁止无类型注解的装饰器
# 启用更多可选检查
enable_error_code = [
"truthy-bool",
"ignore-without-code",
"redundant-expr",
"unused-awaitable",
"possibly-undefined",
]
5. 主流集成方式
5.1 pre-commit 集成
pre-commit 在 Python 社区中广泛用于本地提交门禁,可自动运行 mypy 检查暂存区文件。
适用前提:pre-commit 会为 mypy 创建隔离虚拟环境,因此需要通过
additional_dependencies安装项目所需的类型存根包(如types-requests)。mypy 默认以--ignore-missing-imports运行。
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v2.1.0
hooks:
- id: mypy
name: mypy-check
args: [--strict, --show-error-codes]
additional_dependencies:
- types-requests
- types-PyYAML
- pydantic
# 限制只检查 src 目录下的 Python 文件
files: ^src/
# 排除迁移目录
exclude: ^src/migrations/
安装步骤:
# 1. 安装 pre-commit
pip install pre-commit
# 2. 安装 Git 钩子
pre-commit install
# 3. 手动对所有文件运行一次检查
pre-commit run --all-files
增量检查:pre-commit 默认仅将暂存区中变更文件传给 hook,天然实现文件级增量。但由于 Mypy 的类型检查依赖 import chain,仅检查单个变更文件可能导致误报(依赖模块的类型信息缺失)。建议在 pre-commit 中使用 pass_filenames: false 并让 Mypy 检查整个项目,同时依赖 Mypy 自身的模块级增量缓存(.mypy_cache)来加速。
# .pre-commit-config.yaml(增量推荐配置)
repos:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v2.1.0
hooks:
- id: mypy
args: [--strict, --show-error-codes]
additional_dependencies:
- types-requests
- types-PyYAML
# pre-commit 默认仅传入暂存区变更文件
files: ^src/.*\.py$
5.2 IDE 集成
VS Code
VS Code 通过 Microsoft 官方 Python 扩展提供 mypy 集成支持。推荐使用 Pylance(Python 扩展自带的语言服务器)作为主类型检查器,同时通过终端或 pre-commit 运行 mypy 进行补充检查。
配置路径:.vscode/settings.json
{
// Pylance 类型检查模式(推荐)
"python.typeChecking.mode": "strict",
}
PyCharm
PyCharm 自 2023.3 版本起内置 mypy 支持。
配置路径:File > Settings > Languages & Frameworks > Python > Type Checking
- 勾选 Use mypy for type checking
- 指定 mypy 可执行路径(通常自动检测虚拟环境中的 mypy)
- 指定配置文件路径(如
$PROJECT_DIR$/pyproject.toml)
Vim/Neovim
使用 ALE(Vim/Neovim):
" .vimrc 或 init.vim
let g:ale_python_mypy_executable = 'mypy'
let g:ale_python_mypy_options = '--strict --show-error-codes'
let g:ale_linters = {'python': ['mypy']}
5.3 命令行使用方式
# 检查单个文件
mypy script.py
# 检查整个目录
mypy src/
# 严格模式检查
mypy --strict src/
# 显示错误码(推荐,便于精确抑制)
mypy --show-error-codes src/
# 指定 Python 版本
mypy --python-version 3.10 src/
# 指定配置文件
mypy --config-file pyproject.toml src/
# 生成 HTML 报告
mypy --html-report ./report src/
# 显示当前生效的配置
mypy --show-config src/
增量检查:可通过 git diff 筛选变更文件实现文件级增量,但 Mypy 也内置了模块级增量缓存机制。
基于 Git 的命令行增量方案:
# 获取相对于 main 分支的变更 Python 文件,仅对这些文件运行 mypy
mypy $(git diff --name-only --diff-filter=ACMR main... -- '*.py')
局限性:此方案仅检查变更文件本身,不检查依赖这些文件的模块,可能导致遗漏。更适合用于快速本地验证,不建议作为 CI 的唯一检查方式。
Mypy 原生增量能力(模块级):虽然 Mypy 不支持文件级增量,但其内置的模块级增量机制和守护进程模式对大型项目仍有显著的性能提升。
模块级增量缓存(默认启用):
# 首次运行(全量检查,较慢)
mypy src/
# 后续运行(增量检查,自动利用 .mypy_cache 加速)
mypy src/
dmypy 守护进程(推荐用于开发阶段):
# 启动守护进程并检查(首次运行会自动启动)
dmypy run -- src/
# 后续增量检查(极快,状态保存在内存中)
dmypy run -- src/
# 停止守护进程
dmypy stop
CI 脚本调用:
GitHub Actions
# .github/workflows/type-check.yml
name: Type Check
on:
push:
branches: [main]
pull_request:
jobs:
mypy:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mypy
pip install types-requests types-PyYAML
- name: Cache mypy
uses: actions/cache@v4
with:
path: .mypy_cache
key: mypy-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/*.py') }}
- name: Run mypy
run: mypy --strict --show-error-codes src/
GitLab CI
# .gitlab-ci.yml
type-check:
image: python:3.12-slim
stage: test
before_script:
- pip install mypy types-requests
script:
- mypy --strict --show-error-codes src/
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
增量检查:GitHub Actions 中可按 PR 变更文件触发增量检查。
# .github/workflows/type-check.yml(增量)
name: Type Check
on:
pull_request:
jobs:
mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 获取完整历史以支持 git diff
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: |
pip install mypy
pip install types-requests types-PyYAML
- name: Cache mypy
uses: actions/cache@v4
with:
path: .mypy_cache
key: mypy-${{ runner.os }}-${{ hashFiles('**/*.py') }}
- name: Run mypy on changed files
run: |
CHANGED=$(git diff --name-only --diff-filter=ACMR origin/main... -- '*.py')
if [ -n "$CHANGED" ]; then
mypy --strict --show-error-codes $CHANGED
else
echo "No Python files changed, skipping mypy."
fi
5.4 pip / uv / poetry 安装(生态主流)
pip/uv/poetry 是 Python 生态中最主流的包管理方式,也是安装 mypy 的首选途径。
# 基础安装
pip install mypy
# 安装含加速缓存的版本(需要 orjson)
pip install "mypy[faster-cache]"
# 作为开发依赖安装(推荐)
pip install -e ".[dev]"
pyproject.toml 中声明依赖:
[project.optional-dependencies]
dev = [
"mypy",
"types-requests",
]
5.5 pyproject.toml 配置(推荐)
现代 Python 项目推荐使用 pyproject.toml 作为统一配置文件。
# pyproject.toml
[tool.mypy]
python_version = "3.10"
strict = true
show_error_codes = true
warn_return_any = true
warn_unused_configs = true
warn_unused_ignores = true
exclude = [
"tests/",
"migrations/",
]
# 按模块定制规则
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
ignore_errors = true
6. 告警抑制(屏蔽)方法
Mypy 提供多层次的告警抑制机制,从行级注释到文件级注释再到全局配置,满足不同粒度的需求。
6.1 通过代码屏蔽:行级注释 # type: ignore
在代码行末添加 # type: ignore 注释,可忽略该行的所有类型错误。
import some_untyped_module # type: ignore
result = some_untyped_module.process() # 此行不会被检查
6.2 通过代码屏蔽:行级精确屏蔽 # type: ignore[error-code]
通过指定错误码,只忽略特定类型的错误,避免意外屏蔽其他有价值的检查。
from foolib import foo # type: ignore[attr-defined]
x: int = "hello" # type: ignore[assignment]
常用错误码包括:attr-defined、assignment、arg-type、return-value、union-attr、import-untyped、override 等。
6.3 通过代码屏蔽:文件级注释
忽略整个文件的所有错误:
# mypy: ignore-errors
# 此文件中的所有类型错误都会被忽略
def foo(x, y):
return x + y
忽略整个文件的特定错误码:
# mypy: disable-error-code="truthy-bool, ignore-without-code"
6.4 通过代码屏蔽:@no_type_check 装饰器
from typing import no_type_check
@no_type_check
class LegacyClass:
def process(self, data):
# 此类中的所有代码不进行类型检查
return data
6.5 通过工具配置文件屏蔽
按模块忽略错误:
# pyproject.toml
[[tool.mypy.overrides]]
module = "third_party.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "legacy_module"
ignore_errors = true
# mypy.ini
[mypy-third_party.*]
ignore_missing_imports = True
[mypy-legacy_module]
ignore_errors = True
全局禁用/启用特定错误码:
[tool.mypy]
# 全局禁用特定错误码
disable_error_code = ["import-untyped", "misc"]
# 全局启用特定错误码
enable_error_code = ["truthy-bool", "ignore-without-code", "redundant-expr"]
通过配置文件排除文件/目录:
[tool.mypy]
exclude = [
"^one\\.py$",
"two\\.pyi$",
"^three\\.",
]
6.6 通过工具命令行参数屏蔽
# 禁用特定错误码
mypy --disable-error-code import-untyped src/
# 启用特定错误码
mypy --enable-error-code truthy-bool src/
# 忽略缺失导入
mypy --ignore-missing-imports src/
# 允许未类型化的定义
mypy --allow-untyped-defs src/
# 排除匹配正则的文件/目录
mypy --exclude '/build/' src/
6.7 通过集成调度工具屏蔽
pre-commit 的文件匹配:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v2.1.0
hooks:
- id: mypy
# 仅检查 src 目录下的 .py 文件
files: ^src/.*\.py$
# 排除迁移目录
exclude: ^src/migrations/