Оставайтесь с нами, чтобы узнать о лучших хуках предварительной фиксации для python и MLOps ниже.

Если вы применяете передовые методы CI и развертывания, скорее всего, вы используете один или два линтера. Линтеры изначально использовались для проверки исходного кода на наличие программных и стилистических ошибок. Когда код не придерживался всех протоколов в линтере, он проходил успешно. С годами линтеры становились все более и более сложными, и, если честно, они немного вышли из-под контроля в хорошем смысле. В какой-то момент разработчики поняли, что полезный способ использования линтеров заключается в том, чтобы запускать их до того, как вы на самом деле зафиксируете код. Это гарантирует, что «плохой» код никогда не попадет в репозиторий, и вуаля: родился хук перед фиксацией.

В настоящее время хуки перед фиксацией используются по-разному, но в основном двумя способами:

  1. ) Запуск проверок перед тем, как код будет разрешен в локальных и/или удаленных репозиториях.
  2. ) Выполнение проверок кода, проходящего через конвейер

В обоих случаях обычно бывает так, что если не все хуки (проверки кода) проходят успешно, то код не пропускается в репо или через пайплайн. Конечно, есть много разных вариантов использования и исключений, о которых стоит подумать, но общая идея такова: если наши тесты не пройдут, мы остановим процесс. И что может быть лучше, чем обширная экосистема с открытым исходным кодом, состоящая из тысяч хуков и кодовых протоколов, автоматически проверяющих наш код. Убедитесь, что он непротиворечив, удобочитаем, безопасен и соответствует стандартам и передовым практикам. Лично я ничего не мог придумать, обожаю я какие-то предкоммитные хуки! И, мальчик, нам повезло, что мы живем в этот день и в эпоху, когда, кажется, для всего есть крючок перед фиксацией.

Ну, не все, но предложение удивительно богатое и постоянно растущее. Кроме того, каждый уважающий себя крючок имеет множество вариантов конфигурации, что позволяет адаптировать их к любым потребностям и позволяет создавать сотни тысяч комбинаций. С таким количеством вариантов, какие крючки вы должны использовать? И с чего начать? Чтобы помочь вам усовершенствовать свои методы CI, мы выбрали одни из лучших и простых в использовании хуков для Python и MLOps. Чтобы разобраться в доступных типах хуков, мы разделили их на пять категорий: ограждения, средства форматирования, средства проверки кода, корректоры кода и помощники git.

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

Ограждения

