Python-проект редко обходится одним инструментом: для порядка в коде ставят black, isort, flake8 с десятком плагинов, autoflake и pyupgrade. Каждый запускается отдельно, каждый читает файлы заново, а конфигурация расползается по трём-четырём файлам. ruff решает эту проблему иначе: один бинарник на Rust, который одновременно работает линтером и форматтером, понимает настройки в pyproject.toml и тратит на проверку среднего проекта доли секунды. Ниже — практическая настройка, а не рекламный обзор.

Что именно заменяет ruff

Линтер ruff реализует правила из flake8, pycodestyle, isort, pyupgrade, flake8-bugbear, flake8-bandit и ещё нескольких десятков наборов. Форматтер повторяет поведение black с небольшими отличиями. Отсюда типичный список того, что можно убрать из зависимостей:

  • black — форматирование;
  • isort — сортировка импортов;
  • flake8 и его плагины — базовые проверки;
  • autoflake — удаление неиспользуемых импортов;
  • pyupgrade — перевод кода на новый синтаксис.

Чего ruff не делает: он не проверяет типы, для этого остаются mypy или pyright, и не анализирует связи между модулями. Это важно понимать, чтобы не ждать от него невозможного.

Установка

Ставить ruff лучше отдельным инструментом, а не в виртуальное окружение каждого проекта: тогда одна версия обслуживает все репозитории. Если проект использует uv, разумно зафиксировать версию в dev-зависимостях, чтобы у команды и CI она совпадала.

uv tool install ruff
python -m pip install --user ruff
sudo pacman -S ruff
ruff --version

Проверить, что в PATH попал нужный бинарник, можно командой which ruff. Если редактор запускает свою копию, версии могут расходиться, и правила будут срабатывать по-разному.

Первый запуск и чтение вывода

ruff check .
ruff check --statistics
ruff check --diff
ruff check --output-format=concise src/

По умолчанию включён минимальный набор: E4, E7, E9 и F. Это ошибки синтаксиса, неопределённые имена, неиспользуемые импорты. Ключ —statistics показывает, каких кодов больше всего, — с него удобно начинать знакомство с проектом. Ключ —diff печатает предлагаемые изменения, ничего не записывая. Формат вывода переключается на json, github или sarif — последний пригодится для загрузки результатов в системы анализа кода.

Настройка в pyproject.toml

[tool.ruff]
line-length = 100
target-version = "py312"
extend-exclude = ["migrations", "build"]

[tool.ruff.lint]
select = ["E", "F", "I", "B", "UP", "SIM"]
ignore = ["E501"]

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"]

[tool.ruff.lint.isort]
known-first-party = ["myapp"]

Разница между select и extend-select принципиальна: select полностью заменяет набор по умолчанию, extend-select добавляет правила к нему. Если написать select = [«B»], исчезнут даже базовые проверки F. Параметр target-version влияет на то, какие предложения по модернизации кода будет делать pyupgrade, поэтому его нужно держать в соответствии с минимальной поддерживаемой версией Python. Строку E501 часто отключают, когда длину строки уже контролирует форматтер.

Форматирование

ruff format .
ruff format --check .
ruff format --diff src/app.py

Форматтер стабилен и совместим с black почти полностью; на практике отличие может появиться в редких конструкциях, поэтому при переходе стоит один раз прогнать оба инструмента на большой ветке и сравнить diff. Настройки стиля задаются в секции [tool.ruff.format]: quote-style, indent-style, line-ending. Магические запятые, удерживающие перенос строки, тоже поддерживаются.

Автоисправления и noqa

ruff check --fix .
ruff check --fix --unsafe-fixes .
ruff check --add-noqa .
ruff check --select F401 --fix src/

Без флага —fix ruff только сообщает о проблемах. С флагом применяет безопасные исправления: убирает лишние импорты, переставляет их, заменяет устаревший синтаксис. Небезопасные правки, меняющие поведение кода, спрятаны за —unsafe-fixes, и их всегда нужно смотреть глазами. Для большого наследия помогает —add-noqa: он расставляет комментарии # noqa там, где нарушения уже есть, и позволяет включить строгий набор правил, не переписывая весь проект сразу.

pre-commit и CI

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.14.0  # подставьте актуальный тег
    hooks:
      - id: ruff-check
        args: [--fix]
      - id: ruff-format

В CI достаточно двух команд: ruff check --output-format=github . и ruff format --check .. Первая даёт аннотации прямо в diff пул-реквеста, вторая валит сборку, если кто-то забыл отформатировать файл. Если используется GitHub Actions, есть готовое действие astral-sh/ruff-action, но обычная установка через pip не менее надёжна и не добавляет зависимости от стороннего экшена. Тег в pre-commit берите из свежих релизов репозитория, а не из этой статьи.

Редактор и языковой сервер

ruff поставляет собственный языковой сервер ruff server. Расширение для VS Code, конфигурации для Neovim и Helix используют его для диагностики и форматирования при сохранении. Настройка обычно сводится к выбору бинарника и включению format on save; важно, чтобы редактор и CI использовали одну версию, иначе локально всё чисто, а сборка падает.

Миграция без боли

  1. Сначала включите форматтер и отформатируйте весь код одним коммитом.
  2. Затем добавьте базовый набор E, F, I и исправьте безопасные автофиксы.
  3. После этого расширяйте select по одному набору, начиная с B и UP.
  4. Для унаследованного кода используйте —add-noqa, а не отключение правил целиком.

Такой порядок даёт маленькие обозримые диффы. Если включить всё сразу через select = [«ALL»], проект утонет в сотнях замечаний, и миграцию бросят на середине.

Ограничения

ruff быстрый, но не всемогущий. Часть плагинов flake8 не имеет аналогов, например узкоспециализированные проверки под конкретный фреймворк. Правила в статусе preview могут меняться между выпусками, поэтому в общем CI их включают осознанно. Автофиксы не заменяют ревью: небезопасные правки иногда меняют порядок вычислений. И наконец, форматтер не гарантирует побайтового совпадения с black, хотя конфликтов на практике почти не возникает.

Чек-лист внедрения

  • Версия ruff зафиксирована и совпадает локально, в pre-commit и CI.
  • target-version совпадает с минимальной версией Python в проекте.
  • Форматтер запускается до линтера, а не наоборот.
  • Небезопасные исправления просматриваются вручную.
  • Исключения оформлены через per-file-ignores, а не через глобальный ignore.
  • В CI проверяются и check, и format —check.

Итог

ruff закрывает большую часть повседневных задач Python-разработчика: линтинг, сортировку импортов, модернизацию синтаксиса и форматирование. Один инструмент вместо пяти означает меньше конфигурации, меньше расхождений между машинами и заметно более быстрый feedback. Плата за это скромная: нужно один раз разобраться с наборами правил и не забывать про проверку типов другими средствами. Для нового проекта разумный старт — E, F, I, B, UP и форматтер в pre-commit; для старого — постепенное включение с —add-noqa.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте