Поиск по сайту:

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


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

Ruff — это современный линтер, который чрезвычайно быстр и имеет простой интерфейс, что делает его простым в использовании. Он также призван стать полной заменой многих других инструментов линтинга и форматирования, таких как Flake8, isort и Black. Он быстро становится одним из самых популярных линтеров Python.

Из этого руководства вы узнаете, как:

  • Установите Ерш
  • Проверьте свой код Python на наличие ошибок.
  • Автоматически исправляйте ошибки линтинга
  • Используйте Ruff для форматирования кода.
  • Добавьте дополнительные конфигурации, чтобы повысить эффективность линтинга.

Чтобы получить максимальную пользу от этого руководства, вы должны быть знакомы с виртуальными средами, устанавливать сторонние модули и уметь пользоваться терминалом.

Установка Ерша

Теперь, когда вы знаете, почему линтинг вашего кода важен и насколько Ruff — мощный инструмент для этой работы, пришло время его установить. К счастью, Ruff работает «из коробки», поэтому для его использования не требуется никаких сложных инструкций по установке или настройке.

Предполагая, что в вашем проекте уже настроена виртуальная среда, вы можете установить Ruff следующими способами:

$ python -m pip install ruff

Помимо pip, вы также можете установить Ruff с помощью Homebrew, если вы используете macOS или Linux:

$ brew install ruff

Пользователи Conda могут установить Ruff с помощью conda-forge:

$ conda install -c conda-forge ruff

Если вы используете Arch, Alpine или openSUSE Linux, вы также можете использовать официальные репозитории дистрибутива. Конкретные инструкции вы найдете на странице установки Ruff официальной документации.

Кроме того, если вы хотите, чтобы Ruff был доступен для всех ваших проектов, вы можете установить Ruff с помощью pipx.

Проверить правильность установки Ruff можно с помощью команды ruff version:

$ ruff version
ruff 0.4.7

Чтобы команда ruff появилась в вашем PATH, вам может потребоваться закрыть и снова открыть приложение терминала или начать новый сеанс терминала.

Линтинг вашего кода Python

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

Проверка ошибок

Код ниже представляет собой простой скрипт под названием one_ring.py. Когда вы запускаете его, он получает случайное имя персонажа Властелина колец из кортежа и сообщает, нес ли этот персонаж бремя Единого Кольца. Этот код не имеет реального практического применения и представляет собой просто развлечение. Независимо от размера вашей кодовой базы, шаги будут одинаковыми:

import os
import random

CHARACTERS = ("Frodo", "Sam", "Merry", "Pippin", "Aragorn", "Legolas", "Gimli", "Boromir", "Gandalf", "Saruman", "Sauron")

def random_character():
    return random.choice(CHARACTERS)

def ring_bearer():
    return name in ("Frodo", "Sam")

if __name__ == "__main__":
    character = random_character()
    if ring_bearer(character):
        print(f"{character} is a ring bearer")
    else:
        print(f"{character} is not a ring bearer")

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

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

$ ruff check
one_ring.py:1:8: F401 [*] `os` imported but unused
one_ring.py:10:12: F821 Undefined name `name`
Found 2 errors.
[*] 1 fixable with the `--fix` option.

Успех! Рафф обнаружил две ошибки. Он не только показывает номера файлов и строк ошибок, но также выдает коды ошибок и сообщения. Кроме того, это дает понять, что одну из двух ошибок можно исправить. Большой!

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

$ ruff check --fix
one_ring.py:9:12: F821 Undefined name `name`
Found 2 errors (1 fixed, 1 remaining).

Неиспользуемый импорт теперь исправлен, и эта строка кода удалена из one_ring.py. Последняя из этих двух ошибок не подлежит автоматическому исправлению. Проблема в строке 9 может быть для вас очевидна, но, возможно, это не так.

К счастью, Ruff дает вам код ошибки и возможность быстро его найти без необходимости искать документацию в Интернете. Введите вторую команду ruff: rule.

Поскольку Ruff предоставляет код ошибки, вы можете передать его команде ruff rule, чтобы просмотреть более подробную информацию о сообщении об ошибке, включая пример кода:

$ ruff rule F821

Когда вы запустите эту команду, вы получите более подробную информацию в формате Markdown в своем терминале:

# undefined-name (F821)

Derived from the **PyFlakes** linter.

## What it does
Checks for uses of undefined names.

## Why is this bad?
An undefined name is likely to raise `NameError` at runtime.

## Example

```python
def double():
    return n * 2  # raises `NameError` if `n` is undefined when `double` is called
```

Use instead:

```python
def double(n):
    return n * 2
```

## References
- [Python documentation: Naming and binding](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding)