Крючки Guard Rails направлены на обеспечение соблюдения определенных правил и мер безопасности для обеспечения качества кода, безопасности и соблюдения лучших практик. Они включают такие проверки, как проверка форматов файлов, проверка синтаксиса и проверка исполняемого скрипта. Эти крючки помогают выявить потенциальные проблемы на ранней стадии и обеспечивают защиту от распространенных ошибок или уязвимостей. Некоторые из наших любимых крючков для ограждения:

  • check-ast: проверяет синтаксис и структуру кода Python с помощью абстрактного синтаксического дерева (AST).
  • check-added-large-files: гарантирует, что большие файлы не будут случайно добавлены в репозиторий.
  • check-json: проверяет файлы JSON на наличие синтаксических ошибок.
  • check-toml: проверяет файлы TOML на наличие синтаксических ошибок.
  • check-yaml: проверяет файлы YAML на наличие синтаксических ошибок.
  • check-shebang-scripts-are-executable: проверяет, что сценарии shebang (сценарии, начинающиеся с #!/) являются исполняемыми.
  • bandit: проверяет распространенные проблемы безопасности в коде Python.
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.3.0
    hooks:
      - id: check-ast
      - id: check-added-large-files
      - id: check-json
      - id: check-toml
      - id: check-yaml
      - id: check-shebang-scripts-are-executable
  - repo: https://github.com/PyCQA/bandit
    rev: 1.7.4
    hooks:
      - id: bandit

Форматтеры

Форматтеры — это хуки, которые автоматически форматируют код в соответствии с определенными рекомендациями по стилю. Они помогают поддерживать единообразное форматирование кода в проекте, обеспечивая удобочитаемость и соблюдение выбранных правил кодирования. Эти ловушки могут исправить такие проблемы, как конечные пробелы, смешанные окончания строк и отсутствующие маркеры конца файла. Популярные средства форматирования, такие как Black и Isort, обычно используются в этой категории.

  • end-of-file-fixer: Добавляет маркер конца файла в конец файлов, если он отсутствует.
  • mixed-line-ending: Исправляет несовместимые окончания строк в файлах.
  • trailing-whitespace: Удаляет конечные пробелы в конце строк.
  • black: автоматически форматирует код Python в соответствии с рекомендациями по стилю кода Блэка. Многие рекомендации можно изменить с помощью таких аргументов, как длина строки в приведенном ниже примере.
  • black-jupyter: Форматирует блокноты Jupyter с использованием рекомендаций по стилю кода Black.
  • isort: Сортирует импорт Python в соответствии с определенным стилем, в данном случае «черный» профиль должен быть совместим с черным хуком выше.
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.3.0
    hooks:
      - id: end-of-file-fixer
      - id: mixed-line-ending
      - id: trailing-whitespace
  - repo: https://github.com/psf/black
    rev: 22.10.0
    hooks:
      - id: black
        language_version: python3.11
        args:
          - --line-length=128
      - id: black-jupyter
        language_version: python3.11
  - repo: https://github.com/pycqa/isort
    rev: 5.11.5
    hooks:
      - id: isort
        args: [ "--profile", "black" ]

Программы проверки кода

Хуки Code Checkers сосредоточены на анализе кода на наличие потенциальных проблем, таких как запахи кода, анти-шаблоны и распространенные ошибки. Они выполняют статический анализ кодовой базы, чтобы выявить такие проблемы, как отсутствующие строки документации, операторы отладки или нарушения рекомендаций по стилю кода. Такие инструменты, как Flake8 и MyPy, обычно используются в этой категории для обеспечения всесторонних возможностей проверки кода. И зря! Flake8 и MyPy — мощные помощники в проверке кода, объединяющие сотни правил при использовании с плагинами. Мы рекомендуем расширить Flake8 с помощью Bugbear, Comprehensions и Simplify.

  • check-docstring-first: проверяет, присутствует ли строка документации на уровне модуля и находится ли она в начале файла.
  • debug-statements: указывает на использование операторов отладки, таких как print, pdb и т. д.
  • flake8: выполняет различные проверки кода и соблюдение стиля. Отличительной особенностью Flake8 является то, что он возвращает код и описание нарушенного протокола, а также гиперссылку непосредственно на строку кода, где он был обнаружен.
  • pyupgrade: обновляет код Python до более новых версий синтаксиса. В приведенном ниже примере специально ориентирован на Python 3.9 и выше.
  • yesqa: автоматически удаляет ненужные # noqa комментарии. Эти комментарии можно использовать для игнорирования строк кода, не прошедших линтинг. Когда строки проходят, комментарий удаляется.
  • pycln: Удаляет все неиспользуемые операторы импорта. Будь диким, живи немного!
  • mypy: Тип проверяет код Python с помощью средства проверки статического типа MyPy. Мы рекомендуем игнорировать отсутствующие импорты в MyPy.
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.3.0
    hooks:
      - id: check-docstring-first
      - id: debug-statements
  - repo: https://github.com/pycqa/flake8
    rev: v5.0.4
    hooks:
      - id: flake8
        args:
          - "--max-line-length=128"
        additional_dependencies:
          - flake8-bugbear
          - flake8-comprehensions
          - flake8-simplify
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v0.991
    hooks:
      - id: mypy

Корректоры кода

Крючки Code Correctors предназначены для автоматического исправления или обновления кода для улучшения качества кода, удобства сопровождения и совместимости. Эти хуки могут выполнять автоматический рефакторинг, применять современные улучшения синтаксиса и устранять распространенные проблемы с кодом. Они помогают оптимизировать кодовую базу, автоматически применяя изменения, которые в противном случае потребовали бы ручного вмешательства.

  • pyupgrade: обновляет код Python до более новых версий синтаксиса. В приведенном ниже примере специально ориентирован на Python 3.9 и выше.
  • yesqa: автоматически удаляет ненужные # noqa комментарии. Эти комментарии можно использовать для игнорирования строк кода, не прошедших линтинг. Когда строки проходят, комментарий удаляется.
  • pycln: удаляет все неиспользуемые операторы импорта. Будь диким, живи немного!
repos:
  - repo: https://github.com/asottile/pyupgrade
    rev: v3.7.0
    hooks:
      - id: pyupgrade
        args: [--py311-plus]
  - repo: https://github.com/asottile/yesqa
    rev: v1.4.0
    hooks:
      - id: yesqa
        additional_dependencies: &flake8_deps
          - flake8-bugbear==22.8.23
          - flake8-comprehensions==3.10.0
          - flake8-docstrings==1.6.0
  - repo: https://github.com/hadialqattan/pycln
    rev: v2.1.1
    hooks:
      - id: pycln
        args: [--all]

Помощники Git

Хуки Git Helpers предоставляют помощь и обеспечивают соблюдение определенных правил, связанных с системой контроля версий Git. Они помогают гарантировать, что коммиты соответствуют определенным рекомендациям, таким как форматы сообщений коммитов, и выполняют проверки на различных этапах рабочего процесса Git. Эти крючки могут улучшить совместную работу, процессы проверки кода и поддерживать непротиворечивую историю в рамках проекта. Хуки, такие как Commitizen и No-Commit-To-Branch, являются примерами помощников Git.

  • commitizen: Принуждает использовать формат сообщений коммитов Commitizen для согласованных и стандартизированных коммитов.
  • commitizen-branch: выполняет проверку сообщения фиксации. С приведенным ниже аргументом он работает специально для отправки веток, но доступны и другие варианты.
repos:
  - repo: https://github.com/commitizen-tools/commitizen
      rev: v2.35.0
      hooks:
        - id: commitizen
        - id: commitizen-branch
          stages: [push]
  - repo: https://github.com/pre-commit/pre-commit-hooks
      rev: v4.3.0
      hooks:
        - id: check-merge-conflict
        - id: no-commit-to-branch

Найдите код в https://github.com/marvelousmlops/precommit-heaven и порадуйте нас пиаром, если считаете, что мы должны что-то добавить в коллекцию!

Настройка и игнорирование

Иногда вы можете позволить определенному хуку обходить строку кода, потому что вы считаете, что это ложное срабатывание, или потому что у вас есть веская причина не следовать рекомендуемому стилю или соглашению. Большинство хуков имеют разные способы настройки. Например, вы можете указать, какие файлы следует игнорировать в ваших установочных файлах, файлах конфигурации или непосредственно в коде. Если вы хотите, чтобы хуки пропускали определенные строки, вы можете добавить комментарий в строку (справа от вашего кода).

Разные хуки имеют разные комментарии для игнорирования, но общий комментарий, возвращаемый в нескольких хуках, — # noqa. Тем не менее, есть комментарии для игнорирования конкретных хуков. В приведенном ниже примере мы настраиваем только хук pycln для игнорирования строки кода с его собственным хуком # nopycln: import.

from pandas import ( # nopycln: import
    read_csv,
    DataFrame,
    concat
)

Но для того же эффекта можно использовать общий # noqa.

from pandas import ( # noqa
    read_csv,
    DataFrame,
    concat
)

Когда затем используется общий #noqa, несколько хуков могут игнорировать строку. Например, #flake8 также восприимчив к #noqa, так что действуйте осторожно.

Война крючков

Разные хуки могут пытаться делать одни и те же или похожие вещи разными способами. Когда это происходит, вы можете убедиться, что ваши хуки работают вместе, объединив их с аргументами. Ниже приведен классический пример двух популярных хуков, которые могут иметь разные настройки длины строки по умолчанию. Обратите внимание, что два аргумента хуков, настраивающих одно и то же, имеют разные имена. Вы можете попытаться быть умным и позволить средству проверки кода работать перед средством форматирования кода, но следующий разработчик (или конвейер), использующий точно такие же хуки, столкнется с проблемой. Поэтому убедитесь, что ваши крючки работают как одна команда.

repos:
- repo: https://github.com/psf/black
    rev: 22.10.0
    hooks:
      - id: black
        language_version: python3.11
        args:
          - --line-length=128
- repo: https://github.com/pycqa/flake8
    rev: v5.0.4
    hooks:
      - id: flake8
        args:
          - "--max-line-length=128"

Многие крючки в наши дни даже кивают друг другу. Например, isort имеет определенную настройку, соответствующую стилю black.

- repo: https://github.com/pycqa/isort
    rev: 5.11.5
    hooks:
      - id: isort
        args: [ "--profile", "black" ]

Для настройки мы снова рекомендуем использовать пакет pre-commit и настраивать аргументы в пакете .pre-commit-config.yaml, чтобы обеспечить ясность, контроль и контроль версий.

Когда использовать хуки (а когда нет)

Итак, когда вы должны их использовать? Каждый крючок — это отдельная история. Варианты использования также могут сильно различаться. Однако мы рекомендуем использовать хуки в ваших пайплайнах, начиная с CI. Использование перехватчиков в заданиях пайплайна на ранней стадии может обеспечить ранний сбой пайплайна, экономя драгоценное время и деньги. В конечном итоге затраты на развертывание могут варьироваться от долей цента до миллионов. Так что деньги не всегда являются аргументом. Что может быть еще важнее, так это то, что использование правильных хуков высвободит драгоценное время и пространство для разработчиков, чтобы они могли сосредоточиться на других, более важных и менее очевидных задачах. Задачи, которые нельзя автоматизировать с помощью автоматизированного протокола в хуке. Например: почему человек должен проверять, совпадает ли импорт всех скриптов с остальным кодом? Такие задачи могут быть очень важными, но могут стать утомительными и не использовать наши когнитивные способности.

Все под контролем

В то время как люди беспокоятся о том, что ИИ захватит мир, вы, как разработчик, должны беспокоиться о том, что хуки захватят ваш код. Некоторые крючки самоуверенны, что делает их надежными и приятными советчиками. Однако, когда эти взгляды не совпадают с вашими собственными, самоуверенность может быстро превратиться в дерзость! Вы будете добавлять # noqa повсюду. Иногда ловушку можно эффективно настроить централизованно с правильными аргументами. Однако, когда этого становится слишком много, вы должны спросить себя, работает ли этот крючок на вас или против вас. Помните, что меньше может быть больше. Удачного линтинга!