Ruff — линтер и форматтер кода на Python

28 сентября 2026
Автор

Антон Ларичев

Что такое Ruff

Ruff — линтер и форматтер Python-кода, написанный на Rust. Он появился в 2022 году и быстро занял место главного инструмента статического анализа в Python-проектах благодаря одному ключевому свойству: работает в 10–100 раз быстрее аналогов.

До ruff стандартный стек выглядел так: flake8 для проверки стиля, isort для сортировки импортов, pyupgrade для обновления синтаксиса, black для форматирования. Каждый инструмент — отдельная зависимость, отдельная конфигурация, отдельный запуск. Ruff заменяет всё это одним бинарником.

Главные преимущества:

  • Единый инструмент вместо четырёх и более
  • Скорость: 10–100 мс на больших кодовых базах вместо нескольких секунд
  • Более 800 встроенных правил, совместимых с flake8, pylint, isort и другими
  • Автоматическое исправление большинства найденных проблем
  • Встроенный форматтер, совместимый с black по стилю

Курс по теме

Курс Python с нуля

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Установка

Ruff устанавливается через pip или любой другой пакетный менеджер:

pip install ruff

Если вы используете uv (рекомендуемый способ для новых проектов):

uv add --dev ruff

Проверить установку:

ruff --version
# ruff 0.6.0

Ruff также можно установить как отдельный бинарник без Python-окружения — через pipx:

pipx install ruff

Первый запуск

Проверка текущей директории:

ruff check .

Проверка конкретного файла:

ruff check src/main.py

Автоматическое исправление найденных проблем:

ruff check --fix .

Форматирование кода:

ruff format .

Просмотр того, что изменится при форматировании, без реального изменения файлов:

ruff format --diff .

Конфигурация

Ruff читает конфигурацию из файлов ruff.toml, pyproject.toml или .ruff.toml. Рекомендуется использовать pyproject.toml, чтобы держать всё в одном месте.

Минимальная конфигурация в pyproject.toml:

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

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

Основные параметры

line-length — максимальная длина строки. По умолчанию 88 (совпадает с black).

target-version — версия Python, для которой пишется код. Влияет на правила, связанные с синтаксисом.

src — пути к исходным файлам. Ruff использует это для корректного определения первой и сторонней части в импортах:

[tool.ruff]
src = ["src", "tests"]

exclude — директории и файлы, которые нужно игнорировать:

[tool.ruff]
exclude = [
    ".venv",
    "migrations",
    "__pycache__",
]

Правила линтера

Все правила сгруппированы в наборы (prefix). Каждый набор соответствует оригинальному инструменту или категории:

Префикс Источник
E, W pycodestyle (PEP 8)
F pyflakes
I isort
N pep8-naming
UP pyupgrade
B flake8-bugbear
S flake8-bandit (безопасность)
PT flake8-pytest-style
SIM flake8-simplify
RUF собственные правила ruff

Выбор правил

В разделе [tool.ruff.lint] указываются правила через select и ignore:

[tool.ruff.lint]
# Включить группы правил
select = [
    "E",    # pycodestyle errors
    "W",    # pycodestyle warnings
    "F",    # pyflakes
    "I",    # isort
    "N",    # pep8-naming
    "UP",   # pyupgrade
    "B",    # flake8-bugbear
    "SIM",  # flake8-simplify
    "RUF",  # ruff-specific
]

# Исключить конкретные правила
ignore = [
    "E501",   # line too long (контролируем через line-length)
    "B008",   # do not perform function calls in default arguments
]

Чтобы включить все правила сразу:

[tool.ruff.lint]
select = ["ALL"]
ignore = ["D"]  # исключаем docstring-правила, если они не нужны

Примеры того, что ловит ruff

# F401: импорт не используется
import os
import sys  # используется ниже

# F841: переменная определена, но не используется
def calculate(x, y):
    result = x + y
    unused = x * y  # F841
    return result

# E711: сравнение с None через == вместо is
def check(value):
    if value == None:  # E711
        return False
    return True

# UP006: используй list вместо List из typing (Python 3.9+)
from typing import List

def get_items() -> List[str]:  # UP006
    return []

# SIM102: объединённые if в один
def process(a, b):
    if a > 0:       # SIM102: можно написать `if a > 0 and b > 0:`
        if b > 0:
            return True
    return False

Подавление правил в коде

Иногда нужно отключить конкретное правило для строки или блока:

import os  # noqa: F401

# Отключить несколько правил для строки
result = eval(user_input)  # noqa: S307, B023

