mypy и pyright

Материал из llmdocs — справочник разработчика
Портал: Python
mypy и pyright
Типстатические анализаторы типов
mypyЮкка Лехтосало, с 2012; написан на Python, лицензия MIT
pyrightMicrosoft (Эрик Траут), с 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]

См. также

Источники

  1. mypy documentation
  2. python/mypy — GitHub
  3. Pyright documentation
  4. Pyright — Differences between Pyright and mypy
  5. Static Typing with Python — typing.python.org
  6. python/typeshed — GitHub