$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогПрактическая настройка pre-commit для Python: линтеры, автоправки и контроль скорости сборки

Практическая настройка pre-commit для Python: линтеры, автоправки и контроль скорости сборки

$ sudo teach IT
·8 августа 2026 г.·10 мин·70
Практическая настройка pre-commit для Python: линтеры, автоправки и контроль скорости сборки

Разберём, как собрать pre-commit-хук-пайплайн так, чтобы он реально повышал качество: ruff/format, запрет изменений в тестах, кэширование и правила для больших репозиториев. Дадим готовый конфиг и схему внедрения без «остановки разработки».

Содержание
Что важно понять перед настройкойАрхитектура пайплайна: что и где запускатьПочему тесты — особая зонаПодготовка: структура репозитория и baselineМинимальный набор конфигурации для ruffГотовый конфиг pre-commit для Python и ruffЧто здесь происходит на практикеКонтроль скорости: кэширование, ограничение объёма и выбор стратегии1) Кэш pre-commit: где выигрыш и как не сломать2) Фильтрация файлов — самый недооценённый рычаг3) Два режима: проверка в коммите и форматирование отдельно4) Не злоупотребляйте «--fix» на всё подрядКак запретить изменения тестов на уровне пайплайнаСхема внедрения без остановки разработкиШаг 1. Ввести пайплайн в режиме «только проверка» (на этапе теста)Шаг 2. Сделать один «технический» коммит на приведение к стандартамШаг 3. Включить автоправки только на безопасных путяхШаг 4. Настроить правило “не пускать тесты в автофикс”Как настроить pre-commit для больших репозиториев: практические рекомендации1) Явно исключайте авто-генерируемое2) Контролируйте, какие файлы попадают в хук3) Разделяйте «быстрое» и «глубокое»4) Согласуйте версию ruff на всех машинахБазовые команды для диагностикиЗапуск по staged-файлам (обычный сценарий)Запуск по всем файлам (для baseline и техдолга)Принудительно выполнить один хук (например, формат или fix)Типичные ошибки и как их избежатьОшибка 1: включить --fix для всех, включая тестыОшибка 2: забыть про ruff format --checkОшибка 3: разные версии ruff в локальном и pre-commit окруженииОшибка 4: слишком широкие хуки (нет исключений)Как это выглядит в workflow командыВыводы

Инструменты статического анализа и форматирования в Python сегодня стали почти стандартом: ruff заменяет целый зоопарк линтеров, а автоформатирование делает стиль воспроизводимым. Но на практике самая частая проблема звучит так: «Хук срабатывает, но сборка стала медленнее, и команда начала его отключать/обходить». Причина почти всегда одна — pre-commit настроен без учета производительности и без продуманной политики изменений в критичных файлах.

Ниже разберём, как собрать рабочий pre-commit-пайплайн для Python так, чтобы он действительно повышал качество кода и при этом не превращался в узкое горлышко. Фокус: ruff (линтер+форматтер), запрет правок тестов/снапшотов, кэширование и правила для больших репозиториев. В конце — готовый конфиг и схема внедрения без остановки разработки.


Что важно понять перед настройкой

pre-commit — это не «ещё один линтер», а оркестратор проверок при каждом коммите. Он умеет:

  • запускать набор хуков до записи коммит-объекта;
  • использовать виртуальные окружения для изоляции;
  • кэшировать результаты (в зависимости от хуков);
  • запускать хуки по файлам (и тем самым экономить время).

Качество настройки обычно определяется тремя параметрами:

  1. Стабильность: хуки должны быть детерминированными и давать одинаковые результаты в разных средах.
  2. Политика изменений: где автофиксы допустимы, а где — нет.
  3. Производительность: хуки должны быстро проходить по типичным коммитам и не ломать интерактивный workflow.

Чтобы добиться этого, нужно понимать, как именно pre-commit вызывает инструменты и как ruff ведёт себя при форматировании и автопочинке.


Архитектура пайплайна: что и где запускать

Для ruff обычно хочется три слоя:

  • проверка стиля/формата (как правило, через ruff format --check);
  • линтер (через ruff check);
  • автоправки (опционально, через ruff check --fix и/или форматирование без --check).

При этом важно различать:

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

Почему тесты — особая зона