Благодаря дополнительному контексту кода ошибки вы теперь можете видеть, что код примера, который вы видели ранее, допустил ту же ошибку. Переменная name в строке 9 не была передана в качестве аргумента сигнатуре функции ring_bearer(). Упс!

Чтобы исправить эту ошибку, вы можете изменить ring_bearer(), чтобы он принимал аргумент name:

# ...

def ring_bearer(name):
    return name in ("Frodo", "Sam")

Теперь, когда вы внесли небольшое изменение в код, вы можете снова запустить ruff check, чтобы проверить, пройдет ли он:

$ ruff check
All checks passed!

Большой! Обе ошибки теперь исправлены, и ваш код должен выглядеть следующим образом:

import random

CHARACTERS = ("Frodo", "Sam", "Merry", "Pippin", "Aragorn", "Legolas", "Gimli", "Boromir", "Gandalf", "Saruman", "Sauron")

def random_character():
    return random.choice(CHARACTERS)

def ring_bearer(name):
    return name in ("Frodo", "Sam")

if __name__ == "__main__":
    character = random_character()
    if ring_bearer(character):
        print(f"{character} is a ring bearer")
    else:
        print(f"{character} is not a ring bearer")

Запускать проверку ерша каждый раз, когда вы меняете код, может оказаться непрактичным. К счастью, у Раффа есть решение. В следующем разделе вы увидите, как можно постоянно проверять свой код на наличие ошибок.

Ускорение вашего рабочего процесса

Когда вы активно работаете над кодом, Ruff может еще больше упростить ваш рабочий процесс, информируя вас об ошибках по мере разработки. Это ускорит весь процесс и сделает вашу работу более продуктивной. Чтобы обеспечить непрерывный анализ во время написания кода, откройте новое окно терминала и передайте флаг --watch команде check:

$ ruff check --watch

После запуска приведенной выше команды вы должны увидеть в своем терминале что-то вроде этого:

[14:04:01 PM] Starting linter in watch mode...
[14:04:01 PM] Found 0 errors. Watching for file changes.

Теперь ваш код свободен от ошибок. Или это так? В следующем разделе вы узнаете, что Рафф не уловил по умолчанию.

Поиск дополнительных ошибок

Несмотря на то, что ошибки, найденные Раффом, исправлены, код всё равно требует очистки. Есть еще пара проблем с файлом one_ring.py, которые можно исправить, чтобы сделать этот код еще чище и читабельнее. Самая заметная проблема находится в строке 3. Кортеж CHARACTERS кажется слишком длинным, и его можно было бы сделать более читабельным.

Вы можете задаться вопросом: почему Рафф не уловил это? Это совершенно правильный вопрос. Копание в документации дает такой ответ:

По умолчанию Ruff включает правила F Flake8, а также подмножество правил E , исключая любые стилистические правила, которые перекрываются с использованием форматтера, например ruff формат или Черный. (Источник)

В стандартной версии Ruff не применяет правило для проверки длины строки. Однако вы можете указать ему, какие дополнительные правила вы хотите включить или исключить. Вы можете попросить его включить все правила E или конкретное правило с помощью флага --select:

$ ruff check --select E
one_ring.py:4:89: E501 Line too long (122 > 88)
Found 1 error.

$ ruff check --select E501
one_ring.py:4:89: E501 Line too long (122 > 88)
Found 1 error.

Ах, вы нашли дополнительную ошибку. Однако вы можете заметить, что нет никаких указаний на то, что длина строки может быть автоматически исправлена с помощью флага --fix. Не волнуйтесь, потому что в Ruff есть способ исправить ошибки форматирования с помощью новой команды. В следующем разделе вы узнаете о формате ruff.

Форматирование вашего кода Python

По умолчанию Ruff имеет разумные правила форматирования и был разработан как замена для Black. Команда format доступна начиная с версии Ruff 0.1.2.

Как и команда check, команда format принимает дополнительные аргументы для указания пути к одному файлу или каталогу. Поскольку код в этом примере руководства представляет собой один файл, вы можете использовать его без каких-либо аргументов:

$ ruff format
1 file reformatted

Ваш файл one_ring.py теперь должен выглядеть более читабельным и иметь единообразное форматирование:

import random

CHARACTERS = (
    "Frodo",
    "Sam",
    "Merry",
    "Pippin",
    "Aragorn",
    "Legolas",
    "Gimli",
    "Boromir",
    "Gandalf",
    "Saruman",
    "Sauron",
)


def random_character():
    return random.choice(CHARACTERS)


def ring_bearer(name):
    return name in ("Frodo", "Sam")


if __name__ == "__main__":
    character = random_character()
    if ring_bearer(character):
        print(f"{character} is a ring bearer")
    else:
        print(f"{character} is not a ring bearer")

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

Следующее внесенное изменение заключается в том, что расстояние между функциями теперь согласовано и соответствует стандарту PEP 8, с рекомендуемыми двумя интервалами между функциями.

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

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

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

--- one_ring.py
+++ one_ring.py
@@ -1,16 +1,31 @@
 import random


-CHARACTERS = ("Frodo", "Sam", "Merry", "Pippin", "Aragorn", "Legolas", "Gimli", "Boromir", "Gandalf", "Saruman", "Sauron")
+CHARACTERS = (
+    "Frodo",
+    "Sam",
+    "Merry",
+    "Pippin",
+    "Aragorn",
+    "Legolas",
+    "Gimli",
+    "Boromir",
+    "Gandalf",
+    "Saruman",
+    "Sauron",
+)
+

 def random_character():
     return random.choice(CHARACTERS)

+
 def ring_bearer(name):
     return name in ("Frodo", "Sam")

+
 if __name__ == "__main__":
     character = random_character()
     if ring_bearer(character):
         print(f"{character} is a ring bearer")
     else:
-        print(f"{character} is not a ring bearer")
\ No newline at end of file
+        print(f"{character} is not a ring bearer")

1 file would be reformatted

Возможно, это все, что вам когда-либо понадобится для форматирования кода. Однако могут быть случаи, когда вы предпочитаете другую длину строки или хотите включить или исключить определенные правила. В таких ситуациях перечисление всех необходимых правил в командной строке каждый раз, когда вы хотите проверить свой код, может занять много времени. Должен быть лучший способ!

Есть. Хотя это и не обязательно, Ruff может быть широко настраиваемым. В следующем разделе вы кратко ознакомитесь с некоторыми основами настройки.

Настройка Раффа

Если вы анализируете большую базу кода, имеете несколько коммиттеров или хотите настроить свой опыт, Ruff позволяет вам сохранить вашу конфигурацию в файле TOML. Точнее, ruff.toml, .ruff.toml или существующий файл pyproject.toml.

Как упоминалось ранее, ruff имеет разумные значения по умолчанию. Эти конфигурации задокументированы на странице конфигурации Ruff, которую вы можете прочитать. Полный список настроек, доступных для вашей конфигурации, хорошо документирован. Вот пример простой конфигурации ruff.toml, которую вы можете добавить в свой проект:

line-length = 88

[lint]
select = ["E501", "I"]

[format]
docstring-code-format = true
docstring-code-line-length = 72

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

[tool.ruff]
line-length = 88

[tool.ruff.lint]
select = ["E501", "I"]

[tool.ruff.format]
docstring-code-format = true
docstring-code-line-length = 72

В этих примерах вы заметите несколько новых правил. Как и ранее, вы указали, что хотите включить правило E501 при анализе с помощью ruff, которое будет возвращать ошибку, если длина строки превышает длину строки. по умолчанию 88 символов.

Помимо добавления правила E501 в конфигурацию проверки, вы также попросили Ruff добавить все правила I. Правила I уникальны для isort, еще одного пакета, который вы, возможно, использовали раньше для анализа и форматирования операторов import Python. Благодаря этой конфигурации вам больше не нужны isort и Black для форматирования кода. Это означает меньше инструментов для управления и меньше зависимостей от разработчиков.

В строках с 6 по 8 вы увидите, что Ruff теперь отформатирует ваши строки документации до длины 72 символа. Это число может быть любым, и многие могут выбрать 88 символов, чтобы оно соответствовало длине строки кода. Имейте в виду, что по умолчанию Ruff не форматирует строки документации.

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

Если у вас уже есть опыт работы с линтером, поделитесь своими любимыми правилами и настройками в комментариях ниже.

Следующие шаги

Теперь, когда вы узнали, почему вам следует использовать линтер и что Ruff — отличный инструмент, который поможет вам получить чистый, читаемый и безошибочный код, вам стоит попробовать Ruff.

Как упоминалось выше, существует множество конфигураций, которые вы можете использовать, чтобы вывести свой анализ на новый уровень. Существует также несколько интеграций, которые могут ускорить ваш рабочий процесс, например расширение VS Code, плагин PyCharm, перехватчик предварительной фиксации и действия GitHub.

Заключение

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

Из этого руководства вы узнали, как:

  • Установить Ruff
  • Проверьте свой код Python на наличие ошибок.
  • Автоматически исправляйте ошибки линтинга
  • Используйте Ruff для форматирования кода.
  • Добавьте дополнительные конфигурации, чтобы повысить эффективность линтинга.

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