Рубрики
Без рубрики

Написание крючков для “предварительной коммита”

Есть уже сотни великих крючков вокруг. Но что, если мы хотим еще один? Ну, чтение должно быть … Теги с Python, Git, преобладают.

Уже есть сотни великих Крючки около. Но что, если мы хотим еще один? Ну, читая Официальные документы Это отличное место для начала (и ссылки в будущем).

Что я хочу:

  • показать пример Как можно разработать конкретную идею
  • Опишите подводные камни, которые удивили меня больше всего (и как их преодолеть)
  • привлечь внимание на такой замечательный инструмент, который Предварительный коммит является

Тема, которую я выбрал для иллюстрации – это выражения присваивания (PEP572). И вниз по линии я создадим три разных крючка для этого.

Препараты

Для экспериментов с крючками, уже существующими в дикой природе, вам понадобится только один репозиторий Git. И тогда вы настроили его для установки/использования крючков откуда-то еще.

Но чтобы написать свои собственные, должны быть два отдельных репозитория.

Давай начнем.

  • Если вы не установили Предварительный коммит в систему/окружающую среду – см. Документация о том, как это сделать.

  • Начните с базовой структуры, как это (две папки с git init)

[~/code/temp/try-pre-commit]
$ tree -a -L 2
.
├── code
│   └── .git
└── hooks
    └── .git
  • Создать манекен Крючки/.pre-Compad-tooks.yaml
$ touch hooks/.pre-commit-hooks.yaml
  • Создать манекен код/эксперименты.txt.
$ touch code/experiments.txt
  • CD в Код папка и инициализация Предварительный коммит (Не позволяйте этой второй установке запутать вас, он отличается от установки в систему/среду. Это обладает хранилищем – широкий, один файл HIT крючка, на самом деле)
$ cd code
$ pre-commit install
pre-commit installed at .git/hooks/pre-commit

Вот как это должно выглядеть сейчас

[~/code/temp/try-pre-commit]
$ tree -a -L 2
.
├── code
│   ├── experiments.txt
│   └── .git
└── hooks
    ├── .git
    └── .pre-commit-hooks.yaml

Пример 1: овладеть

Первый крючок будет очень простым, используя существующие возможности структуры. Я назову это “Walrus-grep”.

Обновление Крючки/.pre-Compad-tooks.yaml

- id: walrus-grep
  name: walrus-grep
  description: Warn about assignment expressions
  entry: "language: pygrep

Pygrep это кроссплатформенная версия Греп Отказ

Вход Принимает Regexp чего-то, чего вы не хотите совершать. В моем случае я использовал «: =» – шаблон без фактического регенерации волшебных или убегающих символов.

Обновление код/эксперименты.txt.

text file
a := 1

Самый удобный способ эксперимента с try-repo команда. Это позволяет тестировать крючки без комбинга .PRE-COMPY-TOUCKS.YAML Каждый раз и без закрепления до редакции позже в .Pre-Compad-config.yaml Отказ

Мы можем либо поставить файлы каждый раз перед запуском проверки или использования try-repo Команда с --all-файлы флаг.

[master][~/code/temp/try-pre-commit/code]
$ pre-commit try-repo ../hooks walrus-grep --verbose --all-files

Это произошло, потому что крючки Reppo не имеют ни одного фиксации – нет ничего головы, чтобы указать. Совершать .PRE-COMPY-TOUCKS.YAML и повторить.

Смотрите ” (нет файлов, чтобы проверить) “? Часто это было бы правдой, чеки могут иметь заранее определенные ограничения E.g. Расширение файла, путь, игнорировать параметры. Но у нас есть наш Experiments.txt файл и не ожидайте ни одного из них!

Готча: --all-файлы Флаг больше похоже на «все отслеживается , не только поэтапные файлы». Итак, в любом случае, вы должны добавить файлы в Git перед запуском проверки.

В конце концов! ” Не удалось «это то, что нам нужно было доказать, что крюк Grep»: = «Внутри какого-то случайных поэтапных файлов. Так что здесь неудача – это хорошо.

Но думать об этом ситуации далеко от идеала:

  • Во-первых, мы на самом деле не хотим поймать »: = “Внутри текстовых файлов, только в Python Code
  • Во-вторых, что если «: =» происходит внутри комментариев, Docstrings, строки?

Для «только код Python» здесь есть Типы: [Python] Вариант, который я добавлю в .PRE-COMPY-TOURSS.YAML

- id: walrus-grep
  ...
  types: [python]

Мы видим: « (нет файлов для проверки) пропущены « Опять же, но сейчас ожидается.

Дважды проверить

$ mv experiments.txt experiments.py
$ git add experiments.py
$ pre-commit try-repo ../hooks walrus-grep --verbose --all-files

И у нас наша возлюбленная неудача обратно.

Что касается второй озабоченности: «: =« Внутри комментариев или Docstrings, нам потребуется что-то другое.

Пример 2: Аст

Второй крюк будет анализировать синтаксическое дерево, чтобы найти точный тип узла. Я назову это “Walrus-AST”.

Отказ от ответственности: Этот будет работать только в среде Python 3.8 (Beta1 в настоящее время доступна для тестирования).

Обновление Крючки/.pre-Compad-tooks.yaml

...
- id: walrus-ast
  name: walrus-ast
  description: Warn about assignment expressions
  entry: walrus-ast
  language: python
  types: [python]

а также эксперименты

# .py file with `:=` (Python3.8 syntax)
(x := 123)

Сам крючок будет в Крючки/Walrus_ast.py.

