Грустное про обратную совместимость
Jan 31, 2021 · CommentsПрограммирование
На Hacker News давече разгорелась очередная дискуссия про трудности перехода со второго Питона на третий. Этот случай, без сомнения, должен войти в учебники как пример того, как делать ни в коем случае не надо. Очень показательно, что официальное руководство по переходу ссылается на две статьи: Питон 3: вопросы и ответы и Почему Питон 3 существует. Обе статьи признают, что переход создал множество проблем, но при этом многословно объясняют почему третий Питон был необходим. Обе статьи были бы гораздо честнее, если бы авторы просто сказали “Простите. Мы ошиблись”.
Мое мнение по этому поводу можно свести к двум тезисам:
- Питон 3 не предложил ничего, что оправдало бы обратную несовместимость со вторым Питоном.
- 100% покрытие кода тестами - необходимое условие для того, чтобы безболезненно переписывать код на Питоне.
Чтобы проиллюстрировать это мнение, я собрал немного статистики по миграции небольшого проекта на 38 тысяч строк. Проект состоит из коллекции тестов, проверяющих поведение операционной системы на целевой платформе, и обвязки, которая позволяет эти тесты запускать на железе. Код проекта покрыт юнит тестами плохо. Юнит тесты занимают всего около двух с половиной тысяч строк. Это имеет рациональное объяснение - покрывать код тестов тестами вроде как особого смысла нет. Но на легкость переписывания кода это, само собой, влияет.
Основной перевод кода с py2 на py2+py3 начался в июле прошлого года и занял ровно две недели, 14 пул-реквестов и примерно чуть более 5% измененного кода. Если включить автоматическое переформатирование кода, то дельта увеличивается до 30% кода. Для сравнения, в два раза более крупное, похожее по характеру изменение в проекте на С++ (150 тысяч строк кода) заняло 4 дня от начала до конца.
Здесь кстати есть еще один характерный момент. Переход на третий Питон - это почти всегда переход на код, совместимый с обоими версиями сразу. Просто потому, что невозможно перевести сколь-нибудь крупный проект за раз. Но, как мы помним, эти версии умышленно несовместимы друг с другом. Так что костыли вроде six просто необходимы.
Какие же изменения были сделаны за эти две недели? Да все же тот стандартный список:
- Байты и строки.
- Целочисленное деление.
int
vslong
.print
стал функцией.- Некорректные управляющие последовательности в строках.
- Отсутствующий
sys.maxint
. - Отсутствующий
xrange
.
В случае типизированного языка, все эти несовместимости ловятся на этапе компиляции. В конце концов, компилятор компилирует 100% кода программы (слава богу SFINAE в нормальных программах используется более-менее локально). Единственный способ надежно отловить все ошибки компиляции в Питоне можно только выполнив каждую строку кода в программе. Я еще ни разу в жизни не видел ни одного реального программного проекта со 100% покрытием тестами.
Обидно, что даже самое полезное новшество - раздельные строки и байты не требует ломать
обратную совместимость. Ну совсем. Что нужно было сделать? - дать возможность явно
указать, ожидает ли код байты или строки в новом коде, а старый код обложить
диагностикой, которая ловит ошибки. Добавить специальный режим проверок во время
исполнения, если требуется. Что сделали разработчики Питона? Переименовали str
в
bytes
, а unicode
в str
. Чем сломали ожидания огромного массива существующего кода.
Однако авторам этого показалось мало и они добавили еще кучу тривиальных, и совсем
ненужных несовместимостей. К примеру убрали iteritems()
или убрали оператор print
.
Что мешало оставить первый и позволить второму быть и оператором и функцией - это выше
моего понимания.
Но если вы думаете, что за две недели эпопея закончилась, вы сильно ошибаетесь. Это был только первый тикет в баг-трекере. Спустя полгода их заведено уже 15. Заслано 33 пул-реквеста. Крайний и видимо не последний баг, вызванный переходом, был найден неделю назад. Как минимум три тикета еще предстоит исправить. Вот такие дела.