$ sudo teach IT
Модуль 2 · Линтинг

Режим наблюдения и форматы вывода

--watch для разработки, JSON и GitHub-формат для CI.

🔧 Практика 🕐 ~30 минут
🗺️

Обзор возможностей вывода

Ruff предлагает множество форматов вывода и режимов работы, которые делают его удобным как для разработки, так и для CI/CD. В этом уроке мы рассмотрим:

  • --watch — режим наблюдения за файлами в реальном времени
  • --output-format — различные форматы вывода (text, json, github, gitlab, pylint, concise, full)
  • --show-files — какие файлы проверяются
  • --show-settings — какие настройки активны
  • --show-source — показать исходный код с ошибкой
  • --preview — включить preview-правила

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

👁️

--watch — режим наблюдения

Флаг --watch запускает Ruff в режиме непрерывного наблюдения. Ruff следит за файловой системой и автоматически перезапускает проверку при любых изменениях (сохранении файлов). Это невероятно удобно держать в отдельном окне терминала или в split-панели редактора во время разработки.

Как это работает: Ruff использует notify (кросс-платформенную библиотеку для отслеживания файловой системы), которая эффективно отслеживает изменения без постоянного опроса диска. При каждом изменении Ruff перепроверяет только изменённый файл, а не весь проект, что делает режим очень быстрым даже на больших проектах.

# базовый режим наблюдения
ruff check --watch .

# с выбором правил
ruff check --select E,F,I --watch .

# с автоисправлением (безопасным)
ruff check --fix --watch .

# с предпросмотром — не работает с --watch (только diff)
# но можно комбинировать с --output-format
ruff check --output-format concise --watch .

# наблюдение за конкретной папкой
ruff check --watch src/

Практический сценарий: Откройте терминал, разделённый на две панели. В одной — редактор (VS Code, vim и т.д.), в другой —:

ruff check --select ALL --ignore E501 --watch .
# Теперь при каждом сохранении файла вы сразу видите все ошибки.
💡 --watch особенно полезен, когда вы изучаете новое правило или набор правил. Включите правило, смотрите на ошибки в реальном времени и сразу исправляйте их.
📄

--output-format: обзор всех форматов

Флаг --output-format позволяет выбрать формат вывода результатов проверки. Доступные форматы:

Формат Описание Использование
text Стандартный текстовый вывод (по умолчанию) Разработка, локальное использование
concise Сокращённый текст, по одному нарушению на строку Терминал, быстрый просмотр
json JSON-формат для программной обработки CI/CD, кастомные инструменты, парсинг
github GitHub Actions аннотации GitHub Actions CI
gitlab GitLab CI Code Quality формат GitLab CI
pylint Совместимость с pylint Миграция с pylint
full Полный формат с исходным кодом для каждого нарушения Детальный анализ, обучение
ruff check --output-format text .      # дефолтный текстовый (по умолчанию)
ruff check --output-format concise .   # компактный
ruff check --output-format json .      # JSON для парсинга
ruff check --output-format github .    # GitHub Actions аннотации
ruff check --output-format gitlab .    # GitLab CI формат
ruff check --output-format pylint .    # совместимость с pylint
ruff check --output-format full .      # полный с исходным кодом
💡 Формат по умолчанию — text. Его можно изменить в конфигурационном файле: output-format = "concise" в секции [tool.ruff].
📝

Форматы text и concise

text (по умолчанию) выводит каждое нарушение с полной информацией: путь, строка, колонка, код правила и сообщение:

# text:
src/main.py:10:5: F841 Local variable `unused` is assigned to but never used
src/main.py:15:1: E302 Expected 2 blank lines after class or function definition, found 1
src/main.py:20:89: E501 Line too long (95 > 88 characters)
src/main.py:25:1: I001 Import block is unsorted or incorrectly formatted.
src/main.py:30:5: UP015 Unnecessary open mode parameters
Found 5 errors.

concise — более компактный формат, где путь выводится один раз для группы нарушений в одном файле:

# concise:
src/main.py:10:5: F841 Local variable `unused` is assigned to but never used
src/main.py:15:1: E302 Expected 2 blank lines after class or function definition, found 1
src/main.py:20:89: E501 Line too long (95 > 88 characters)
src/main.py:25:1: I001 Import block is unsorted
src/main.py:30:5: UP015 Unnecessary open mode parameters
found 5 errors

Разница в том, что concise не выводит разделители между файлами и заголовки, делая вывод более плотным. Удобно для терминала с ограниченной высотой.

🔧

JSON формат — для программной обработки

Формат json выводит все нарушения в структурированном JSON-виде, который можно парсить в любом языке программирования. Это основа для интеграции Ruff с кастомными CI-системами, дашбордами качества кода и другими инструментами.

Пример вывода JSON:

ruff check --output-format json . > report.json
# Содержимое report.json:
{
  "files": {
    "/path/to/src/main.py": {
      "cell": null,
      "messages": [
        {
          "code": "F841",
          "message": "Local variable `unused` is assigned to but never used",
          "fix": null,
          "location": {
            "row": 10,
            "column": 5
          },
          "end_location": {
            "row": 10,
            "column": 11
          },
          "filename": "/path/to/src/main.py",
          "noqa_row": 10,
          "url": null
        }
      ]
    }
  },
  "summary": {
    "raw": "Found 5 errors.",
    "violations": 5
  }
}

Пример парсинга JSON на Python:

import json
import subprocess

# запускаем ruff и парсим JSON
result = subprocess.run(
    ["ruff", "check", "--output-format", "json", "."],
    capture_output=True, text=True
)
data = json.loads(result.stdout)

# анализируем
for file_path, file_data in data["files"].items():
    for msg in file_data["messages"]:
        code = msg["code"]
        row = msg["location"]["row"]
        col = msg["location"]["column"]
        print(f"{file_path}:{row}:{col}: {code} - {msg['message']}")

# группировка по коду правила
from collections import Counter
codes = Counter()
for file_data in data["files"].values():
    for msg in file_data["messages"]:
        codes[msg["code"]] += 1
print("Статистика:", codes.most_common())

# фильтрация: только ошибки безопасности
security_issues = []
for file_path, file_data in data["files"].items():
    for msg in file_data["messages"]:
        if msg["code"].startswith("S"):
            security_issues.append((file_path, msg))
print(f"Найдено {len(security_issues)} проблем безопасности")
🐙

Формат github — GitHub Actions аннотации

Формат github создаёт аннотации в формате GitHub Actions. Это позволяет отображать ошибки линтинга прямо в интерфейсе GitHub — как комментарии к строкам кода в Pull Request и во вкладке Files changed.

# вывод в формате github:
ruff check --output-format github .
# ::error file=src/main.py,line=10,col=5,title=F401::`os` imported but unused
# ::error file=src/main.py,line=15,col=1,title=E302::Expected 2 blank lines...
# ::warning file=src/main.py,line=20,col=89,title=E501::Line too long...

Пример workflow для GitHub Actions:

# .github/workflows/lint.yml
name: Lint
on: [push, pull_request]
jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/ruff-action@v2
        with:
          args: check --output-format github --select E,F,I,N,UP,B

Формат сообщений GitHub Actions:

  • ::error file={path},line={line},col={col},title={code}::{message} — ошибка
  • ::warning file={path},line={line},col={col},title={code}::{message} — предупреждение
  • ::notice file={path},line={line},col={col},title={code}::{message} — заметка
💡 Ruff автоматически определяет severity (error/warning/notice) на основе типа правила. Критические правила (F, B) — error, стилистические (E, W) — warning.
🦊

Формат gitlab — GitLab CI Code Quality

Формат gitlab генерирует отчёт в формате GitLab Code Quality. GitLab может отображать эти отчёты в интерфейсе Merge Request, показывая ошибки прямо в diff.

# вывод в формате gitlab:
ruff check --output-format gitlab . > gl-code-quality-report.json
# [
#   {
#     "description": "`os` imported but unused",
#     "check_name": "F401",
#     "fingerprint": "abc123...",
#     "severity": "major",
#     "location": {
#       "path": "src/main.py",
#       "lines": { "begin": 10 }
#     }
#   }
# ]