import argparse
import ast


def check(filename):
    with open(filename) as f:
        contents = f.read()

    try:
        tree = ast.parse(contents)
    except SyntaxError:
        print('SyntaxError, continue')
        return

    for node in ast.walk(tree):
        if isinstance(node, ast.NamedExpr):
            return node.lineno


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('filenames', nargs='*')
    args = parser.parse_args()

    for filename in args.filenames:
        lineno = check(filename)
        if lineno:
            print(f"{filename}:{lineno}")
            return 1

    return 0


if __name__ == '__main__':
    exit(main())

Что мы можем или не можем сделать здесь? Правила на документацию просты

Крючок должен выйти из ненулевой ошибки или изменить файлы в рабочем каталоге

Что нужно отметить:

  • Выход () с некоторым кодом («0» – это когда все в порядке) – решит ли крючок терпит неудачу или нет
  • Проверяет как Если __name__: и имена, как Главная () или Проверьте () не требуются (то есть вы можете иметь бесполезный файл с Выход (1) как его единственное содержимое)
  • Имя анализа файлов необходимы, если вы хотите иметь возможность ограничить по имени файла. Не требуется, но приятно иметь

Сейчас беги try-repo С новым крючком

$ pre-commit try-repo ../hooks walrus-ast --verbose --all-files

«Справочник». ‘ не устанавливается ». Что случилось?

Оказывается, (в случае (в случае сценариев Python) мы должны описать, как установить его как обычно, с setup.py или pyproject.toml Отказ

Создать Крючки/Setup.py.

from setuptools import setup
setup(
    name="hooks",
    py_modules=["walrus_ast",],
    entry_points={
        'console_scripts': [
            "walrus-ast=walrus_ast:main",
        ],
    },
)

Примечание: вместо py_modules. Вы можете использовать что-то вроде пакеты = Find_packages (".") или что-нибудь еще, что вы привыкли делать в Setup.py.

Отслеживать оба новых файла в Git

$ git add setup.py
$ git add walrus_ast.py

Теперь рабочий каталог должен выглядеть так

[master][~/code/temp/try-pre-commit/hooks]
$ tree -a -L 1
.
├── .git
├── .pre-commit-hooks.yaml
├── setup.py
└── walrus_ast.py

Попробуйте запустить его снова

$ pre-commit try-repo ../hooks walrus-ast --verbose --all-files

Напоминание о том, как Эксперименты .py Выглядит:

# .py file with `:=` (Python3.8 syntax)
(x := 123)

«: =» Действительно на второй линии, а один из комментариев не сообщается. Спасибо, АСТ!

Примечание: И если вы забыли, что это Python3.8 единственное – вы получите (надеюсь) некоторые указания. В этом случае крючок не работает, но не должен останавливать других от выполнения своей работы.

Пример 3: Меньше негативности, больше возможностей!

Это было не мое намерение остановить вас от использования выражений присваивания, скорее играть с новыми вещами и описывать процесс.

Давайте приблизимся к идее от противоположного направления и найдите место, где может соответствовать моржу. Благодаря этот твит И очень интересно Библиотека Чейз Стивенс мы можем найти такие места.

Создать Крючки/Walrus_opportunity.py.

import astpath

def main():
    search_path = "//Assign[targets/Name/@id = following-sibling::*[1][name(.) = 'If']/test/Name/@id]"
    found = astpath.search('.', search_path, print_matches=True, after_context=1)
    return len(found)

if __name__ == "__main__":
    exit(main())

Здесь Astpath использует XPath Чтобы найти узлы AST.

Обновление Setup.py.

from setuptools import setup

setup(
    name="hooks",
    py_modules=["walrus_ast", "walrus_opportunity"],
    entry_points={
        'console_scripts': [
            "walrus-ast=walrus_ast:main",
            "walrus-opportunity=walrus_opportunity:main"
        ],
    },
    install_requires=[
        # Dep. for `walrus-opportunity`
        "astpath[xpath]",
    ],
)

Обновление Крючки/.pre-Compad-tooks.yaml

- id: walrus-opportunity
  name: walrus-opportunity
  description: Warn if you could have used ":=" somewhere
  entry: walrus-opportunity
  language: python
  types: [python]

Обновление Код/Experiments.py.

# .py file with `:=` (Python3.8 syntax)
(x := 123)

# missed chance to use `:=`
x = calculate()
if x:
    print(f"found {x}")

и бегите

$ pre-commit try-repo ../hooks walrus-opportunity --verbose --all-files

Упаковка

Если вы решите, что локальное развитие сделано: Толчок Крючки Папка к Ваше репо (например, на Github ), создайте код/.pre-compad-config.yaml

repos:
- repo: https://github.com//pre-commit-hooks
  rev:  or 
  hooks:
  - id: walrus-grep
  - id: walrus-ast
  - id: walrus-opportunity

И теперь вы должны быть в состоянии использовать Git от Код Репо как обычно: изменить файлы, сцену и совершить их. Перед каждым заимством Git будет запустить скрипт от код/.git/hooks/Предварительный коммит Отказ И Предварительный коммит Framework должен показать отчет и разрешить коммит, если все в порядке, или показать отчет, а прервать коммит, если некоторые проверки вернули ненулевые или файлы, были изменены.

Приложение

Подробнее о AST посетителей и работа на дереве здесь . Благодаря Соттилю Энтони за такую чудесную рамку.

Оригинал: “https://dev.to/gyermolenko/writing-hooks-for-pre-commit-framework-5dpf”