# Отключить для всего файла — в начале файла
# ruff: noqa: F401

Форматтер

Ruff format реализует стиль форматирования, совместимый с black. Если вы уже используете black, переход на ruff format практически не изменит код.

Основные принципы форматирования:

  • Одинарные или двойные кавычки — выбор делается автоматически в пользу минимизации экранирования
  • Trailing comma в многострочных выражениях
  • Аргументы функций переносятся на отдельные строки при превышении длины строки

Пример:

# До форматирования
def long_function_name(argument_one,argument_two,argument_three,argument_four):
    return argument_one+argument_two

x={'key1': 'value1','key2': 'value2','key3': 'value3','key4': 'value4'}

# После ruff format
def long_function_name(
    argument_one, argument_two, argument_three, argument_four
):
    return argument_one + argument_two

x = {
    "key1": "value1",
    "key2": "value2",
    "key3": "value3",
    "key4": "value4",
}

Настройка форматтера

[tool.ruff.format]
# Стиль кавычек: double (по умолчанию) или single
quote-style = "double"

# Стиль отступов: space (по умолчанию) или tab
indent-style = "space"

# Пропускать ли волшебные trailing-запятые
skip-magic-trailing-comma = false

# Стиль переноса строк: lf, crlf, cr или native
line-ending = "lf"

Полная конфигурация для реального проекта

Пример pyproject.toml для Django или FastAPI проекта:

[tool.ruff]
line-length = 88
target-version = "py311"
src = ["src"]
exclude = [
    ".venv",
    "*/migrations/*",
    "__pycache__",
]

[tool.ruff.lint]
select = [
    "E",
    "W",
    "F",
    "I",
    "N",
    "UP",
    "B",
    "SIM",
    "RUF",
]
ignore = [
    "N805",   # first argument of a method should be named 'self'
    "B008",   # do not perform function calls in default arguments (нужно для FastAPI Depends)
]

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

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

Интеграция с VS Code

Установите официальное расширение Ruff из маркетплейса VS Code (publisher: charliermarsh).

Настройки в .vscode/settings.json:

{
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.codeActionsOnSave": {
        "source.fixAll.ruff": "explicit",
        "source.organizeImports.ruff": "explicit"
    },
    "[python]": {
        "editor.defaultFormatter": "charliermarsh.ruff"
    }
}

После этого каждое сохранение файла будет автоматически форматировать код и исправлять проблемы линтера.

Интеграция с pre-commit

pre-commit запускает проверки перед каждым коммитом. Добавьте ruff в .pre-commit-config.yaml:

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.6.0
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

Установка хуков:

pip install pre-commit
pre-commit install

Теперь при попытке закоммитить файлы с проблемами ruff остановит коммит и покажет, что нужно исправить. Флаг --fix автоматически исправит то, что можно исправить.

Интеграция в CI/CD

GitHub Actions

name: Lint and Format

on: [push, pull_request]

jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install ruff
        run: pip install ruff

      - name: Check with ruff lint
        run: ruff check .

      - name: Check with ruff format
        run: ruff format --check .

Флаг --check в ruff format не изменяет файлы, а возвращает ненулевой код выхода, если файлы не отформатированы. Это позволяет использовать команду в CI без изменения файлов.

GitLab CI

lint:
  image: python:3.11
  script:
    - pip install ruff
    - ruff check .
    - ruff format --check .
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Миграция с существующих инструментов

Если в проекте уже используются flake8, black и isort, ruff предоставляет команду для анализа совместимости:

# Посмотреть, какие правила flake8 покрывает ruff
ruff rule --all | grep -i flake8

Общая стратегия миграции:

  1. Установить ruff и запустить ruff check . — посмотреть текущее состояние
  2. Добавить существующие игнорируемые правила в ignore
  3. Убедиться, что количество ошибок совпадает с тем, что выдавал flake8
  4. Удалить flake8, isort, pyupgrade из зависимостей
  5. Заменить black на ruff format

Ruff понимает конфигурацию flake8 из setup.cfg и .flake8, что упрощает первичную миграцию.

Полезные команды

Посмотреть все доступные правила:

ruff rule --all

Подробное описание конкретного правила:

ruff rule E501

Проверить конфигурацию:

ruff check --show-settings .

Статистика нарушений по правилам:

ruff check --statistics .

Проверить файл и показать исходный код нарушений:

ruff check --show-source src/main.py

Исправить только безопасные автоматические исправления:

ruff check --fix .

Исправить в том числе «небезопасные» исправления (меняют поведение):

