Как стать автором
Обновить
2658.05
RUVDS.com
VDS/VPS-хостинг. Скидка 15% по коду HABR15

Путь к проверке типов 4 миллионов строк Python-кода. Часть 3

Время на прочтение 6 мин
Количество просмотров 6.7K
Автор оригинала: Jukka Lehtosalo
Представляем вашему вниманию третью часть перевода материала о пути, который прошла компания Dropbox, внедряя у себя систему проверки типов Python-кода.



→ Предыдущие части: первая и вторая

Достижение 4 миллионов строк типизированного кода


Ещё одной важной задачей (это был вторая по популярности проблема, волновавшая тех, кто участвовал во внутренних опросах) было увеличение объёма кода в Dropbox, покрытого проверками типов. Мы испробовали несколько подходов для решения этой задачи — от естественного роста объёма типизированной кодовой базы до сосредоточения усилий членов команды mypy на статическом и динамическом автоматизированном выводе типов. В итоге создалось такое впечатление, что тут нет простой выигрышной стратегии, но мы смогли достичь быстрого роста объёма аннотированного кода, скомбинировав множество подходов.

В результате в нашем самом большом Python-репозитории (с бэкенд-кодом) число строк аннотированного кода достигло почти 4 миллионов. Работа по статической типизации кода была проведена примерно за три года. Mypy теперь поддерживает различные виды отчётов о покрытии кода типами, которые упрощают наблюдение за ходом типизации. В частности, мы можем формировать отчёты по коду с неопределённостями в типах, с такими, например, как явное использование типа Any в аннотациях, которые невозможно проверить, или с такими, как импорт сторонних библиотек, в которых нет аннотаций типов. Мы, в рамках проекта по повышению точности проверки типов в Dropbox, внесли вклад в улучшении определений типов (так называемых stub-файлов) для некоторых популярных опенсорсных библиотек в централизованном Python-репозитории typeshed.

Мы реализовали (и стандартизировали в последующих PEP) новые возможности системы типов, которые позволяют использовать более точные типы для некоторых специфичных Python-паттернов. Заметным примером этого является TypeDict, который предоставляет типы для JSON-подобных словарей, имеющих фиксированный набор строковых ключей, каждый из которых имеет значение собственного типа. Мы продолжим расширять систему типов. Вероятно, нашим следующим шагом станет улучшение поддержки возможностей Python по работе с числами.


Количество строк аннотированного кода: сервер


Количество строк аннотированного кода: клиент


Общее количество строк аннотированного кода

Вот обзор основных особенностей тех действий, которые мы выполнили ради повышения объёма аннотированного кода в Dropbox:

Строгость аннотирования. Мы постепенно повышали требования по строгости аннотирования нового кода. Мы начали с советов линтера, в которых предлагалось добавлять аннотации в файлы, в которых уже есть некоторые аннотации. Теперь мы требуем наличия аннотаций типов в новых Python-файлах и в большинстве существующих файлов.

Отчёты о типизации. Мы еженедельно рассылаем командам отчёты об уровне типизации их кода и даём советы, касающиеся того, что именно стоит аннотировать в первую очередь.

Популяризация mypy. Мы рассказываем о mypy на различных мероприятиях и общаемся с командами, помогая им начать пользоваться аннотациями типов.

Опросы. Мы проводим периодические опросы пользователей для выявления главных проблем. Мы готовы зайти достаточно далеко в деле решения этих проблем (вплоть до создания нового языка ради ускорения mypy!).

Производительность. Мы значительно улучшили производительность mypy благодаря использованию демона и mypyc. Сделано это ради сглаживания неудобств, возникающих в процессе аннотирования, и ради того, чтобы получить возможность работать с большими объёмами кода.

Интеграция с редакторами. Мы создали средства для поддержки запуска mypy в редакторах, которые пользуются популярностью в Dropbox. Сюда входят PyCharm, Vim и VS Code. Это значительно упростило процесс выполнения работ по аннотированию кода и по проверке его работоспособности. Подобные действия обычно характерны при аннотировании существующего кода.

Статический анализ. Мы создали инструмент для вывода сигнатур функций с использованием средств статического анализа. Этот инструмент может работать только в сравнительно простых ситуациях, но он помог нам без особых усилий увеличить покрытие кода типами.

Поддержка сторонних библиотек. Во многих наших проектах используется набор инструментов SQLAlchemy. В нём применяются динамические возможности Python, которые типы PEP 484 неспособны смоделировать напрямую. Мы, в соответствии с PEP 561, создали соответствующий stub-файл и написали плагин для mypy (опенсорсный), улучшающий поддержку SQLAlchemy.

Сложности, с которыми мы встретились


Путь к 4 миллионам строк типизированного кода не всегда давался нам легко. На этом пути мы встретили немало ям и сделали несколько ошибок. Вот некоторые из проблем, с которыми мы столкнулись. Надеемся, рассказ о них поможет другим подобных проблем избежать.

Пропущенные файлы. Мы начинали работу с проверки лишь небольшого объёма файлов. Всё, не входящее в число этих файлов, не проверялось. Файлы в список проверки добавлялись тогда, когда в них появлялись первые аннотации. Если что-то импортировалось из модуля, расположенного за пределами области проверки, то речь шла о работе со значениями типа Any, которые вообще не проверялись. Это привело к значительной потере точности типизации, особенно — на ранних стадиях миграции. Такой подход до сих пор работал на удивление хорошо, хотя типичной была ситуация, когда добавление файлов в область проверки выявляло проблемы в других частях кодовой базы. В самом худшем случае, когда объединялись две изолированных области кода, в которых, независимо друг от друга, типы были уже проверены, оказывалось, что типы этих областей несовместимы друг с другом. Это приводило к необходимости внесения в аннотации множества изменений. Теперь, оглядываясь назад, мы понимаем, что нам следовало бы как можно раньше добавить в область проверки типов mypy базовые библиотечные модули. Это сделало бы нашу работу гораздо более предсказуемой.

Аннотирование старого кода. Когда мы начинали работу, у нас было около 4 миллионов строк уже существующего Python-кода. Было ясно, что аннотирование всего этого кода — задача не из лёгких. Мы создали инструмент, названный PyAnnotate, который может собирать сведения о типах во время выполнения тестов и умеет добавлять в код аннотации типов, основываясь на собранных сведениях. Однако особенно широкого внедрения этого инструмента мы не заметили. Сбор сведений о типах был медленным, автоматически сгенерированные аннотации часто требовали множества ручных правок. Мы думали об автоматическом запуске этого средства при каждой проверке кода, или о том, чтобы собирать сведения о типах, основываясь на анализе некоего небольшого объёма реальных сетевых запросов, но решили этого не делать, так как любой из этих подходов слишком рискован.

В итоге можно отметить, что большая часть кода была вручную аннотирована его владельцами. Мы, для того, чтобы направлять этот процесс в правильное русло, готовим отчёты по особо важным модулям и функциям, которые нужно аннотировать. Например, важно снабдить аннотациями типов библиотечный модуль, используемый в сотнях мест. А вот старый сервис, который заменяют новым, аннотировать уже не так важно. Мы, кроме того, экспериментируем с использованием статического анализа для генерирования аннотаций типов для старого кода.

Циклические импорты. Выше я говорил о циклических импортах (о «клубках зависимостей»), существование которых усложнило ускорение mypy. Нам, кроме того, пришлось серьёзно поработать над тем, чтобы снабдить mypy поддержкой всех видов идиом, причиной возникновения которых являются эти вот циклические импорты. Недавно мы завершили крупный проект по редизайну системы, который исправил большинство проблем mypy, касающихся циклических импортов. Эти проблемы, на самом деле, произрастали из весьма ранних дней проекта, ещё из Alore, учебного языка, на который был изначально ориентирован проект mypy. Синтаксис Alore позволяет легко решать проблемы циклических команд импорта. Современный mypy унаследовал некоторые ограничения от своей ранней бесхитростной реализации (которая отлично подходила для Alore). Python усложняет работу с циклическими импортами в основном из-за неоднозначности выражений. Например, в ходе операции присваивания значения может, на самом деле, определяться псевдоним типа. Mypy не всегда способен выявлять подобные вещи до тех пор, пока большая часть цикла импорта не будет обработана. В Alore таких неоднозначностей не было. Неудачные решения, принятые на ранних этапах разработки системы, могут преподнести программисту неприятный сюрприз через много лет.

Итоги: путь к 5 миллионам строк кода и к новым горизонтам


Проект mypy прошёл долгий путь — от ранних прототипов, до системы, средствами которой контролируются типы продакшн-кода объёмом в 4 миллиона строк. По ходу развития mypy была осуществлена стандартизация подсказок по типам в Python. В наши дни вокруг типизации Python-кода развилась мощная экосистема. В ней нашлось место поддержке библиотек, в ней присутствуют вспомогательные средства для IDE и редакторов, в ней имеются несколько систем контроля типов, у каждой из которых есть свои плюсы и минусы.

Несмотря на то, что проверка типов уже воспринимается в Dropbox как нечто само собой разумеющееся, я уверен в том, что мы всё ещё живём на заре типизации Python-кода. Я думаю, что технологии проверки типов будут продолжать развиваться и совершенствоваться.

Если вы ещё не пользовались проверками типов в своём крупномасштабном Python-проекте, то знайте, что сейчас — весьма подходящее время для того, чтобы начать переход на статическую типизацию. Я беседовал с теми, кто совершил подобный переход. Никто из них об этом не пожалел. Контроль типов превращает Python в язык, который гораздо лучше, чем «обычный Python», подходит для разработки больших проектов.

Уважаемые читатели! Пользуетесь ли вы контролем типов в своих Python-проектах?


Теги:
Хабы:
+30
Комментарии 12
Комментарии Комментарии 12

Публикации

Информация

Сайт
ruvds.com
Дата регистрации
Дата основания
Численность
11–30 человек
Местоположение
Россия
Представитель
ruvds