$ 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 мин·28
Практическая настройка 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 – для начинающих!

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

Перейти к курсу
Python для аналитиков

Python для аналитиков

Данный курс будет очень полезен и отдельно аналитикам, и специалистам по машинному обучению, и тем, кто объединяет обе эти профессии (специалисты по Data Science).

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

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

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

Перейти к курсу
Python для машинного обучения. Основы

Python для машинного обучения. Основы

Машинное обучение это популярнейшее и востребованное направление в сфере IT, с использованием языка Python.

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

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

Как устроен сборщик мусора в CPython: трассировка объектов и циклические ссылки
устроен

Как устроен сборщик мусора в CPython: трассировка объектов и циклические ссылки

Погружаемся в механизм reference counting и cyclic garbage collector — смотрим на исходный код CPython, пишем тесты и выясняем, когда объект действительно удаляется из памяти.

17 июля 2026 г.
570
SQLModel vs SQLAlchemy: что выбрать для Python-проекта с FastAPI
sqlmodel

SQLModel vs SQLAlchemy: что выбрать для Python-проекта с FastAPI

Сравниваем два подхода к работе с базами данных в Python-экосистеме: где SQLModel упрощает жизнь, а где мощность SQLAlchemy незаменима. С примерами моделей, запросов и миграций.

18 июля 2026 г.
530
Создание Telegram бота в 2026 легко и просто! Полный курсы!
создание

Создание Telegram бота в 2026 легко и просто! Полный курсы!

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

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

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

21 сентября 2026 г.
100
FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь
fastapi

FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь

Поймём различия между синхронной обработкой, BackgroundTasks и внешними очередями. Разберём idempotency, ретраи и мониторинг фоновых процессов.

22 июля 2026 г.
750
cargo

Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей

Разберём, как организовать Rust workspace, управлять optional features, фиксировать версии библиотек и уменьшать “дрейф” зависимостей при командной разработке и CI.

18 сентября 2026 г.
110

Комментарии

Пока нет комментариев

Содержание

Что важно понять перед настройкойАрхитектура пайплайна: что и где запускатьПочему тесты — особая зонаПодготовка: структура репозитория и 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 командыВыводы