Поддерживайте чистый код Python с помощью Black и GitHub Actions
Написать код достаточно сложно, но обеспечить его хороший формат и легкость чтения может быть еще более сложной задачей.
За десять лет наши навыки кодирования значительно улучшились. Но большинство из них - это не какое-то причудливое использование API или что-то в этом роде. Это то, как мы форматируем код.
В свои ранние годы мы кодировали результат. Конечно, это конечная цель каждого программиста. Но по мере продвижения становилось понятным, что хорошее кодирование — это гораздо больше, чем просто выполнение задач.
Было нелегко поделиться своими кодами с другими и не получить кучу вопросов, чтобы объяснить их. Другим программистам было очень сложно переварить наши коды, даже с помощью вспомогательных документов и встроенных комментариев.
Работа программиста не заканчивается на рабочем коде.
Помимо документации, есть кое-что, что делает хороший код отличным. И сообщество программистов не преминуло выяснить, что это такое.
Это то, как мы форматируем наш код.
Python, наш любимый язык программирования, имеет самый простой синтаксис. Но это не гарантирует, что все, кто читает ваш код на Python, поймут его.
По этой причине у нас есть руководство по стилю, известное как PEP 8. Этот стандарт позволяет каждому программисту кодировать в одном стиле. Если все сделано правильно, начинающий программист может потратить меньше времени на выяснение, какая строка к чему относится.
Вот пример кода. Первый - это то, как я бы закодировал его в начале своей карьеры. Это делает свою работу. Он считывает файл и обучает модель.
На приведенном выше изображении вы можете видеть, что только половина нашего кода помещается на экране. Нам пришлось бы прокручивать вправо и влево, чтобы прочитать и понять код. Везде есть нежелательные пустые строки и пробелы, в то время как там, где они должны быть, нет пустой строки.
И вот как это будет выглядеть после внедрения руководства по стилю. Этот вариант легче понять, не так ли?
Но есть одна проблема. Запомнить руководство по стилю и заставить себя закодировать его таким образом требует много работы. Мы все еще хотели потратить больше времени на выполнение этой работы.
Было бы полезно, если бы кто-то другой позаботился о форматировании кода. Вот тут-то и появляется черный цвет. Black - это пакет Python для форматирования вашего кода в соответствии с предопределенным руководством по стилю в одной команде. Это руководство может быть PEP 8, или вы можете настроить его под свою организационную версию.
Современные редакторы кода обычно поставляются с поддержкой форматирования документов. Например, в VS Code вы можете щелкнуть правой кнопкой мыши на редакторе и выбрать форматирование кода. Если установлен Black, он сразу же отформатирует ваш код.
Все эти улучшения на этом не остановились. Вы можете автоматизировать форматирование кода с помощью крючков предварительной фиксации и не беспокоиться об этом во время кодирования. Когда вы вносите изменения, они форматируют все ваши файлы python.
Последний недостающий кусочек головоломки - это вот что. Даже крючки предварительной фиксации запускаются на компьютерах разработчиков. Работая в команде, вы по-прежнему полагаетесь на них при форматировании.
Что, если вы можете сделать это централизованно, независимо от того, кто является разработчиком и как они это сделали?
На этом и сосредоточен этот пост. Для этого мы используем действия GitHub.
Когда разработчики регистрируются в центральном репозитории, их код автоматически приводится в соответствие со стандартами организации. Действия GitHub запускают рабочий процесс всякий раз, когда мы вносим изменения в репозиторий. Мы можем настроить его для выполнения команды форматирования блока.
Настройте действия Black на GitHub для автоматического форматирования кода Python
Чтобы использовать black
для форматирования вашего кода Python при внесении изменений с помощью GitHub Actions, вы можете выполнить следующие действия:
Во-первых, убедитесь, что в вашем проекте установлен black
. Вы можете сделать это, добавив black
к requirements.txt
файл в вашем проекте или запустив pip install black
в виртуальной среде вашего проекта.
Затем создайте новый файл в каталоге .github/workflows
вашего проекта под названием format.yml
. Этот файл будет содержать конфигурацию для вашего рабочего процесса GitHub Actions.
В файле format.yml
вы можете определить рабочий процесс, который выполняется черным цветом в вашем коде всякий раз, когда вы вносите изменения в свой репозиторий. Вот пример рабочего процесса, который делает это:
name: Format code
on:
push:
branches: [ master ]
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Format code with black
run: |
pip install black
black .
- name: Commit changes
uses: EndBug/add-and-commit@v4
with:
author_name: ${{ github.actor }}
author_email: ${{ github.actor }}@users.noreply.github.com
message: "Format code with black"
add: "."
branch: ${{ github.ref }}
Этот рабочий процесс будет выполняться всякий раз, когда вы вносите изменения в главную ветвь вашего репозитория.
Он устанавливает black
, а затем запускает команду black
для всего проекта (.
в конце black
команды указывается текущий каталог). Наконец, он фиксирует новые изменения в той же ветке.
После того как вы определили свой рабочий процесс, вы можете зафиксировать изменения в своем репозитории и отправить их на GitHub. Когда вы это сделаете, рабочий процесс запустится автоматически, отформатировав ваш с black
.
Исключение и включение файлов и папок для форматирования
Существует проблема с вышеуказанной конфигурацией. Он просматривает все файлы и папки в нашем репозитории и пытается их отформатировать. Иногда мы хотим избежать этого для определенных файлов.
У black
есть опции для настройки его поведения при форматировании.
В следующем примере мы настроили black
так, чтобы он игнорировал любые файлы внутри папки ref.
- name: Format code with black
run: |
pip install black
black . --exclude="env/*"
Это приведет к запуску команды black
для всех файлов в текущем каталоге, за исключением файлов в папке env
.
По умолчанию black
отформатирует все файлы .py
, .pyi
и .ipynb
. В качестве альтернативы вы можете указать список файлов для включения напрямую, используя флаг --include
, например:
- name: Format code with black
run: |
pip install black
black --include="\.py" .
Это приведет к запуску команды black
только для файлов с расширением .py
в текущем каталоге.
Да, вы можете использовать оба флага --include
и --exclude
вместе, чтобы указать более сложный шаблон файлов для включения или исключения.
- name: Format code with black
run: |
pip install black
black --include="\.py" --exclude="env/*" .
Это приведет к запуску команды black
для всех файлов с расширением .py
в текущем каталоге, за исключением файлов в папке env
.
Обратите внимание, что флаг--include
имеет приоритет над флагом--exclude
, поэтому, если файл соответствует обоим шаблонам, он будет включен. Таким образом, будьте особенно осторожны, когда включаете шаблоны. Слепое включение шаблонов, как в приведенном выше примере, приведет к тому, чтоblack
также отформатирует файлы в вашей папкеenv
.
Вы можете указать несколько шаблонов как для --include
, так и для --exclude
, разделив их запятой, например:
- name: Format code with black
run: |
pip install black
black --include="\.py,\.pyi" --exclude="env/*,tests/*" .
Это приведет к запуску команды black
для всех файлов с расширением .py
или .pyi
в текущем каталоге, за исключением файлов в папках env
или tests
.
Еще один полезный способ управления файлами, подлежащими исключению, - это ваш файл .gitignore
. Возможно, у вас уже есть один в вашем проекте. Если файл соответствует каким-либо шаблонам в .gitignore
, эти файлы будут проигнорированы при форматировании.
Если вам нужно использовать оба варианта поведения .gitignore
и exclude
, согласно официальным документам, вам нужно использовать --extend-exclude
вместо --exclude
.
Расширение рабочего процесса с помощью более чистых инструментов кода
Black - один из важнейших инструментов для форматирования кода Python. Это заботится о многих вещах. Но у нас есть и другие важные вещи.
Одним из аспектов чистого кодирования является логическая сортировка импорта. Но еще раз, вам не нужно беспокоиться об этом. Вы можете использовать вставку пакета с вашим рабочим процессом GitHub для автоматической обработки этого.
Удаление избыточных или неиспользуемых переменных - еще одно качество хорошей кодовой базы. Многие идеи в настоящее время автоматически выделяют такие новые переменные. Но autoflake8 также может автоматически удалять их.
Вот готовая конфигурация действий на GitHub:
name: Format code
on:
push:
branches: [ master ]
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Format code with black
run: |
pip install black
black .
- name: Sort imports with isort
run: |
pip install isort
isort .
- name: Remove unused imports with autoflake
run: |
pip install autoflake
autoflake --in-place --remove-all-unused-imports --remove-unused-variables --recursive .
- name: Commit changes
uses: EndBug/add-and-commit@v4
with:
author_name: ${{ github.actor }}
author_email: ${{ github.actor }}@users.noreply.github.com
message: "Format code with black"
add: "."
branch: ${{ github.ref }}
Когда все идет не так, как мы хотим
Независимо от всех наших усилий, могут быть случаи, когда этот автоматический рефакторинг кода может завершиться неудачей.
В хорошей среде разработки этого следует избегать. Большинство разработчиков тестируют свой код локально, прежде чем отправлять его в Git, потому что это позволяет им выявлять любые ошибки или проблемы с кодом перед развертыванием.
Локальное тестирование также позволяет разработчикам вносить необходимые изменения и гарантировать, что код функционирует правильно, прежде чем он будет запущен в эксплуатацию, экономя время и ресурсы в долгосрочной перспективе.
Из-за этого такие проблемы, как опечатки и другие незначительные вещи, обычно приводящие к сбою рефакторинга кода, не будут проблемой. Но когда они случаются, мы должны быть готовы.
Приведенная ниже конфигурация будет отправлять уведомление по электронной почте всякий раз, когда наш рефакторинг кода завершается неудачей.
- name: Notify errors
if: failure()
uses: dawidd6/action-send-mail@v2
with:
server_address: smtp.gmail.com
server_port: 465
username: ${{ secrets.EMAIL_USERNAME }}
password: ${{ secrets.EMAIL_PASSWORD }}
subject: "Format code with black"
body: "Format code with black failed"
to: ${{ secrets.EMAIL_TO }}
from: ${{ secrets.EMAIL_FROM }}
content_type: text/plain
Вышеупомянутое расширение конфигурации использует пользовательское действие GitHub, созданное Дэвидом Дзирлой.
Заключение
Никому не нужен беспорядочный код, но у некоторых хватает терпения его почистить.
У каждого сообщества программистов есть руководство по стилю, обеспечивающее читаемость кода. Но это первое препятствие в решении проблемы. Нам нужно сделать форматирование кода легким, чтобы каждый разработчик мог часто им пользоваться. Пакеты, подобные Black, делают это.
Но то, что это делается без усилий, не означает, что им пользуются все. Крючки предварительной фиксации автоматизируют процесс, но по-прежнему полагаются на разработчика. В этом посте представлен способ централизованного автоматического форматирования.
Black с помощью GitHub Actions может гарантировать, что код всегда отформатирован. Вы можете централизованно диктовать руководство по стилю. Это может быть PEP 8 или ваш собственный. И когда они по какой-либо причине выходят из строя, вы также можете получить уведомление.