$ sudo teach IT
Модуль 4 · Конфигурация

Выбор правил: select и ignore

Тонкая настройка того, что проверяет Ruff.

⚙️ Конфигурация 🕓 ~25 минут
✔️

select — какие правила включить

Параметр select определяет, какие категории или конкретные правил будет применять Ruff при проверке. Это главный рычаг управления тем, что проверяет линтер. Без указания select Ruff использует значение по умолчанию — категории E (pycodestyle errors) и F (Pyflakes).

💡 По умолчанию Ruff включает только правила категории E и F. Остальные нужно добавлять явно через select или extend-select.

Минимальная конфигурация, которая включает стиль, логику и сортировку импортов:

[tool.ruff.lint]
select = ["E", "F", "I"]

Более строгий набор для серьёзных проектов:

[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B", "S", "C90"]

А если нужно включить абсолютно все правила:

[tool.ruff.lint]
select = ["ALL"]

Как указывать правила в select

Правила можно указывать на трёх уровнях детализации:

Уровень Пример Описание
Категория "F" Все правила категории (Pyflakes)
Подкатегория "F4" Все правила, начинающиеся на F4
Конкретное правило "F401" Только одно конкретное правило

Пример использования разных уровней:

[tool.ruff.lint]
select = [
    "E",        # вся категория pycodestyle errors
    "F4",       # только подкатегория F4 (логические ошибки Pyflakes)
    "N801",     # только конкретное правило — классы должны быть CamelCase
    "B",        # вся категория flake8-bugbear
    "I001",     # только конкретное правило — сортировка импортов (избыточно если есть вся I)
]
⚠️ Важно: Если вы указали категорию "I", то указывать "I001" отдельно уже не нужно — правило уже входит в категорию. Но это не ошибка, просто избыточно.
🚫

ignore — исключить правила

Параметр ignore позволяет исключить определённые правила из проверки. Он применяется поверх того, что было выбрано через select. Это удобно, когда вы включаете целую категорию, но хотите отключить несколько конкретных правил из неё.

[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B"]
ignore = [
    "E501",   # line too long — часто отключают в Django/FastAPI проектах
    "E711",   # comparison to None — иногда мешает при работе с SQLAlchemy
    "B008",   # function call in default argument — нужен в FastAPI для зависимостей
    "N818",   # exception class name — некоторые проекты используют свой нейминг
]

Правила, которые часто отключают

Правило Причина отключения Типичный сценарий
E501 Слишком длинные строки Django модели, SQL запросы, строки документации
B008 Вызов функции в аргументе по умолчанию FastAPI зависимости, Pydantic поля
B009 getattr/setattr с константами Магические методы, ORM маппинг
S101 Использование assert Тесты (pytest использует assert)
N818 Имя исключения без суффикса Error Кастомные исключения в бизнес-логике
ANN101 Пропущен self в аннотации Крупные codebase где не хотят везде писать self: Self

Как работает ignore: приоритет и конфликты

Механизм работы ignore при совместном использовании с select подчиняется следующим правилам:

  1. select определяет, какие правила вообще рассматриваются.
  2. ignore вычитается из набора, полученного на шаге 1.
  3. Если правило указано и в select, и в ignore — ignore имеет приоритет (правило будет отключено).
  4. Правила, не входящие ни в одну из выбранных категорий, не могут быть включены через ignore.
[tool.ruff.lint]
select = ["E", "F", "ARG"]    # включили категории
ignore = ["E501", "ARG002"]   # исключили два правила

# Итоговый набор:
# Все E, кроме E501
# Все F
# Все ARG, кроме ARG002
➕

extend-select и extend-ignore

Параметры extend-select и extend-ignore работают так же, как select и ignore, но добавляются к значениям по умолчанию, а не заменяют их. Это особенно полезно, когда вы хотите сохранить дефолтные правила E и F и добавить к ним новые категории, не перечисляя всё вручную.

[tool.ruff.lint]
# добавить к дефолтным правилам (E + F), не заменяя их
extend-select = ["I", "N", "UP"]

# добавить правило в ignore поверх дефолтного
extend-ignore = ["E501"]

Сравнение select vs extend-select

Подход Конфигурация Результат
Только дефолт ничего не указано E + F
select select = ["I", "N"] I + N (E и F потеряны!)
extend-select extend-select = ["I", "N"] E + F + I + N (сохранены)
⚠️ Важно: Если вы используете select, то полностью заменяете набор правил по умолчанию. Категории E и F не будут включены, если не указать их явно. extend-select этого недостатка лишён.

Практические сценарии использования

Сценарий 1: Вы начинаете проект и хотите постепенно ужесточать проверки.

# Этап 1: базовые проверки
[tool.ruff.lint]
extend-select = ["I"]   # E + F + I
# Этап 2: добавляем именование и типы
[tool.ruff.lint]
extend-select = ["I", "N", "UP", "ANN"]
# Этап 3: полный набор
[tool.ruff.lint]
extend-select = ["I", "N", "UP", "B", "S", "C90", "ANN", "ARG"]

Сценарий 2: В вашей компании есть базовый стандарт кодирования, но некоторые проекты добавляют свои правила.

# Базовый конфиг компании (в общем pyproject.toml)
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B"]

# Проект может расширить
# (в локальном pyproject.toml, который мержится с базовым)
extend-select = ["S", "C90"]     # добавляем безопасность и сложность
extend-ignore = ["B008"]         # отключаем B008 для FastAPI
🕵

Специальное значение ALL

Значение "ALL" в select включает все правила, известные Ruff. Это удобно для максимально строгой проверки, но может быть слишком шумным для больших проектов.

[tool.ruff.lint]
select = ["ALL"]
ignore = [
    "E501",    # отключаем длину строки
    "B008",    # отключаем вызовы в дефолтах
    "S101",    # отключаем assert для тестов
    "ANN001",  # отключаем обязательные аннотации для аргументов
    "D",       # отключаем docstrings (если их нет в проекте)
]
💡 Совет: Используйте select = ["ALL"] в новых проектах с нуля, чтобы сразу привить лучшие практики. Для существующих проектов лучше добавлять категории постепенно, иначе вы получите тысячи ошибок.

Когда вы используете "ALL", Ruff включает правила из всех категорий:

Префикс Категория Кол-во правил
Epycodestyle errors~50
Wpycodestyle warnings~20
FPyflakes~40
Iisort~5
Npep8-naming~30
UPpyupgrade~50
Bflake8-bugbear~60
Sflake8-bandit~60
C90mccabe~5
ANNflake8-annotations~20
ARGflake8-unused-arguments~5
Dpydocstyle~50
RUFRuff-specific~30
PLPylint~200
SIMflake8-simplify~50
T20flake8-print~3
PTflake8-pytest-style~20
RETflake8-return~10
SLFflake8-self~5
ERAeradicate (комментарии-код)~1
PDpandas-vet~20
NPYNumPy-specific~10
FAflake8-future-annotations~3
ISCflake8-implicit-str-concat~3
ICflake8-import-conventions~3
PTHflake8-use-pathlib~20
TDflake8-todos~5
DJflake8-django~15
ASYNCflake8-async~10
TCHflake8-type-checking~10
PYIflake8-pyi (stub files)~20
EXEflake8-executable~5
RSEflake8-raise~5
FURBrefurb~30
COMflake8-commas~10
FLYflynt (f-strings)~3
PERFperflint~15
DOCpydoclint~20
DTZflake8-datetimez~5
EMflake8-errmsg~5
Gflake8-logging-format~10
LOGflake8-logging~10
INPflake8-ini~3
PIEflake8-pie~20
Qflake8-quotes~3
Rruff-format~3
⚠️ Важно: Использование "ALL" с select заменяет все дефолтные настройки. Это означает, что если вы укажете select = ["ALL"], а потом extend-select = ["S"], то extend-select не добавит ничего нового, так как ALL уже включает S. Однако extend-select всё ещё работает, если базовый select не включает нужную категорию.
🔧

fixable и unfixable — управление автоисправлением

Не все правила Ruff могут автоматически исправлять код. Некоторые правки считаются безопасными (safe), другие — только предварительными (unsafe). Параметры fixable и unfixable управляют тем, какие правила могут применять автоисправления.

fixable — разрешить исправление

По умолчанию Ruff может исправлять все безопасные правила. Параметр fixable ограничивает этот набор:

[tool.ruff.lint]
# разрешить автоисправление только для импортов и pyupgrade
fixable = ["I", "UP"]

# все остальные правила (E, F, B, S и т.д.) будут только предупреждать,
# но не исправлять код автоматически

Также можно использовать "ALL":

[tool.ruff.lint]
# разрешить автоисправление для всех правил
fixable = ["ALL"]

unfixable — запретить исправление

Противоположность fixable. Позволяет отключить автоисправление для конкретных правил, не отключая проверку целиком:

[tool.ruff.lint]
# не удалять неиспользуемые импорты автоматически
unfixable = ["F401"]

# не исправлять мутируемые дефолты
unfixable = ["B006"]

Safe vs Unsafe исправления

Тип Флаг Описание Пример
Safe --fix Исправления, которые точно не меняют смысл кода Удаление неиспользуемого импорта
Unsafe --unsafe-fixes Исправления, которые могут изменить поведение программы Замена типа, удаление assert
# только безопасные исправления
ruff check --fix

# включая небезопасные (нужно подтверждение)
ruff check --fix --unsafe-fixes
⚠️ Важно: Параметры fixable и unfixable определяют максимально возможный набор исправлений. Даже если правило разрешено в fixable, Ruff применит только безопасные исправления без флага --unsafe-fixes.

Комбинирование fixable и unfixable

Два параметра работают в связке: сначала fixable определяет белый список, затем unfixable вычитает из него правила:

[tool.ruff.lint]
fixable = ["ALL"]             # разрешить всё
unfixable = ["F401", "B006"]  # но не исправлять F401 и B006

# Итог: все безопасные исправления, кроме F401 и B006
💻

CLI vs Конфигурация: приоритет

Ruff поддерживает настройку правил как через конфигурационные файлы (pyproject.toml, ruff.toml), так и через аргументы командной строки. Важно понимать, как эти два источника взаимодействуют.

Приоритет настроек (от высшего к низшему)

  1. Аргументы CLI — имеют наивысший приоритет.
  2. CONFIG в CLI — параметр --config path/to/config указывает на конкретный файл.
  3. Локальный конфиг — ruff.toml или pyproject.toml в корне проекта.
  4. Пользовательский конфиг — ~/.config/ruff/ruff.toml (или ~/.ruff.toml).
  5. Значения по умолчанию — встроенные в Ruff.

Примеры CLI-переопределений

# Временно добавить категорию (не меняя конфиг)
ruff check --select I

# Отключить правило при запуске
ruff check --ignore E501

# Включить все правила
ruff check --select ALL

# Применить исправления
ruff check --fix

# Использовать конкретный конфиг
ruff check --config /path/to/ruff.toml

# Посмотреть, какие правила активны
ruff check --show-settings

Как CLI переопределяет конфиг

Параметр в CLI Поведение
--select Заменяет select из конфига полностью
--ignore Добавляется к ignore из конфига
--fix Включает режим исправления, независимо от конфига
--show-settings Показывает итоговую конфигурацию (полезно для отладки)

Пример отладки:

# Посмотреть какие правила будут применены
ruff check --show-settings

# Вывод покажет:
# - Какой конфиг был найден
# - select / ignore / fixable / unfixable
# - per-file-ignores
# - Все остальные настройки
💡 Совет: Используйте ruff check --show-settings для отладки, когда не уверены, какие правила активны. Это особенно полезно при работе с несколькими конфигурационными файлами или наследованием настроек.
🔄

extend-fixable

Аналогично extend-select, параметр extend-fixable добавляет правила к списку исправляемых, не заменяя его полностью. Это полезно, когда вы используете чей-то базовый конфиг и хотите добавить свои правила в автоисправление:

[tool.ruff.lint]
# Базовый конфиг: fixable = ["I"]
# Добавляем UP к исправляемым
extend-fixable = ["UP"]

# Итог: fixable = ["I", "UP"] (если базовый конфиг не переопределён)
🛠

Практические примеры конфигураций

Пример 1: Минимальная конфигурация для нового проекта

[tool.ruff]
line-length = 88

[tool.ruff.lint]
extend-select = ["I", "N", "UP", "B"]
ignore = ["E501"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"

Пример 2: Строгая конфигурация для open-source библиотеки

[tool.ruff]
line-length = 100
target-version = "py311"

[tool.ruff.lint]
select = ["ALL"]
ignore = [
    "D203", "D212", "D213", "D214", "D215",
    "PLR0913",  # too-many-arguments
    "ANN101",   # missing-type-self
    "ANN102",   # missing-type-cls
    "S101",     # assert used
]
fixable = ["ALL"]
unfixable = ["F401"]

[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["S101", "ANN", "D"]
"__init__.py" = ["F401", "D"]

[tool.ruff.format]
quote-style = "double"

Пример 3: Конфигурация для Django проекта

[tool.ruff]
line-length = 120

[tool.ruff.lint]
extend-select = [
    "I", "N", "UP", "B", "S", "C90",
    "DJ",       # Django-specific rules
    "ARG",      # unused arguments
]
ignore = [
    "E501",     # строки в Django бывают длинными
    "B008",     # FastAPI-like patterns в Django тоже
    "S105",     # hardcoded passwords — часто ложно
    "S106",     # hardcoded passwords
    "DJ008",    # model meta
]

[tool.ruff.lint.per-file-ignores]
"*/migrations/*.py" = ["ALL"]
"*/tests/**/*.py" = ["S101", "ANN", "D", "DJ"]
"*/settings.py" = ["S105", "S106"]
"*/admin.py" = ["D"]
"*/models.py" = ["D"]

[tool.ruff.format]
quote-style = "double"

Пример 4: Постепенное внедрение в legacy проекте

Если у вас большой существующий проект, не стоит включать сразу все правила. Лучше внедрять Ruff постепенно:

# Этап 1: только самые важные проблемы
[tool.ruff.lint]
extend-select = ["F", "I"]   # логика + импорты
# F404: undefined name — помогает находить реальные баги
# F401: unused import — очищает код
# Этап 2: добавляем стиль и именование
[tool.ruff.lint]
extend-select = ["F", "I", "E", "N"]
ignore = ["E501"]  # слишком много нарушений в старом коде
# Этап 3: добавляем баги и безопасность
[tool.ruff.lint]
extend-select = ["F", "I", "E", "N", "B", "S", "UP"]
ignore = ["E501", "B008"]
# Этап 4: полный набор
[tool.ruff.lint]
extend-select = ["F", "I", "E", "N", "B", "S", "UP", "C90", "ANN", "ARG"]
ignore = ["E501", "B008", "ANN001", "ANN201"]
🔎

Проверка и отладка конфигурации

Ruff предоставляет несколько инструментов для проверки текущей конфигурации:

ruff check --show-settings

Показывает полную конфигурацию, которую Ruff использует для текущего проекта:

ruff check --show-settings

# Пример вывода (сокращён):
# Resolved settings:
#   - config: /path/to/project/pyproject.toml
#   - select: ["E", "F", "I", "N", "UP"]
#   - ignore: ["E501"]
#   - fixable: ["I", "N", "UP"]
#   - unfixable: []
#   - per-file-ignores: {"__init__.py": ["F401"]}
#   - line-length: 88
#   - target-version: py311

ruff check --only

Позволяет запустить только одно правило (игнорируя все остальные). Полезно для отладки конкретного правила:

# Проверить только F401 во всём проекте
ruff check --only F401

# Проверить только B006 в конкретном файле
ruff check --only B006 src/module.py
💡 Совет: --only игнорирует select и ignore — он включает только указанное правило. Это идеальный способ проверить, какие проблемы конкретное правило найдёт в вашем коде.
⚠

Частые ошибки и их решения

❌ Ошибка: Я указал select = ["I", "N"], но Ruff перестал проверять E и F.
✅ Решение: Параметр select заменяет значения по умолчанию, а не дополняет их. Используйте extend-select вместо select, если хотите сохранить E и F: extend-select = ["I", "N"].
❌ Ошибка: Я добавил "ALL" в select, но Ruff не проверяет import сортировку.
✅ Решение: "ALL" включает все правила, включая I. Возможно, у вас включён ignore = ["I"] или используется ruff check --no-fix без явного исправления. Проверьте ruff check --show-settings.
❌ Ошибка: Ruff не применяет исправления для правила S105.
✅ Решение: Не все правила имеют автоисправления. Если у правила нет fix-режима, оно работает только как предупреждение. Проверьте документацию правила через ruff rule S105.
❌ Ошибка: У меня в конфиге select = ["ALL"], но Ruff выдаёт слишком много ошибок.
✅ Решение: Используйте ignore для отключения самых шумных правил. Для больших проектов не используйте "ALL" — лучше включайте категории по одной. Начните с ["E", "F", "I"] и добавляйте остальные постепенно.
❌ Ошибка: Я использую extend-select, но Ruff всё равно проверяет только E и F.
✅ Решение: Проверьте, не указан ли в конфиге select выше по иерархии. Если в пользовательском конфиге (~/.config/ruff/ruff.toml) указан select, то локальный extend-select будет расширять именно его, а не дефолт. Используйте ruff check --show-settings для диагностики.
📝

Итоги

Параметр Назначение Заменяет дефолт?
select Какие правила включить Да
extend-select Добавить к текущему select Нет
ignore Какие правила отключить Нет (добавляется)
extend-ignore Добавить к текущему ignore Нет
fixable Какие правила можно исправлять Да
unfixable Какие правила нельзя исправлять Нет (вычитается)
💡 Главные выводы:
  1. select полностью заменяет дефолтный набор. extend-select — добавляет к нему.
  2. ignore всегда вычитается из select, независимо от способа указания.
  3. ALL включает все возможные правила, но может быть шумным.
  4. Настройки CLI имеют приоритет над конфигурационными файлами.
  5. Используйте ruff check --show-settings для отладки.

Урок 4.2: select и ignore

5 вопросов