mypy и pyright
| Тип | статические анализаторы типов |
|---|---|
| mypy | Юкка Лехтосало, с 2012; написан на Python, лицензия MIT |
| pyright | Microsoft (Эрик Траут), с 2019; написан на TypeScript, лицензия MIT |
| Основа | PEP 484 и спецификация типизации Python |
| Интеграция | VS Code (Pylance), PyCharm, Neovim, CI |
| Новые анализаторы | ty (Astral), Pyrefly (Meta) — на Rust |
mypy и pyright — два самых распространённых статических анализатора типов для Python. Они читают исходный код вместе с аннотациями типов и, не запуская программу, находят несоответствия: передачу строки туда, где ожидается число, обращение к атрибуту, которого нет, забытую проверку на None. Интерпретатор Python аннотации не проверяет, поэтому без анализатора они остаются лишь документацией.
Анализатор подключают в редакторе — ошибки подсвечиваются при наборе — и в конвейере CI/CD, чтобы код с ошибками типов не попадал в основную ветку.
История
mypy начинался в 2012 году как диссертационный проект Юкки Лехтосало: сначала это был отдельный язык с похожим на Python синтаксисом, затем — анализатор для обычного Python. Гвидо ван Россум увидел потенциал проекта, и идеи mypy легли в основу PEP 484 (2014). Позже разработку поддерживала Dropbox, где mypy проверял миллионы строк кода. mypy считается эталонной реализацией и развивается в организации python на GitHub.[2]
pyright выпустила Microsoft в 2019 году; основной автор — Эрик Траут. Он написан на TypeScript ради скорости и интеграции с редакторами и лежит в основе расширения Pylance для VS Code. Сообщество поддерживает форк basedpyright с дополнительными проверками и открытой языковой поддержкой. В 2025 году появились анализаторы на Rust — ty от Astral (авторов Ruff и uv) и Pyrefly от Meta, — делающие ставку на скорость на больших кодовых базах.
Пример
from dataclasses import dataclass
@dataclass
class User:
id: int
email: str | None
def domain(user: User) -> str:
return user.email.split("@")[1] # ошибка: email может быть None
def find(users: list[User], uid: int) -> User:
for u in users:
if u.id == uid:
return u
# ошибка: функция может завершиться без return, т. е. вернуть None
$ mypy app.py
app.py:9: error: Item "None" of "str | None" has no attribute "split" [union-attr]
app.py:11: error: Missing return statement [return]
Обе ошибки проявились бы только во время выполнения, на конкретных данных. Анализатор находит их сразу и для всех путей выполнения.
Сравнение
| mypy | pyright | |
|---|---|---|
| Язык реализации | Python (частично скомпилирован mypyc) | TypeScript (Node.js) |
| Скорость | медленнее, есть инкрементальный кэш и демон dmypy |
быстрее, рассчитан на проверку при наборе |
| Функции без аннотаций | по умолчанию не проверяет тело | выводит типы и проверяет |
| Вывод типов | консервативнее | агрессивнее, сужение типов по коду |
| Плагины | есть (Django, Pydantic, SQLAlchemy) | нет, опора на стандартные механизмы |
| Редактор | через расширения | встроен в Pylance (VS Code) |
Различия в поведении на одном и том же коде подробно описаны в документации pyright.[4] На практике многие команды используют pyright в редакторе, а в CI запускают тот анализатор, под который настроен проект.
Настройка
Оба инструмента читают настройки из pyproject.toml:
[tool.mypy]
python_version = "3.13"
strict = true # набор строгих проверок
warn_unused_ignores = true
exclude = ["migrations/"]
[[tool.mypy.overrides]]
module = ["legacy.*"]
ignore_errors = true # старый код — постепенно
[tool.pyright]
typeCheckingMode = "standard" # off | basic | standard | strict
pythonVersion = "3.13"
uv add --dev mypy pyright
uv run mypy src/
uv run pyright
Режим strict требует аннотаций у всех функций и запрещает неявный Any. В существующем проекте строгость наращивают постепенно: сначала базовый режим для всего кода, затем строгий для новых и критичных модулей.[1][3]
Полезные приёмы
reveal_type(x)— анализатор покажет, какой тип он вывел для выражения; незаменимо при отладке типов.- Сужение типов. После
if x is not None:илиisinstance(x, str)анализатор знает, что тип стал уже. Для своих проверок естьTypeGuardиTypeIs. # type: ignore[код-ошибки]— точечно подавить ошибку, указав её код;warn_unused_ignoresнапомнит удалить устаревшие подавления.- Заглушки типов. Для библиотек без аннотаций ставят пакеты
types-*из typeshed — общего репозитория аннотаций для стандартной библиотеки и популярных пакетов.[6] py.typed. Библиотека, распространяющая встроенные аннотации, кладёт в пакет пустой файл-маркерpy.typed.
Ограничения
Статический анализ не заменяет тесты: он проверяет согласованность типов, но не логику. Динамические приёмы — getattr по строке, monkey-patching, метаклассы — анализатор понимает плохо, а Any выключает проверку для всего, что через него проходит. Для проверки данных извне — JSON из API, конфигурации — нужны проверки во время выполнения, например Pydantic (см. Dataclasses и Pydantic). Общие правила для всех анализаторов собраны в документации проекта typing.[5]