Даже если у вас «только стиль не совпал», автофиксы могут затронуть строки в тестах (особенно если форматирование меняет переносы строк, отступы, конструкции с assert). На уровне результата это может казаться безобидным, но в реальной разработке тесты — это документация поведения. Любая массовая правка тестов:

  • затрудняет ревью (diff становится шумным);
  • усложняет поддержку «почему именно сломалось»;
  • создаёт риск скрытых изменений ожиданий.

Поэтому разумная политика: в тестах разрешить только проверки, а автопочинки — только для исходного кода.


Подготовка: структура репозитория и baseline

Перед внедрением полезно понять, как устроены файлы:

  • исходники: src/ или пакет в корне;
  • тесты: tests/;
  • конфиги: pyproject.toml;
  • большие каталоги (в которых форматирование не должно выполняться): generated/, migrations/, vendor/, и т.п.

Если тесты действительно выделены в tests/, можно задать правило: хуки, которые меняют файлы, запускаются только на исходниках, а хуки проверки — на всё.

Минимальный набор конфигурации для ruff

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

code
# pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py311"

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM", "PL"]
ignore = ["E501"] # если используете format, часто E501 не нужен

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false

Если у вас уже есть pyproject.toml — не переписывайте его, используйте текущий. Ключевое: ruff должен быть настроен единообразно, чтобы --check и --fix/format не вели себя неожиданно.


Готовый конфиг pre-commit для Python и ruff

Ниже — практичный /.pre-commit-config.yaml, который решает задачи статьи:

  • линтер ruff check;
  • форматтер ruff format;
  • автоправки только для исходников (а тесты — только проверяем);
  • контроль скорости: фильтрация файлов, лимиты, и рекомендации по кэшированию.

Важно: ниже предполагается, что тесты лежат в tests/. Если у вас другой путь — замените regex.

code
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.6.9
    hooks:
      # 1) Форматирование: проверка на всём, где разрешено форматировать
      - id: ruff-format
        name: ruff format (check)
        args: ["--check"]
        # Запускаем на Python-файлы, кроме типичных "не трогаем"
        exclude: ^(generated/|migrations/|vendor/|\.venv/|\.tox/)
        types_or: [python]

      # 2) Линтер: проверка на всём
      - id: ruff
        name: ruff check
        exclude: ^(generated/|migrations/|vendor/|\.venv/|\.tox/)
        types_or: [python]

      # 3) Автоправки линтера: только для исходников
      - id: ruff
        name: ruff check (fix - src only)
        args: ["--fix"]
        # Не трогаем тесты и прочие зоны
        exclude: ^(tests/|generated/|migrations/|vendor/|\.venv/|\.tox/)
        types_or: [python]

      # 4) Форматирование после автоправок: только для исходников
      - id: ruff-format
        name: ruff format (write - src only)
        # Без --check — инструмент будет менять файлы
        exclude: ^(tests/|generated/|migrations/|vendor/|\.venv/|\.tox/)
        types_or: [python]

  # Дополнительно: можно добавить проверки конфигов/текстов,
  # но для задачи статьи оставим фокус на Python+ruff.

# Общие настройки pre-commit
default_stages: [commit]
fail_fast: false

Что здесь происходит на практике

  1. ruff format (check) запускается на всех *.py, кроме исключённых директорий. Это защищает от «почти форматирования» — коммит не пройдёт, если формат не соответствует правилам.
  2. ruff check тоже проверяет всё. Так вы фиксируете и стиль, и потенциальные ошибки.
  3. ruff check (fix - src only) — автопочинка только для исходников. Тесты не будут меняться автоматически.
  4. ruff format (write - src only) гарантирует, что после автоправок исходники окажутся в правильном формате.

Это комбинация, которая обычно хорошо работает в командах: разработчик получает автофиксы там, где это безопасно, и явно отвечает за изменения тестов.


Контроль скорости: кэширование, ограничение объёма и выбор стратегии

1) Кэш pre-commit: где выигрыш и как не сломать

pre-commit хранит виртуальные окружения и результаты в каталоге кэша. Кэш ускоряет повторные запуски и переиспользует зависимости.

Проверьте, что у команды не настроены конфликтующие переменные окружения. В типичной среде можно оставить настройки по умолчанию.

