Python Дайджест: как актуализировать всю кодовую базу с помощью pre-commit

от автора

Продолжаю рассказывать, как open source проект Python Дайджест спустя 5 лет без обновлений удалось актуализировать по всему стэку технологий. В первой части рассказал, как удалось outdated проект с Python 3.4 обновить до Python 3.11 и Django 4.1.

В этой части расскажу, как удалось максимально дешево привести кодовую базу в актуальное состояние.

Содержание цикла статей

Состояние после обновления до Python 3.11

  • Приложение запускается, тесты проходят.
  • Код написан для работы с Python 3.4 и имеет вставки для поддержки Python 2.7.
  • Куча неиспользуемых импортов, переменных, код не соблюдает даже части PEP8, форматирование строк сильно прыгает.
  • Нет времени и какого-либо желания руками переписывать код для поддержки 3.6+ фич.

Задача (часть 2) — перейти от ручного форматирования кода к автоматизированному

Приступим к решению.

План работ

  • Подобрать автоформатер, который не ломает код
  • Настроить автозапуск перед коммитом
  • Отформатировать код

Зачем форматируем код

Исходный код — текстовое представление замысла разработчика. Можно понять смысл программы и на COW-language, только потребуется больше усилий, да и меньшее количество коллег захотят это делать. Человеку проще запомнить один способ чтения исходного кода программы, чем под каждый новый файл перенастраивать себя и свои глаза.

Начинающие специалисты еще не привыкли, что перечитывать код нужно чаще, чем писать его. Поэтому склонны к оформлению в стиле «как пойдет». И сложность не столько в нестандартном оформлении, сколько в очень частом изменении стиля. После года-двух специалист уже может держать один стиль дольше и жить становится проще.

В командах у специалистов разный уровень и, чтобы получить единый стиль у всех, выбирается единый code style. И дальше возникает вопрос — как его проверять или выполнять. Желательно автоматически.

Как делать автоформатирование кода

Когда говорим «автоформатирование Python кода», то сразу вспоминается black/autopep8/flake8/isort/redbaron/yapf и инструменты на их базе. Это инструменты, которые позволяют привести код к единому формату. Про них есть статьи на habr — раз, два, три, а sobolevn сделал много докладов на эту тему.

Одни инструменты редактируют только 1 вид кода, например, isort — импорты. Другие инструменты редактируют весь код на Python, а иногда могут применяться и для других языков.

В Python Дайджест нет «исторически сложившихся» ограничений, связанных с кодом, командой и процессом разработки, поэтому автоформатировать хочется все сразу и без лишних настроек. Значит, основным форматтером будет black, а остальные будут дополнять его.

Как обновить код под новую версию Python

Синтаксис Python расширяется/дорабатывается с каждой версией, меняются API даже стандартных библиотек, появляются конструкции, которые выглядят понятнее, устанавливаются более корректные значения «по умолчанию» для функций. Все это можно читать в changelog’ах к версиям Python.

Появление f-string в 3.6 — это наиболее заметное изменение в форматировании строк. Строки стали выглядеть легче и понятнее. При этом пройтись по всей кодовой базе и .format переделать на f-string — занятие на много часов. Подобные изменения можно делать автоматически.

Если раньше активно применяли six и future flags для плавного обновления кода, то теперь можно использовать pyupgrade.

pyupgrade — утилита, которая может обновить код с версии X до версии Y. Утилита аккуратно обновляет «старые» конструкции на «новые». Работает хорошо и не ломает код, поэтому можно запускать при каждом коммите.

Чем запускал скрипты форматирования — pre-commit

Раз инструменты есть, теперь нужно научиться не забывать их запускать. Для этого хорошо подходит концепт с pre-commit git hooks, чтобы непосредственно перед коммитом запускать форматирование и корректировать неточности. Общеупотребимой реализацией концепции является проект pre-commit.

pre-commit — это расширяемый (за счет hook/плагинов) способ запускать различные скрипты перед коммитом.

Про него на Хабре писали здесь и здесь.

Этот проект позволяет настроить автоматическую проверку чего только вздумаешь — можете самостоятельно ознакомиться со страницей плагинов.

Из всего перечня плагинов мне нужны:

  • Форматирование кода с помощью black, удаление неиспользуемых импортов с isort и autoflake
  • Поиск случайно оставленных секретов/паролей в коде
  • Валидация всех конфигурационных файлов (JSON, YAML), в том числе для CI
  • Поиск устаревших пакетов на основе dependabot
  • Возможность автоматически сконвертировать код до Python 3.11 версии с помощью pyupgrade

