Линтер Ruff (The Ruff Linter)
Линтер Ruff — очень быстрый линтер для Python, задуманный как замена Flake8 (плюс десятки плагинов), isort, pydocstyle, pyupgrade, autoflake и других.
ruff check
ruff check — основная команда для запуска линтера Ruff. Она принимает список файлов или каталогов и проверяет все найденные Python-файлы, при необходимости применяя исправления. При проверке каталога Ruff рекурсивно ищет Python-файлы в нём и во вложенных каталогах:
$ ruff check # Проверить файлы в текущем каталоге.
$ ruff check --fix # Проверить и автоматически исправить то, что можно.
$ ruff check --watch # Проверить и перезапускать проверку при изменениях.
$ ruff check path/to/code/ # Проверить файлы в path/to/code/.
Полный список опций: ruff check --help.
Выбор правил (Rule selection)
Набор включённых правил задаётся настройками lint.select, lint.extend-select и lint.ignore.
Линтер Ruff повторяет систему кодов правил Flake8: код состоит из префикса в 1–3 буквы и трёх цифр (например F401). Префикс указывает «источник» правила (например F — Pyflakes, E — pycodestyle, ANN — flake8-annotations).
Селекторы вроде lint.select и lint.ignore принимают полный код правила (например F401) или префикс (например F). Пример конфигурации:
pyproject.toml:
[tool.ruff.lint]
select = ["E", "F"]
ignore = ["F401"]
ruff.toml:
[lint]
select = ["E", "F"]
ignore = ["F401"]
В результате Ruff включит все правила с префиксами E (pycodestyle) и F (Pyflakes), кроме F401. Подробнее о настройке в pyproject.toml: Configuring Ruff.
Особый случай — код ALL: он включает все правила. Некоторые правила pydocstyle конфликтуют (например D203 и D211 — разные форматы docstring). При включении ALL Ruff автоматически отключает конфликтующие правила.
Рекомендации по настройке:
- Начните с небольшого набора правил (
select = ["E", "F"]) и добавляйте категории по одной. Например, расширьте доselect = ["E", "F", "B"]для flake8-bugbear. - Используйте
ALLосторожно: при обновлении Ruff будут автоматически включаться новые правила. - Лучше задавать явный набор через lint.select, чем lint.extend-select.
- Contributing
Пример конфигурации с популярными правилами (без излишней строгости):
pyproject.toml / ruff.toml:
[tool.ruff.lint]
select = [
"E", # pycodestyle
"F", # Pyflakes
"UP", # pyupgrade
"B", # flake8-bugbear
"SIM", # flake8-simplify
"I", # isort
]
Итоговый набор правил может собираться из нескольких источников: текущий pyproject.toml, наследуемые pyproject.toml, опции CLI (например --select). Ruff берёт select с наивысшим приоритетом как основу, затем применяет extend-select и ignore. Опции CLI имеют приоритет над pyproject.toml, текущий pyproject.toml — над наследуемыми.
Пример: при конфигурации select = ["E", "F"], ignore = ["F401"]:
ruff check --select F401— будет проверяться только правилоF401.ruff check --extend-select B— будут проверяться правилаE,FиB, кромеF401.
Исправления (Fixes)
Ruff умеет автоматически исправлять многие нарушения: удалять неиспользуемые импорты, переформатировать docstring, переписывать аннотации типов под новый синтаксис Python и т.д.
Чтобы включить исправления, передайте флаг --fix в ruff check:
$ ruff check --fix
По умолчанию Ruff применяет все «безопасные» исправления. Поддержка исправлений по правилам описана в Rules.
Безопасность исправлений (Fix safety)
Исправления делятся на «безопасные» (safe) и «небезопасные» (unsafe). Безопасные сохраняют смысл кода; небезопасные могут его изменить.
Небезопасное исправление может изменить поведение в runtime, удалить комментарии или и то и другое. Безопасные призваны сохранять поведение и удаляют комментарии только при удалении целых операторов/выражений (например неиспользуемого импорта).
Пример: правило unnecessary-iterable-allocation-for-first-element (RUF015) ищет неэффективное использование list(...)[0]. Исправление заменяет его на next(iter(...)), что может сильно ускорить код (в примере в документации — с ~1.69 с до ~70.8 нс на операцию). Но для пустой коллекции тип исключения меняется с IndexError на StopIteration, что может сломать обработку ошибок выше по коду, поэтому такое исправление считается небезопасным.
По умолчанию Ruff применяет только безопасные исправления. Небезопасные включаются настройкой unsafe-fixes в конфиге или флагом --unsafe-fixes:
# Показать небезопасные исправления
ruff check --unsafe-fixes
# Применить небезопасные исправления
ruff check --fix --unsafe-fixes
По умолчанию Ruff подсказывает, когда доступны небезопасные исправления. Подсказку можно отключить, задав unsafe-fixes в false или флагом --no-unsafe-fixes.
Безопасность исправлений можно менять по правилам через lint.extend-safe-fixes и lint.extend-unsafe-fixes. Например, сделать исправления для F601 безопасными, а для UP034 — небезопасными:
pyproject.toml / ruff.toml:
[tool.ruff.lint]
extend-safe-fixes = ["F601"]
extend-unsafe-fixes = ["UP034"]
Можно использовать префиксы (например F для всех правил Pyflakes).
При выводе в формате
jsonRuff всегда показывает все исправления; полеapplicabilityуказывает безопасность.Примечание
Отключение исправлений (Disabling fixes)
Ограничить набор правил, для которых Ruff может применять исправления, можно настройками lint.fixable / lint.extend-fixable и lint.unfixable.
Пример: включить исправления для всех правил, кроме unused-imports (F401):
[tool.ruff.lint]
fixable = ["ALL"]
unfixable = ["F401"]
Обратный пример — только исправления для F401:
[tool.ruff.lint]
fixable = ["F401"]
Подавление ошибок (Error suppression)
Ruff поддерживает несколько способов подавления срабатываний: ложные срабатывания или допустимые нарушения.
Конфигурация
Чтобы отключить правило везде, добавьте его в список ignore через lint.ignore в CLI, pyproject.toml или ruff.toml. Чтобы отключить по путям/шаблонам файлов — lint.per-file-ignores.
Ruff поддерживает комментарии подавления: построчные и файловые noqa, а также подавление по диапазону.
Построчное (Line-level)
Поддерживается система noqa, похожая на Flake8. Чтобы игнорировать одно срабатывание, в конец строки добавьте # noqa: {code}:
# Игнорировать F841.
x = 1 # noqa: F841
# Игнорировать E741 и F841.
i = 1 # noqa: E741, F841
# Игнорировать все срабатывания на строке.
x = 1 # noqa
Для многострочных строк (например docstring) директива noqa ставится в конце строки (после закрывающих тройных кавычек) и относится ко всей строке:
"""Lorem ipsum dolor sit amet.
...
""" # noqa: E501
Для сортировки импортов noqa ставится в конце первой строки блока импортов и действует на весь блок:
import os # noqa: I001
import abc
Спецификация inline-комментария: после совпадения (без учёта регистра) #noqa с опциональными пробелами после # и после noqa может идти :, затем список кодов правил (буквы и цифры, через пробелы или запятые). «Одеяльное» подавление — #noqa без : и кодов.
Блочное (Block-level)
Чтобы игнорировать срабатывания в диапазоне кода, используется комментарий «disable», затем парный «enable»:
# ruff: disable[E501]
VALUE_1 = "Lorem ipsum ..."
VALUE_2 = "Lorem ipsum ..."
# ruff: enable[E501]
Коды в «disable» и «enable» должны совпадать и идти в одном порядке; уровень отступа должен быть в пределах одного логического блока. Если парный «enable» не найден, Ruff считает диапазон «неявным» — от «disable» до выхода из области с меньшим отступом. Рекомендуется использовать явные диапазоны, чтобы не подавить лишнее; при неявном диапазоне выдаётся диагностика RUF104. Подавления по диапазону не позволяют включать правила, не выбранные в конфигурации или CLI. «Enable» только завершает предшествующий «disable» с теми же кодами. В отличие от noqa, в диапазоне нельзя подавить «всё» — нужно указать хотя бы один код.
Спецификация: коды через запятые, опциональные пробелы; отдельная строка с точным #ruff:, затем disable или enable, затем [, коды, ].
Файловое (File-level)
Чтобы игнорировать все срабатывания в файле, добавьте строку # ruff: noqa (лучше в начало файла). Для одного правила — # ruff: noqa: {code}. Файловый noqa должен быть на отдельной строке. Ruff также понимает директиву Flake8 # flake8: noqa как аналог # ruff: noqa.
Обнаружение неиспользуемых подавлений (Detecting unused suppressions)
Правило unused-noqa (код RUF100) проверяет, что подавления действительно относятся к срабатывающим нарушениям. Чтобы включить проверку: ruff check --extend-select RUF100. Удалить неиспользуемые подавления автоматически: ruff check --extend-select RUF100 --fix.
Добавление директив noqa (Inserting necessary suppression comments)
Флаг --add-noqa автоматически добавляет директивы noqa ко всем строкам с нарушениями (удобно при миграции кодовой базы на Ruff): ruff check /path/to/file.py --add-noqa.
Комментарии действий isort (isort action comments)
Ruff учитывает action comments isort (# isort: skip_file, # isort: on, # isort: off, # isort: skip, # isort: split) для выборочного включения/отключения сортировки импортов. Поддерживаются варианты с префиксом # ruff: (например # ruff: isort: skip_file). В отличие от isort, Ruff не учитывает такие комментарии внутри docstring. Подробнее: документация isort.
Коды выхода (Exit codes)
По умолчанию ruff check завершается с кодами:
- 2 — аварийное завершение (неверная конфигурация, неверные опции CLI или внутренняя ошибка).
- 1 — найдены нарушения.
- 0 — нарушений нет или все нарушения были автоматически исправлены.
Аналогично ESLint, Prettier, RuboCop.
Два флага меняют поведение кода выхода:
--exit-non-zero-on-fix— код выхода1, если нарушения были найдены, даже если все они исправлены автоматически (в итоге может быть не ноль при отсутствии оставшихся нарушений).--exit-zero— код выхода0даже при наличии нарушений. Код2при аварийном завершении сохраняется.