На CI обычно кэширование делают отдельно, но даже локально полезно понимать механику:

  • если вы обновляете rev хуков, кэш частично инвалидируется;
  • если меняется pyproject.toml, часть проверок всё равно будет пересчитываться (но pre-commit всё равно управляет окружением).

2) Фильтрация файлов — самый недооценённый рычаг

В большом репозитории главная причина замедления — хуки запускаются на слишком много файлов. pre-commit и так фильтрует по staged-файлам, но:

  • некоторые команды коммитят «всё подряд»;
  • иногда staged включает массовые изменения;
  • часть файлов может попадать в check по ошибочному паттерну.

Поэтому в конфиге выше есть exclude, который вырезает явные «не трогаем» зоны. Это экономит секунды на каждом коммите.

3) Два режима: проверка в коммите и форматирование отдельно

Для сверхбыстрого workflow иногда делают иначе:

  • в коммит — только ruff check и ruff format --check;
  • форматирование/фиксы — по требованию: отдельной командой pre-commit run --all-files или pre-commit run ruff check (fix).

Однако в нашем конфиге автофиксы включены только для исходников — это компромисс: качество растёт, а время не расползается.

4) Не злоупотребляйте «--fix» на всё подряд

--fix потенциально может:

  • менять импорт-структуру;
  • делать более широкие замены, чем ожидает автор коммита;
  • создавать “diff noise” в файлах, к которым вы не стремились.

Потому запрет на фиксы в tests/ — не просто удобство, а инструмент контроля диффа и скорости ревью.


Как запретить изменения тестов на уровне пайплайна

Мы уже ограничили автоправки через exclude для тестов. Но есть ещё один момент: даже если хуки не трогают тесты, форматирование иногда может затронуть вложенные файлы при неправильных паттернах.

Чтобы минимизировать риск:

  • убедитесь, что exclude: ^(tests/...) совпадает с реальной структурой путей;
  • учитывайте, что pre-commit видит пути относительно корня репозитория;
  • если у вас тесты в app/tests или backend/tests, поменяйте regex соответствующе.

Дополнительно можно усилить контроль на уровне процесса: командой ревью проверить, что коммит с тестами действительно менялся автором вручную.


Схема внедрения без остановки разработки

Самая неприятная ситуация — когда вы добавляете хук в середине проекта и внезапно обнаруживаете, что в репозитории уже много нарушений. Тогда каждый коммит превращается в борьбу с накопленным техдолгом.

Правильная схема внедрения обычно выглядит так:

Шаг 1. Ввести пайплайн в режиме «только проверка» (на этапе теста)

Начните с конфигурации, где автоправки выключены, а проверки включены. Это можно сделать, временно оставив только ruff-format --check и ruff check.

Затем прогоните локально:

code
pre-commit run --all-files

Получите список файлов, где уже есть нарушения. Зафиксируйте масштаб.

Шаг 2. Сделать один «технический» коммит на приведение к стандартам

Дальше действуйте одним из вариантов:

  • либо прогнать ruff check --fix и ruff format вручную по репозиторию;
  • либо временно включить автоправки в конфиге и запустить pre-commit run --all-files.

В большинстве команд предпочтительнее именно один техкоммит: он делает историю чище, а не размазывает правки по десяткам PR.

Шаг 3. Включить автоправки только на безопасных путях

После того как baseline выровнен, можно включать --fix и форматирование без --check для исходников (как в финальном конфиге выше). Это снизит сопротивление внедрению: команда видит, что хук помогает, а не ломает.

Шаг 4. Настроить правило “не пускать тесты в автофикс”

Именно здесь становится критичным исключение tests/ из «пишущих» хуков. Команда получает предсказуемый дифф: тесты меняются только по намерению автора коммита.


Как настроить pre-commit для больших репозиториев: практические рекомендации

В больших кодовых базах проблема обычно не в том, что ruff медленный. Проблема — в том, что “hooks per commit” начинают быть слишком частыми и слишком тяжёлыми.

Ниже набор решений, которые реально работают:

1) Явно исключайте авто-генерируемое

Каталоги вроде generated/, migrations/, vendor/ почти всегда не должны проходить форматирование и линтинг. Иначе вы платите временем за файлы, которые всё равно не редактируются вручную.

2) Контролируйте, какие файлы попадают в хук

Если в репозитории есть специфические расширения (например, .pyi, .pyw), задайте types_or или дополнительные паттерны. Для стандартных пайплайнов достаточно types_or: [python].