Это все умеют плагины pre-commit. Вот конфигурация, которая получилась — .pre-commit-config.yaml, pyproject.yaml

Как настроил pre-commit

Для регулярного запуска я выбрал такие плагины для pre-commit:

# copy-paste .pre-commit-config.yaml # .... exclude: "^docs/|/migrations/" default_stages: [commit]  repos:   - repo: https://github.com/pre-commit/pre-commit-hooks     rev: v4.4.0     hooks:       - id: trailing-whitespace       - id: end-of-file-fixer       - id: check-yaml       - id: check-merge-conflict       - id: detect-private-key       - id: debug-statements    - repo: https://github.com/asottile/pyupgrade     rev: v3.3.1     hooks:       - id: pyupgrade         args: [--py311-plus]    - repo: https://github.com/PyCQA/isort     rev: 5.12.0     hooks:       - id: isort    - repo: https://github.com/psf/black     rev: 23.1.0     hooks:       - id: black         exclude: ^.*\b(migrations)\b.*$    -   repo: https://github.com/PyCQA/autoflake       rev: v2.0.1       hooks:       -   id: autoflake    - repo:  https://github.com/python-jsonschema/check-jsonschema     rev: 0.21.0     hooks:       - id: check-github-workflows       - id: check-dependabot    - repo: https://github.com/pre-commit/pygrep-hooks     rev: v1.10.0     hooks:       - id: python-use-type-annotations       - id: python-check-blanket-noqa    -   repo: https://github.com/Yelp/detect-secrets       rev: v1.4.0       hooks:       -   id: detect-secrets           args: ['--baseline', '.secrets.baseline']           exclude: package.lock.json

А чтобы black и isort не ссорились — внес такие изменения в конфигурацию проекта.

# copy isort/black settings in pyproject.toml # ...  [tool.black] line-length = 119 target-version = ['py311'] include = '\.pyi?$'  [tool.isort] profile = "black" line_length = 119 multi_line_output = 3 include_trailing_comma = true use_parentheses = true ensure_newline_before_comments = true

Как использовал pre-commit

Чтобы использовать выполняем:

# install poetry add pre-commit --group dev pre-commit install  # run check pre-commit run --show-diff-on-failure --color=always --all-files

Последнюю строку можно поместить в Makefile с названием check и руками запускать.

Установив pre-commit, теперь перед формированием коммита, выполнятся все скрипты.

Что отформатировал в коде

Сразу после первого запуска скрипты смогли изменить:

  • Удалены строки from __future__ import unicode_literals и # -*- encoding: utf-8 -*-.
    - # -*- coding: utf-8 -*- - from __future__ import unicode_literals
  • Скорректировано формирование строк — двойные кавычки для строк и f-string.

    - name = '{} - {}'.format(package.name, package_version)  + name = f"{package.name} - {package_version}"

  • Исправлено форматирование множественных импортов — теперь импорты организованы в группы — системные, django и из приложения.
  • Удалены неиспользуемые/модифицированы импорты

    - from mock import patch  + from unittest.mock import patch

  • Использована корректная форма Dictionary Comprehension, вместо преобразования через dict.

    - headers = dict((n, n) for n in fieldnames)  + headers = {n: n for n in fieldnames}

  • Удалены/добавлены переносы строк, где требуется.

    - list_filter = ('is_activated', 'if_element', 'if_action', 'then_element', 'then_action',)  + list_filter = (     "is_activated",     "if_element",     "if_action",     "then_element",     "then_action", )

И теперь разработчик может писать код как угодно — форматтеры приведут все в порядок.

Прелесть pre-commit, что его можно запускать в CI (про это в следующей части) и таким образом получать дополнительную проверку на соответствие формата кода. И тогда тот, кто «забыл» поставить себе pre-commit или предпочитает кодить из браузера, не сможет залить некачественное изменение на сервер.


Выводы

  • Современные автоформатеры Python кода уже не склонны ломать код и можно их использовать активнее (не забудьте настроить свой формат).
  • pre-commit — это проект, который заметно уменьшает вероятность залить в репозиторий не то или не так. Готовых плагинов очень много и каждый для своей команды найдет, что взять оттуда. Форматирование кода и поиск секретов — берите точно.


    НЛО прилетело и оставило здесь промокод для читателей нашего блога:
    15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.


ссылка на оригинал статьи https://habr.com/ru/company/first/blog/721732/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *