ruff check — базовая проверка
Флаги, фильтрация правил и статистика.
Что такое ruff check
ruff check — это основная команда для статического анализа кода. Она проверяет Python-файлы на соответствие набору правил (линтеров) и выводит все найденные проблемы. В отличие от форматирования, линтинг находит потенциальные ошибки, несоответствия стандартам и подозрительные конструкции.
Ruff включает более 800 правил, сгруппированных по категориям. Каждое правило имеет уникальный код, состоящий из префикса категории и номера. Например, F401 означает категорию Pyflakes (F), правило 401 — импортирован, но не используется.
Категории правил
Все правила Ruff разделены на категории, каждая со своим префиксом. Вот основные:
| Префикс | Категория | Источник | Описание |
|---|---|---|---|
| E | pycodestyle | pycodestyle | Ошибки стиля: лишние пробелы, пустые строки, длина строк |
| W | pycodestyle | pycodestyle | Предупреждения стиля (менее строгие, чем E) |
| F | Pyflakes | Pyflakes | Логические ошибки: неиспользуемые импорты, неопределённые переменные |
| I | isort | isort | Сортировка импортов |
| N | pep8-naming | pep8-naming | Именование: snake_case, CamelCase, константы |
| UP | pyupgrade | pyupgrade | Современный синтаксис: f-строки, set literal, union types |
| B | flake8-bugbear | flake8-bugbear | Баги и подозрительный код: мутация аргументов по умолчанию |
| S | flake8-bandit | bandit | Безопасность: SQL инъекции, shell injection, опасные функции |
| SIM | flake8-simplify | flake8-simplify | Упрощение кода: замена сложных конструкций на простые |
| ANN | flake8-annotations | flake8-annotations | Тип-хинты: отсутствие аннотаций функций |
| PT | flake8-pytest-style | pytest-style | Стиль тестов pytest: assert вместо self.assert*, fixture naming |
| D | pydocstyle | pydocstyle | Документация: docstring-и, их формат и содержание |
Каждая категория включает десятки правил. Например, категория F (Pyflakes) включает: F401 (неиспользуемый импорт), F402 (импорт, скрывающий имя из внешней области), F403 (from import * — неопределённые имена) и так далее.
Базовый запуск
Запустим проверку на демо-файле. Создайте файл example.py:
import os # неиспользуемый импорт
import sys
x = 1
if x == 1:
print("hello")
y = [1,2,3] # лишние пробелы
z = x+1 # пробелы вокруг оператора
class myclass: # неправильное именование
pass
Запускаем:
ruff check example.py
Вывод будет выглядеть так:
example.py:1:1: F401 `os` imported but unused
example.py:2:1: I001 Missing required import: `os` should be placed after `sys`
example.py:10:10: E201 Whitespace after '['
example.py:10:14: E202 Whitespace before ']'
example.py:11:7: E225 Missing whitespace around operator
example.py:13:7: N801 Class name `myclass` should use CapWords convention
Каждая строка вывода содержит: путь к файлу, номер строки, номер колонки, код правила и описание. Можно сразу понять, где и что нарушено.
--select и --ignore
Флаги --select и --ignore позволяют управлять правилами прямо из командной строки без конфигурационного файла. Это удобно для быстрых проверок и CI-скриптов.
--select — включает только указанные правила. Все остальные будут проигнорированы:
ruff check --select E . # только pycodestyle ошибки
ruff check --select F401 . # только конкретное правило
ruff check --select E,F,I . # несколько категорий
ruff check --select ALL . # включить все правила
ruff check --select E,W,F . # смесь категорий
--ignore — исключает указанные правила из проверки. Всё остальное проверяется:
ruff check --ignore E501 . # всё кроме E501 (слишком длинные строки)
ruff check --ignore F401,F841 . # не проверять неиспользуемые импорты и переменные
ruff check --ignore E,W . # игнорировать все стилистические правила
--select ALL включит очень много правил — более 800. Часть из них может конфликтовать или быть излишне строгой. Использовать осторожно, особенно на больших проектах.
Как работает выбор правил
Понимание механики выбора правил важно для эффективной настройки Ruff. Вот как это работает:
| Флаг / Конфиг | Поведение |
|---|---|
select |
Полностью заменяет набор правил. Указываются только те правила, которые должны проверяться. Всё остальное отключается. |
extend-select |
Добавляет правила к тем, что уже включены (по умолчанию или из select). Не заменяет, а расширяет. |
ignore |
Исключает конкретные правила из текущего набора. Применяется после select и extend-select. |
fixable / unfixable |
Управляют тем, какие правила могут автоматически исправлять код. Независимо от выбора правил. |
Порядок обработки: сначала Ruff собирает базовый набор select, затем добавляет extend-select, затем применяет ignore. Финальный набор используется для проверки.
# В конфиге: select = ["E", "F", "I"]
# Из командной строки:
ruff check --extend-select B --ignore E501 .
# Результат: E, F, I, B — все кроме E501
# (потому что ignore вырезает E501 из набора)
# Полная замена через CLI:
ruff check --select ALL --ignore E501,W .
# Только ALL кроме E501 и W
Важный нюанс: select в CLI полностью переопределяет select из конфиг-файла. Если вам нужно добавить правила к конфигу, используйте --extend-select.
--extend-select
--extend-select — это ключевое отличие от --select. Он добавляет правила к уже существующему набору, не заменяя его. Это особенно полезно, когда у вас есть конфигурационный файл с базовым набором правил, а в CLI вы хотите временно добавить дополнительные проверки:
# добавить проверки безопасности к дефолтным правилам
ruff check --extend-select S .
# добавить isort + bugbear
ruff check --extend-select I,B .
# добавить проверки документации и типов
ruff check --extend-select D,ANN .
# комбинация: безопасность + упрощение кода
ruff check --extend-select S,SIM .
# в конфиге: select = ["E", "F", "I"]
# в CLI добавляем:
ruff check --extend-select UP .
# итог: E, F, I, UP
select vs extend-select: таблица сравнения
| Характеристика | select | extend-select |
|---|---|---|
| Действие | Заменяет весь набор правил | Добавляет к существующему набору |
| CLI переопределяет конфиг | Да, полностью | Нет, дополняет |
| Использование в CI | Чёткий фиксированный набор | Добавление временных проверок |
| Можно использовать несколько раз | Последний перезаписывает предыдущие | Все значения объединяются |
| Работа с ignore | Ignore применяется после select | Ignore применяется после extend-select |
| Рекомендуемый сценарий | Определение набора в конфиге | Временное добавление из CLI |
select в конфигурационном файле для постоянного набора, а --extend-select в CLI для временных проверок.
Примеры с реальным кодом
Рассмотрим реальные примеры кода и то, какие правила Ruff к ним применит.
Пример 1: Неиспользуемые импорты и переменные
import math # F401: неиспользуемый импорт
import random
from datetime import datetime
def calculate():
unused_var = 42 # F841: присвоено, но не используется
x = random.randint(1, 10)
now = datetime.now()
print(f"Random number: {x}")
# math не используется — F401
Пример 2: Проблемы безопасности
import subprocess # S404: опасный модуль
user_input = "some; rm -rf /"
# S605: shell=True — инъекция
subprocess.call(f"echo {user_input}", shell=True)
password = "secret123" # S105: пароль в коде
eval("print('hello')") # S307: опасный eval
Пример 3: Баги и подозрительный код
def append_to(element, target=[]): # B006: изменяемый аргумент по умолчанию
target.append(element)
return target
result = append_to(1) # [1]
result = append_to(2) # [1, 2] — баг! список сохраняется между вызовами
def check_value(x):
if x == None: # E711: сравнение с None через ==
return False
if x is not None:
if x > 10:
return True
return False
# B007: переменная цикла не используется
for i in range(10):
print("hello")
Пример 4: Именование (PEP8)
class person: # N801: должно быть CapWords (Person)
pass
def DoSomething(): # N802: должно быть snake_case (do_something)
pass
MY_CONSTANT = 10 # правильно
myConstant = 20 # N815: mixCase в глобальной области
class MyClass:
def getData(self): # N802: должно быть get_data
return self._data
ruff check --help или посети официальную документацию на docs.astral.sh/ruff.
--statistics — статистика нарушений
Флаг --statistics выводит сводку по каждому правилу: сколько раз оно было нарушено. Это невероятно полезно, когда вы начинаете линтить большой существующий проект — сразу видно, какие правила нарушаются чаще всего, и можно принять решение: исправлять массово или временно отключить.
ruff check --statistics .
# F401 12 `module` imported but unused
# E501 8 Line too long (88 > 79 characters)
# I001 5 Import block is unsorted
# F841 3 Local variable is assigned to but never used
# E302 2 Expected 2 blank lines after class or function definition
# N802 2 Function name should be lowercase
#
# Total: 32 violations across 6 rules
Без --statistics Ruff выводит каждое нарушение отдельно. С этим флагом — краткую сводку. Комбинировать с другими флагами:
# статистика только по выбранным правилам
ruff check --select E --statistics .
# статистика с игнорированием
ruff check --ignore E501 --statistics .
# можно сохранить вывод в файл
ruff check --statistics . > lint_stats.txt
Пример использования в процессе миграции:
- Запустить
ruff check --statistics .на проекте - Посмотреть топ нарушений
- Добавить самые частые в
ignoreвременно - Постепенно исправлять и убирать из ignore
--show-files — какие файлы проверяются
Флаг --show-files выводит список файлов, которые Ruff будет проверять с учётом всех настроек включения/исключения (include, exclude, extend-exclude). Это помогает отладить конфигурацию, когда вы не уверены, почему какой-то файл проверяется или не проверяется.
ruff check --show-files .
# src/main.py
# src/utils.py
# src/models.py
# tests/test_main.py
Полезно, когда у вас настроены exclude-правила:
# pyproject.toml
[tool.ruff]
exclude = ["migrations/", "build/", "*.generated.py"]
# CLI — посмотреть что осталось
ruff check --show-files .
Вывод --show-files учитывает все настройки: src (корневая директория), include (какие файлы включать по расширению) и exclude.
CLI vs конфигурационный файл: приоритет
Важно понимать, как CLI-флаги взаимодействуют с настройками из конфигурационного файла. Общий принцип: CLI-флаги имеют более высокий приоритет, но есть нюансы.
| Настройка | CLI флаг | Приоритет |
|---|---|---|
| select | --select |
CLI полностью заменяет конфиг |
| extend-select | --extend-select |
CLI дополняет конфиг (оба активны) |
| ignore | --ignore |
CLI добавляется к ignore из конфига |
| fix | --fix |
CLI включает, независимо от конфига |
| line-length | Нет CLI флага | Только конфиг |
| exclude | --exclude |
CLI заменяет exclude из конфига |
Важно: --select из CLI полностью переопределяет select из конфига. Если в конфиге указано select = ["E", "F"], а вы запускаете ruff check --select B ., то будут проверяться только правила B. Именно поэтому существует --extend-select, который не заменяет, а дополняет.
--select в CLI, убедитесь, что это действительно то, что вам нужно. Частая ошибка — случайная перезапись всех настроек конфига.
Практические сценарии
Сценарий 1: Быстрая проверка нового кода
# проверить только новые изменения (через git)
ruff check --select ALL $(git diff --name-only --diff-filter=AM)
# или просто проверить файлы в staged
ruff check --select E,F,I $(git diff --cached --name-only)
Сценарий 2: CI с поэтапным внедрением
# Этап 1: только критические ошибки
ruff check --select F --statistics .
# Этап 2: добавить стиль
ruff check --select F,E --statistics .
# Этап 3: полная проверка с исключениями
ruff check --select ALL --ignore E501,D --statistics .
Сценарий 3: Поиск конкретных проблем
# найти все проблемы безопасности
ruff check --select S .
# найти все проблемы с производительностью (flake8-perflint)
ruff check --select PERF .
# найти все, что связано с logging
ruff check --select LOG .
alias ruffci="ruff check --select ALL --ignore E501,D ."
Подавление предупреждений: noqa
Иногда нужно подавить предупреждение для конкретной строки. Для этого используется комментарий # noqa:
import os # noqa: F401 — подавить только правило F401
import sys # noqa — подавить все правила на этой строке
x = 1 + 2 # noqa: E225
# Можно указать несколько правил через запятую
long_variable_name = "very long string that exceeds line length" # noqa: E501, W291
# Подавить на уровне файла — в начале файла
# ruff: noqa: F401, E501
Можно также настроить noqa в конфиге — например, требовать обязательного указания кода правила (чтобы не было голых # noqa):
# pyproject.toml
[tool.ruff.lint]
select = ["E", "F", "I"]
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] # во всех __init__.py игнорировать F401
"tests/*.py" = ["E501"] # в тестах игнорировать длину строк
# noqa: XXX, а не просто # noqa. Это документирует, почему подавлено предупреждение, и не скрывает новые ошибки.
per-file-ignores — игнорирование по файлам
Настройка per-file-ignores позволяет игнорировать определённые правила для конкретных файлов или групп файлов (по glob-паттерну). Это удобно, когда определённые типы файлов имеют свои особенности:
# pyproject.toml
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401", "F403"] # в __init__.py часто "from . import *"
"tests/**/*.py" = ["E501", "S101"] # в тестах — длинные строки и assert
"migrations/**/*.py" = ["ALL"] # миграции — вообще не проверять
"examples/*.py" = ["E501"] # примеры могут иметь длинные docstring
"scripts/**/*.py" = ["N802"] # в скриптах могут быть функции без snake_case
Эта настройка работает через конфигурационный файл, но её нельзя передать через CLI напрямую.
Резюме
| Флаг | Назначение |
|---|---|
--select |
Выбрать только указанные правила (заменяет конфиг) |
--extend-select |
Добавить правила к существующему набору |
--ignore |
Исключить указанные правила |
--statistics |
Показать статистику нарушений |
--show-files |
Показать какие файлы будут проверяться |
# noqa |
Подавить предупреждение в конкретной строке |
per-file-ignores |
Игнорировать правила для групп файлов |
Шпаргалка: популярные комбинации
# Минимальная проверка (по умолчанию)
ruff check .
# Стандартный набор для нового проекта
ruff check --select E,F,I,N,UP,B .
# Полная проверка безопасности
ruff check --select S .
# Для CI/CD — только критические ошибки
ruff check --select F --statistics .
# Для существующего проекта — без длины строк
ruff check --ignore E501 .
# Для Jupyter Notebooks
ruff check notebook.ipynb .
# Только сортировка импортов
ruff check --select I --fix .
# Проверка с автоприменением безопасных исправлений
ruff check --fix --select E,F,I .
# Статистика по всем правилам
ruff check --select ALL --statistics . 2>/dev/null
Урок 2.1: ruff check — флаги и опции
5 вопросов