# .gitlab-ci.yml
ruff:
  script:
    - ruff check --output-format gitlab . > gl-code-quality-report.json
  artifacts:
    reports:
      codequality: gl-code-quality-report.json
🔄

Формат pylint — обратная совместимость

Формат pylint эмулирует вывод pylint. Это полезно при миграции с pylint на Ruff — ваши существующие инструменты и парсеры продолжат работать без изменений.

# pylint-совместимый вывод:
ruff check --output-format pylint .
# src/main.py:10:5: F401: `os` imported but unused
# src/main.py:15:1: E302: Expected 2 blank lines...
# src/main.py:20:89: E501: Line too long...

# теперь можно передавать в инструменты, ожидающие pylint:
ruff check --output-format pylint . | pylint-report-parser
📖

Формат full — с исходным кодом

Формат full выводит каждое нарушение вместе с фрагментом исходного кода. Это самый информативный формат, который помогает быстро понять контекст ошибки.

# full формат:
ruff check --output-format full .
# src/main.py:10:5: F841 Local variable `unused` is assigned to but never used
#   |
# 9 | def foo():
# 10 |     unused = 42
#   |     ^^^^^^ F841
# 11 |     return True
#   |

# src/main.py:15:1: E302 Expected 2 blank lines after class definition
#   |
# 14 | class Foo:
# 15 |     pass
#   | ^^^^^^^^ E302
#   |

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

📁

--show-files — какие файлы проверяются

Флаг --show-files выводит список файлов, которые Ruff будет проверять с учётом всех настроек включения/исключения. Это помогает отладить конфигурацию, особенно когда вы используете сложные правила include, exclude, extend-exclude или src.

# показать какие файлы будут проверены
ruff check --show-files .

# пример вывода:
# src/main.py
# src/utils.py
# src/models.py
# tests/test_main.py
# tests/test_utils.py

# с --show-files можно увидеть эффект exclude:
ruff check --exclude tests --show-files .
# src/main.py
# src/utils.py
# src/models.py

Полезно, когда у вас настроены include/exclude паттерны и вы хотите убедиться, что нужные файлы проверяются, а ненужные — нет.

⚙️

--show-settings — текущая конфигурация

Флаг --show-settings выводит все активные настройки Ruff для текущего проекта. Он объединяет настройки из конфигурационных файлов (pyproject.toml, ruff.toml) и CLI-флагов, показывая итоговую конфигурацию. Это незаменимый инструмент для отладки, когда вы не уверены, какие настройки реально активны.

ruff check --show-settings .

# пример вывода (сокращён):
# ================================================================
# Settings for: /path/to/project
# ================================================================
# 
# [general]
# src = ["src"]
# exclude = [".bzr", ".direnv", ".eggs", ".git", ".git-rewrite",
#            ".hg", ".ipynb_checkpoints", ".mypy_cache", ".nox",
#            ".pants.d", ".pyenv", ".pytest_cache", ".ruff_cache",
#            ".svn", ".tox", ".venv", ".vscode", "__pypackages__",
#            "_build", "buck-out", "build", "dist", "node_modules",
#            "site-packages", "venv"]
# 
# [lint]
# select = ["E", "F", "I", "N", "UP", "B"]
# ignore = ["E501"]
# 
# [format]
# line-length = 88
# quote-style = "double"
# indent-style = "space"

# полезно для отладки — видно что переопределилось из CLI

Если запустить с флагами CLI, они будут учтены в выводе:

# увидим, что select переопределён
ruff check --select ALL --ignore D --show-settings .
🔍

--show-source — показать исходный код

Флаг --show-source выводит для каждого нарушения соответствующую строку исходного кода с указанием проблемного места. Это как формат full, но встроенный в стандартный text-вывод.

ruff check --show-source .
# src/main.py:10:5: F841 Local variable `unused` is assigned to but never used
#   |
# 10 |     unused = 42
#   |     ^^^^^^
# 
# src/main.py:15:89: E501 Line too long (95 > 88 characters)
#   |
# 15 |     result = some_function_with_very_long_arguments(a, b, c, d, e, f, g, h, i)  
#   |                                                                                   ^^^

Удобно использовать вместе с --watch для быстрого просмотра ошибок с контекстом.

🧪

--preview — preview-правила

Флаг --preview включает правила, которые находятся в стадии Preview (нестабильные, могут измениться в будущих версиях). Это позволяет опробовать новые правила до их официального релиза.

# включить preview-правила
ruff check --preview .
ruff check --select ALL --preview .

# в конфиге:
[tool.ruff]
preview = true
⚠️ Preview-правила могут измениться или быть удалены в любой момент. Не используйте --preview в CI для production-проектов без явного понимания рисков.
🔇

--silent и --quiet — управление выводом

Флаги управления verbosity:

# --quiet: выводить только ошибки, без информационных сообщений
ruff check --quiet .

# --silent: вообще ничего не выводить (только код возврата)
ruff check --silent .
# полезно в CI: код возврата 0 = всё ок, 1 = есть ошибки

--silent особенно полезен в скриптах, где важен только код возврата (exit code). Например, pre-commit хуки или Makefile: ruff check --silent . || exit 1.

📊

Сравнение форматов вывода

Формат Читаемость Машиночитаемость Лучшее применение
text ⭐⭐⭐⭐ ⭐ Локальная разработка
concise ⭐⭐⭐ ⭐ Терминал, watch режим
json ⭐ ⭐⭐⭐⭐⭐ Парсинг, CI, кастомные отчёты
github ⭐⭐⭐ ⭐⭐⭐⭐ GitHub Actions
gitlab ⭐⭐ ⭐⭐⭐⭐ GitLab CI
full ⭐⭐⭐⭐⭐ ⭐ Обучение, детальный разбор
pylint ⭐⭐⭐ ⭐⭐⭐ Миграция с pylint
🛠️

Практические сценарии

Сценарий 1: Локальная разработка с watch

# Терминал 1: редактор
code .
# Терминал 2: ruff в режиме наблюдения
ruff check --select ALL --ignore E501,D --output-format concise --watch .

Сценарий 2: CI pipeline с GitHub Actions

# .github/workflows/ruff.yml
name: Ruff
on: [push, pull_request]
jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/ruff-action@v2
        with:
          args: >
            check --output-format github
            --select E,F,I,N,UP,B,S
            --ignore E501

Сценарий 3: Генерация отчёта для дашборда

# генерация JSON для кастомного дашборда
ruff check --output-format json . > lint-report.json

# затем Python скрипт для создания HTML отчёта:
python3 -c "
import json
data = json.load(open('lint-report.json'))
total = data['summary']['violations']
print(f'

Lint Report

Total violations: {total}

') " > report.html

Сценарий 4: Отладка конфигурации

# проверить какие файлы будут проверяться
ruff check --show-files .

# проверить какие настройки активны
ruff check --show-settings .

# убедиться что exclude работает
ruff check --exclude "migrations" --show-files .
💡 Комбинируйте --show-files и --show-settings для отладки. Если Ruff ведёт себя неожиданно, первым делом проверьте, какие настройки реально активны.
📋

Резюме

Флаг Назначение
--watch Режим наблюдения за файлами
--output-format Выбор формата вывода (text, json, github и др.)
--show-files Список проверяемых файлов
--show-settings Текущая конфигурация Ruff
--show-source Показать исходный код с ошибкой
--preview Включить preview-правила
--quiet / --silent Управление verbosity
📖 Документация: docs.astral.sh/ruff/linter/#output-format
⏱️

Детали работы --watch

Режим --watch использует механизм файловых уведомлений операционной системы (inotify на Linux, FSEvents на macOS, ReadDirectoryChanges на Windows). Это обеспечивает мгновенную реакцию на изменения без постоянного опроса диска (polling).

Особенности работы --watch:

  • При запуске выполняет полную проверку всего проекта
  • При изменении файла перепроверяет только его (не весь проект)
  • Не поддерживает --diff (дифф применяется мгновенно)
  • Работает как с одиночными файлами, так и с директориями
  • Автоматически обрабатывает новые и удалённые файлы
  • Игнорирует изменения в .ruff_cache и node_modules
# watch с автоисправлением — изменения применяются сразу
ruff check --fix --watch .

# watch с компактным выводом
ruff check --output-format concise --watch .

# watch только для определённой директории
ruff check --watch src/ruff/

# watch с ограниченным набором правил
ruff check --select F,I --watch .
💡 На больших проектах (10 000+ файлов) используйте --watch с конкретной директорией, а не со всем проектом: ruff check --watch src/my_module/
💻

Интеграция с редакторами кода

Хотя --watch удобен, большинство разработчиков предпочитают интеграцию Ruff напрямую в редактор. Вот как это работает:

VS Code: расширение "Ruff" от Astral Software. Устанавливается из маркетплейса. Автоматически запускает ruff check при сохранении и подсвечивает ошибки в редакторе.

// .vscode/settings.json
{
  "[python]": {
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.fixAll.ruff": "explicit",
      "source.organizeImports.ruff": "explicit"
    },
    "editor.defaultFormatter": "charliermarsh.ruff"
  },
  "ruff.lineLength": 88,
  "ruff.organizeImports": true,
  "ruff.fixOnSave": true
}