ruff check --fix --unsafe-fixes .

Ruff vs другие инструменты

Ruff не заменяет инструменты с принципиально другими задачами:

  • mypy / pyright — статическая проверка типов. Ruff не анализирует типы, только стиль и очевидные ошибки.
  • pytest — тестирование. Ruff только помогает писать тесты в правильном стиле (правила PT).
  • bandit — глубокий анализ безопасности. Ruff включает базовый набор правил безопасности (S), но не заменяет специализированный инструмент для аудита.

Для полного стека рекомендуется использовать ruff вместе с mypy или pyright.

Итог

Ruff решает реальную проблему Python-экосистемы: фрагментацию инструментов линтинга и форматирования. Вместо пяти конфигурационных файлов и пяти команд — один инструмент с единой конфигурацией в pyproject.toml.

Практическое правило: если вы начинаете новый Python-проект, ставьте ruff с первого дня. Если переходите с существующего стека — потратьте час на миграцию, и получите более быстрый и простой CI.

Для глубокого погружения в Python и профессиональные инструменты разработки смотрите курс на PurpleSchool: https://purpleschool.ru/course/python?utm_source=knowledgebase&utm_medium=text&utm_campaign=ruff-linter-formatter

Стрелочка влевоtyping.Annotated в PythonPython с нуля: дорожная карта для начинающихСтрелочка вправо

Постройте личный план изучения Python до уровня Middle — бесплатно!

Python — часть карты развития Backend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Все гайды по Python