3) Разделяйте «быстрое» и «глубокое»

В некоторых проектах делают два уровня:

  • быстрые проверки в commit;
  • глубокие проверки (интеграционные, с --all-files) по расписанию, например nightly.

Для ruff обычно достаточно commit-level, но если вы добавляете дополнительные хуки (например, типа mypy/тяжёлых тестов), это становится критичным.

4) Согласуйте версию ruff на всех машинах

В командах часто бывает так: разработчики обновили ruff локально, но pre-commit использует закрепленную версию. В итоге возникают расхождения: “у меня проходит”. Закрепляйте версию через rev, как в примере.


Базовые команды для диагностики

Когда что-то пошло не так, важно быстро локализовать проблему. Вот набор команд:

Запуск по staged-файлам (обычный сценарий)

code
pre-commit run

Запуск по всем файлам (для baseline и техдолга)

code
pre-commit run --all-files

Принудительно выполнить один хук (например, формат или fix)

code
pre-commit run ruff-format
pre-commit run ruff

Если у вас несколько хуков с одинаковым id, но разными именами, используйте --hook-stage или явно по имени хука (если в вашей версии pre-commit это удобно). Часто проще смотреть список хуков:

code
pre-commit run --list

Типичные ошибки и как их избежать

Ошибка 1: включить --fix для всех, включая тесты

Итог — массовый diff и шум в PR. Решение: исключайте tests/ (или другие чувствительные директории) из write-хуков.

Ошибка 2: забыть про ruff format --check

Если формат только «проверяется» нестрого или выключен, линтер может начинать спорить с автоформатом. В итоге команда получает частые “почему всё не совпадает”. Решение: ruff-format --check должен быть частью пайплайна.

Ошибка 3: разные версии ruff в локальном и pre-commit окружении

Если вы запускаете ruff руками другой версии, а pre-commit — другой, различия неизбежны. Закрепляйте версию в rev.

Ошибка 4: слишком широкие хуки (нет исключений)

В большом репозитории это превращается в постоянные потери времени. Начинайте с честного exclude-листа: сгенерированное, vendor, мёртвые директории.


Как это выглядит в workflow команды

Практическая модель, которую обычно выбирают команды после внедрения:

  1. Разработчик вносит изменения.
  2. При коммите pre-commit автоматически чинит исходники (ruff check --fix и затем форматирование исходников).
  3. Тесты проверяются, но не меняются автоматически.
  4. Если проверка формата или линтера падает — разработчик получает понятный diff и исправляет вручную (или запускает pre-commit run).

Это снижает количество “пустых” PR, ускоряет ревью и постепенно очищает кодовую базу.


Выводы

Хорошая настройка pre-commit для Python — это не список хуков, а система управляемых компромиссов между качеством, предсказуемостью диффа и скоростью. Для ruff-пайплайна особенно важно:

  • иметь отдельные шаги проверки формата и линтинга;
  • включать автоправки только там, где это безопасно (часто — для src/, но не для tests/);
  • контролировать скорость через исключение generated/vendor/migrations и аккуратную фильтрацию;
  • внедрять поэтапно: сначала проверки, потом единый baseline, затем включение фиксов.

Если вам нужен дополнительный «вход в тему», чтобы уверенно понимать, какие правила ruff включать и как читать отчёты, полезно пройти материал вроде “ruff – для начинающих!” — но даже без него, опираясь на приведённый конфиг и схему внедрения, можно собрать устойчивый пайплайн самостоятельно.


Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

ruff – для начинающих!

Курс по теме

ruff – для начинающих!

С нуля до уверенной работы с линтингом и форматированием Python-кода. Подходит для начинающих и тех, кто уже пишет на Python. Разберитесь с Ruff раз и навсегда — и перестаньте гадать, какой инструмент запускать.

Бесплатно
Открыть курс

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Discord-боты на Python: с нуля до PRO

Discord-боты на Python: с нуля до PRO

Свой Discord-бот за первый вечер, а к концу курса — бот для сервера с тикетами, экономикой, модерацией, уровнями и ИИ. Python и discord.py 2.7, slash-команды, Components V2, запуск 24/7.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Solidity DLC - поймай, если сможешь

Solidity DLC - поймай, если сможешь

Аудит смарт-контрактов на реальных взломах. The DAO, Parity, Wormhole, Ronin, Euler и ещё 7 разборов: что пошло не так, сколько потеряли и что бы спасло. Атаки и защиты на Foundry, план входа в профессию аудитора.

Перейти к курсу

Другие статьи

Python для аналитики на практике: как строить признаки и проверять их воспроизводимость
python

Python для аналитики на практике: как строить признаки и проверять их воспроизводимость

Поймём, где чаще всего ломается воспроизводимость (версии данных, фильтры, дрейф) и как оформить пайплайн признаков так, чтобы результаты совпадали между запусками и окружениями.

17 сентября 2026 г.
1830
Запуск и поддержка проекта на Ruff в команде: формат “как в CI”, а не “как договорились”
запуск

Запуск и поддержка проекта на Ruff в команде: формат “как в CI”, а не “как договорились”

Как правильно выстроить конфигурацию Ruff (линтинг, форматирование, автозамены) и сделать так, чтобы одинаковые правила гарантировались в локальной сборке и на CI без ручного шаманства.

16 сентября 2026 г.
1730
Как выучить программирование самому и бесплатно: с чего начать и в каком порядке
выучить

Как выучить программирование самому и бесплатно: с чего начать и в каком порядке

Разберём понятный маршрут для новичка: как выбрать первый язык, что учить в первую очередь, какие мини-проекты делать и как не забросить занятия. Дам бесплатные ориентиры и план, чтобы знания складывались в портфолио.

21 сентября 2026 г.
2420
python

Python 3.15: ленивые импорты, frozendict, sentinel, JIT и жизнь без GIL на реальном FastAPI

Один сервис на FastAPI, код для 3.12 и 3.15 и честные замеры: старт в 2,5 раза быстрее, 8 потоков без GIL в 4 раза быстрее — и где 3.15 медленнее.

6 ноября 2026 г.
110
Какой первый проект выбрать новичку, чтобы не бросить обучение
первый

Какой первый проект выбрать новичку, чтобы не бросить обучение

Подберём 5–7 идей под уровень “с нуля”, объясним, что делать по шагам и как довести проект до результата без перегруза. В конце — как оформить мини-портфолио и что показать, даже если проект маленький.

23 сентября 2026 г.
2280
С чего начать учить Python, если вы вообще не программист: план на 30 минут в день
учить

С чего начать учить Python, если вы вообще не программист: план на 30 минут в день

Разберём, что установить и как устроить обучение: первые переменные, вывод текста, простые условия и циклы — на понятных примерах без “магии”. В конце соберёте мини-скрипт, который что-то делает по вводу пользователя.

6 ноября 2026 г.
180

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

Что важно понять перед настройкойАрхитектура пайплайна: что и где запускатьПочему тесты — особая зонаПодготовка: структура репозитория и baselineМинимальный набор конфигурации для ruffГотовый конфиг pre-commit для Python и ruffЧто здесь происходит на практикеКонтроль скорости: кэширование, ограничение объёма и выбор стратегии1) Кэш pre-commit: где выигрыш и как не сломать2) Фильтрация файлов — самый недооценённый рычаг3) Два режима: проверка в коммите и форматирование отдельно4) Не злоупотребляйте «--fix» на всё подрядКак запретить изменения тестов на уровне пайплайнаСхема внедрения без остановки разработкиШаг 1. Ввести пайплайн в режиме «только проверка» (на этапе теста)Шаг 2. Сделать один «технический» коммит на приведение к стандартамШаг 3. Включить автоправки только на безопасных путяхШаг 4. Настроить правило “не пускать тесты в автофикс”Как настроить pre-commit для больших репозиториев: практические рекомендации1) Явно исключайте авто-генерируемое2) Контролируйте, какие файлы попадают в хук3) Разделяйте «быстрое» и «глубокое»4) Согласуйте версию ruff на всех машинахБазовые команды для диагностикиЗапуск по staged-файлам (обычный сценарий)Запуск по всем файлам (для baseline и техдолга)Принудительно выполнить один хук (например, формат или fix)Типичные ошибки и как их избежатьОшибка 1: включить --fix для всех, включая тестыОшибка 2: забыть про ruff format --checkОшибка 3: разные версии ruff в локальном и pre-commit окруженииОшибка 4: слишком широкие хуки (нет исключений)Как это выглядит в workflow командыВыводы