Neovim: через null-ls или conform.nvim:

lua << EOF
require("null-ls").setup({
  sources = {
    require("null-ls").builtins.diagnostics.ruff,
    require("null-ls").builtins.formatting.ruff,
  },
})
EOF

# Или через conform:
require("conform").setup({
  formatters_by_ft = {
    python = { "ruff_format" },
  },
})

PyCharm: установите плагин "Ruff" через Settings → Plugins. Настройте внешний инструмент:

# File → Settings → Tools → External Tools → Add
# Program: ruff
# Arguments: check $FilePath$
# Working directory: $ProjectFileDir$
📋

Структура JSON-вывода

Полная структура JSON-вывода Ruff:

{
  "files": {
    "/absolute/path/to/file.py": {   # ключ — абсолютный путь
      "cell": null,                  # для Jupyter ноутбуков
      "messages": [                  # массив нарушений
        {
          "code": "F841",            # код правила
          "message": "Local variable...",  # сообщение
          "fix": {                   # объект исправления (null если нет)
            "applicability": "safe", # safe или unsafe
            "message": "...",
            "edits": [
              {
                "content": "...",
                "location": {
                  "row": 10,
                  "column": 5
                },
                "end_location": {
                  "row": 10,
                  "column": 11
                }
              }
            ]
          },
          "location": {
            "row": 10,
            "column": 5
          },
          "end_location": {
            "row": 10,
            "column": 11
          },
          "filename": "/absolute/path/to/file.py",
          "noqa_row": 10,
          "url": "https://docs.astral.sh/ruff/rules/unused-variable"
        }
      ]
    }
  },
  "summary": {
    "raw": "Found 5 errors.",
    "violations": 5
  }
}
💡 Поле fix содержит информацию о доступном автоисправлении. Если fix равен null, для этого правила нет автоматического исправления.
🚦

Коды возврата (exit codes)

Коды возврата Ruff важны для интеграции в CI/CD. Они позволяют автоматически определить, прошла ли проверка успешно:

Код Значение
0 Успех — ошибок не найдено
1 Найдены ошибки
# использование в bash скриптах
if ruff check --silent .; then
  echo "Код чист!"
else
  echo "Найдены ошибки!"
  exit 1
fi

# или более кратко:
ruff check --silent . || { echo "Линтинг не прошёл!"; exit 1; }
📝

Шпаргалка

# Watch режим
ruff check --watch .
ruff check --fix --watch .

# Форматы вывода
ruff check --output-format json .
ruff check --output-format github .
ruff check --output-format gitlab .
ruff check --output-format concise .
ruff check --output-format full .
ruff check --output-format pylint .

# Отладка
ruff check --show-files .
ruff check --show-settings .
ruff check --show-source .

# Preview
ruff check --preview .
ruff check --select ALL --preview .

# Verbosity
ruff check --quiet .
ruff check --silent .

# Комбинации
ruff check --select E,F,I --output-format concise --watch .
ruff check --fix --select I --output-format github .

Урок 2.3: Форматы вывода

5 вопросов