Как отправлять запросы с помощью requests в PythonКак работает команда print в PythonВозможности Python для автоматизации задачПочему Python выводит значение без команды printРабота с JSON в Python на примерахPython get — методы получения данныхКак находить и исправлять ошибки в PythonРабота с данными через API и внешние сервисыСтруктура и оформление кода PythonОсновы Django с PythonПолезные приёмы в Python для повседневной работыИспользование locals в Python для отладкиИнтеграция PHP и PythonКак выполнять HTTPS-запросы в PythonFastAPI Python — быстрый старт: создание REST API с нуляКак работать с API в Python
Ввод целого числа в PythonВедение логов в PythonУдаление данных в Python с помощью removeОбработка исключений с помощью try/except в PythonФункция super() в Python — как вызвать метод родителяСоздание собственных контекстных менеджеров в PythonРабота с символами программирования PythonРабота с переменной X в PythonРабота с классами в PythonПростая программа на Python для начинающихОсновы Python для тех, кто начинаетКак скачать Python на компьютерЧто нового в Python 3Команда python print - полное руководство по выводу данныхПоддерживается ли Python 2 и стоит ли его использоватьПравила именования переменных в PythonPython 1 — с чего начиналась история языкаПользовательские исключения в PythonОсновы Python coreОписание объектов PythonНаследование классов в Python — основы и примерыМножественное наследование в Python — примеры и MROКонтекстный менеджер with в Python — как работает и зачем нуженКакой Python выбрать для установкиКомментарии в Python — однострочные, многострочные и docstringКак вывести целое число с помощью print в PythonКак установить Python на Windows macOS и LinuxКак пользоваться консолью PythonКак получить последний элемент в PythonКак настроить PythonКак использовать print для строк в PythonКак найти значение в PythonКак работает интерпретатор PythonИнструкция по работе с PythonБлок finally в обработке исключений PythonАбстрактные классы в Python — ABC и abstractmethodЦелые числа в Python
Загрузка данных PythonСоздание веб-приложений на Flask PythonУправление проектами на GitHub с PythonСоздание бота на PythonСоздание интерфейсов Python QTСоздание игр с PygameСоздание GUI в PythonКак работать со словарями в PythonРабота с библиотеками через Python PackagingРабота со временем в Python при помощи модуля timePython name — особенности переменнойМатематические операции в Python с модулем mathPython listing — что это и как использоватьPytest — тестирование на Python: полное руководствоМашинное обучение с PythonОбработка изображений с OpenCV PythonNumPy в Python — основы и применение в задачахИспользование Anaconda с PythonМодуль contextlib в Python — утилиты для контекстных менеджеровБиблиотеки Python и их применение в проектах
Возврат значений из функции в PythonВложенные функции в PythonСоздание собственных декораторов в PythonРабота с функцией map в PythonЦикл while в Python и примеры использованияОбработка чисел, введённых через input в PythonОсновные операторы в Python с примерамиУсловные выражения if else в Python для начинающихКак выполняется вызов функций call в PythonПозиционные и именованные аргументы в PythonПередача аргументов по ссылке и по значению в PythonПродвинутые генераторы в Python — send, throw, close и корутиныПередача аргументов через args и kwargs в PythonОбъявление переменных и управление областью видимости в PythonОсновные методы Python и примеры их использованияОператор match/case в Python 3.10+ — основы структурного сопоставленияПаттерны match/case в Python — деструктуризация, guard и вложенные шаблоныПрактические примеры match/case в Python — реальные сценарии примененияЛокальные и глобальные переменные в PythonЧасто используемые команды PythonКлючевые слова global и nonlocal в PythonКак создавать функции в PythonКак работает сборщик мусора в PythonКак работает область видимости переменных в PythonКак работает функция callable в PythonКак проверить тип переменной в PythonКак передать функцию как аргумент в PythonКак использовать функцию isinstance в PythonКак использовать функцию eval безопасно в PythonКак использовать функцию filter в PythonКак использовать декораторы в PythonКак использовать функцию filter в PythonКак работает функция any и all в PythonИзменяемые и неизменяемые типы данных в PythonГенераторы и yield в Python — как создавать и использоватьГенераторные выражения в Python — синтаксис и примерыФункции в Python и способы их вызоваФункции как объекты в PythonЧто такое замыкания в PythonЧто делает функция reduce в PythonЧто делает функция id в PythonАргументы по умолчанию в PythonАнонимные функции и lambda в PythonАлгоритмы на Python — примеры и объяснение
Запись данных в PythonУправление библиотеками с помощью Python PackagingУстановка pip в PythonУправление зависимостями requirement в PythonУдаление пробелов с помощью strip в PythonСтруктурирование кода в PythonСоздание исполняемого файла Python в exeРазбор traceback в модуле PythonРазбор site-packages в PythonРазбор Program Files в PythonРабота с Unicode кодировками в PythonРабота с системными функциями Python sysРабота с папкой AppData в PythonРабота с модулем logging в PythonРабота с каталогами в PythonРабота с CSV в PythonВиртуальная среда venv в Python — создание и настройкаКак создать простое приложение на PythonИспользование pip в Python для установки пакетовМодули в Python и организация кода в проектеИмпорт модулей в Python и правила подключенияРабота с файлами в Python пошаговоЧто делает компилятор Python и как он работаетПолучение строки из модуля PythonПодключение файлов в Python с includeСборка проекта с помощью packaging в PythonПеременные среды в PythonНастройка Python сервераИспользование Python на UbuntuИспользование консоли PythonИспользование кодировок в PythonИнициализация пакетов PythonИмпорт модулей PythonСреда IDLE Python и базовые возможностиИмпорт имен в PythonЧтение и запись TXT в PythonЧтение файлов в Python с помощью open file
Удаление элементов из списка PythonТипы данных в Python — обзор и рекомендацииОсновные операции со строками в PythonМетоды str в Python и обработка текстаСписки в Python и их ключевые методыРабота со строками и символами в PythonСоздание списков данных в PythonРабота со столбцами в PythonРабота со списком значений в PythonРабота с таблицами в Python с помощью DataFrameРабота с RFR в PythonРабота с пробелами в PythonРабота с массивами в PythonРабота с кортежами tuple PythonРабота с координатами X и Y в PythonРабота с ключами в PythonРабота с элементами данных PythonРабота с двоичными числами PythonРабота с данными в PythonРабота с данными NumPy PythonРабота с битами в PythonРабота с большими числами в PythonРабота с байтами в PythonЧто такое значение в Python и как его определитьМножества в Python и операции с нимиИспользование range в Python для цикловПроверка на четность в PythonПроверка числа в PythonПреобразование типов в PythonПреобразование списка в строку PythonПреобразование числа в строку в PythonПостроение графиков в PythonОпределение индекса элемента в PythonОкругление чисел в PythonОбъединение списков в Python с помощью zipМножества в PythonМассивы в Python и отличие от списковМассив чисел в PythonКортежи данных в PythonКак вычислить сумму чисел в PythonКак получить остаток от деления в PythonИспользование Unicode в PythonКак найти следующее число в PythonТип int в Python и его особенностиИндекс списка в PythonФункции для работы со строками в PythonЭлементы Python и способы доступа к нимДоступ к элементам массива в PythonДеление чисел в PythonКак работать с числами в PythonРабота с данными в Python на практике
Открыть базу знаний

Лучшие курсы по теме

Иконка молнииНовый
изображение курса

Основы Python

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий