Рассмотрим популярные инструменты для анализа кода Python и подробно расскажем об их специфике и основных принципах работы.
Автор: Валерий Шагур, teacher assistance на курсе Программирование на Python
Высокая стоимость ошибок в программных продуктах предъявляет повышенные
требования к качеству кода. Каким критериям должен соответствовать хороший код?
Отсутствие ошибок, расширяемость, поддерживаемость, читаемость и наличие документации. Недостаточное внимание к любому из этих критериев может привести к появлению новых ошибок или снизить вероятность обнаружения уже существующих. Небрежно написанный или чересчур запутанный код, отсутствие документации напрямую влияют на время исправления найденного бага, ведь разработчику приходится заново вникать в код. Даже такие, казалось бы, незначительные вещи как неправильные имена переменных или отсутствие форматирования могут сильно влиять на читаемость и понимание кода.
Командная работа над проектом еще больше повышает требования к качеству кода, поэтому важным условием продуктивной работы команды становится описание формальных требований к написанию кода. Это могут быть соглашения, принятые в языке программирования, на котором ведется разработка, или собственное (внутрикорпоративное) руководство по стилю. Выработанные требования к оформлению кода не исключают появления “разночтений” среди разработчиков и временных затрат на их обсуждение. Кроме этого, соблюдение выработанных требований ложится на плечи программистов в виде дополнительной нагрузки. Все это привело к появлению инструментов для проверки кода на наличие стилистических и логических ошибок. О таких инструментах для языка программирования Python мы и поговорим в этой статье.
Анализаторы и автоматическое форматирование кода
Весь инструментарий, доступный разработчикам Python, можно условно разделить на две группы по способу реагирования на ошибки. Первая группа сообщает о найденных ошибках, перекладывая задачу по их исправлению на программиста. Вторая — предлагает пользователю вариант исправленного кода или автоматически вносит изменения.
И первая, и вторая группы включают в себя как простые утилиты командной строки для решения узкоспециализированных задач (например, проверка docstring или сортировка импортов), так и богатые по возможностям библиотеки, объединяющие в себе более простые утилиты. Средства анализа кода из первой группы принято называть линтерами (linter). Название происходит от lint — статического анализатора для языка программирования Си и со временем ставшего нарицательным. Программы второй группы называют форматировщиками (formatter).
Даже при поверхностном сравнении этих групп видны особенности работы с ними. При применении линтеров программисту, во-первых, необходимо писать код с оглядкой, дабы позже не исправлять найденные ошибки. И во вторых, принимать решение по поводу обнаруженных ошибок — какие требуют исправления, а какие можно проигнорировать. Форматировщики, напротив, автоматизируют процесс исправления ошибок, оставляя программисту возможность осуществлять контроль.
Часть 1
- pycodestyle
- pydocstyle
- pyflakes
- pylint
- vulture
Часть 2
- flake8
- prospector
- pylama
- autopep8
- yapf
- black
Соглашения принятые в статье и общие замечания
Прежде чем приступить к обзору программ, мы хотели бы обратить ваше внимание на несколько важных моментов.
Версия Python: во всех примерах, приведенных в статье, будет использоваться третья версия языка программирования Python.
Установка всех программ в обзоре практически однотипна и сводится к использованию пакетного менеджера pip.
$ python3.6 -m pip install --upgrade <package_name>
Некоторые из библиотек имеют готовые бинарные пакеты в репозиториях дистрибутивов linux или возможность установки с использованием git. Тем не менее для большей определенности и возможности повторения примеров из статьи, установка будет производится с помощью pip.
Об ошибках: стоит упомянуть, что говоря об ошибках, обнаруживаемых анализаторами кода, как правило, имеют в виду два типа ошибок. К первому относятся ошибки стиля (неправильные отступы, длинные строки), ко второму — ошибки в логике программы и ошибки синтаксиса языка программирования (опечатки при написании названий стандартных функций, неиспользуемые импорты, дублирование кода). Существуют и другие виды ошибок, например — оставленные в коде пароли или высокая цикломатическая сложность.
Тестовый скрипт: для примеров использования программ мы создали простенький по содержанию файл example.py. Мы сознательно не стали делать его более разнообразным по наличию в нем ошибок. Во-первых, добавление листингов с выводом некоторых анализаторов в таком случае сильно “раздуло” бы статью. Во-вторых, у нас не было цели детально показать различия в “отлове” тех или иных ошибок для каждой из утилит.
Содержание файла example.py:
import os import notexistmodule def Function(num,num_two): return num class MyClass: """class MyClass """ def __init__(self,var): self.var=var def out(var): print(var) if __name__ == "__main__": my_class = MyClass("var") my_class.out("var") notexistmodule.func(5)
В коде допущено несколько ошибок:
- импорт неиспользуемого модуля os,
- импорт не существующего модуля notexistmodule,
- имя функции начинается с заглавной буквы,
- лишние аргументы в определении функции,
- отсутствие self первым аргументом в методе класса,
- неверное форматирование.
Руководства по стилям: для тех, кто впервые сталкивается с темой оформления кода, в качестве знакомства предлагаем прочитать официальные руководства по стилю для языка Python PEP8 и PEP257. В качестве примера внутрикорпоративных соглашений можно рассмотреть Google Python Style Guide — https://github.com/google/styleguide/blob/gh-pages/pyguide.md
Pycodestyle
Pycodestyle — простая консольная утилита для анализа кода Python, а именно для проверки кода на соответствие PEP8. Один из старейших анализаторов кода, до 2016 года носил название pep8, но был переименован по просьбе создателя языка Python Гвидо ван Россума.
Запустим проверку на нашем коде:
$ python3 -m pycodestyle example.py example.py:4:1: E302 expected 2 blank lines, found 1 example.py:4:17: E231 missing whitespace after ',' example.py:7:1: E302 expected 2 blank lines, found 1 example.py:10:22: E231 missing whitespace after ',' example.py:11:17: E225 missing whitespace around operator
Лаконичный вывод показывает нам строки, в которых, по мнению анализатора, есть нарушение соглашений PEP8. Формат вывода прост и содержит только необходимую информацию:
<имя файла>: <номер строки> :<положение символа>: <код и короткая расшифровка ошибки>
Возможности программы по проверке соглашений ограничены: нет проверок на правильность именования, проверка документации сводится к проверки длины docstring. Тем не менее функционал программы нельзя назвать “спартанским”, он позволяет настроить необходимый уровень проверок и получить различную информацию о результатах анализа. Запуск с ключом –statistics -qq выводит статистику по ошибкам:
$ python3 -m pycodestyle --statistics -qq example.py 1 E225 missing whitespace around operator 2 E231 missing whitespace after ',' 2 E302 expected 2 blank lines, found 1
Более наглядный вывод можно получить при использовании ключа –show-source. После каждого сообщения об ошибке будет выведена строка исходного кода, в которой содержится ошибка.
$ python3 -m pycodestyle --show-source example.py example.py:4:1: E302 expected 2 blank lines, found 1 def Function(num,num_two): ^ example.py:4:17: E231 missing whitespace after ',' def Function(num,num_two): ^ example.py:7:1: E302 expected 2 blank lines, found 1 class MyClass: ^ example.py:10:22: E231 missing whitespace after ',' def __init__(self,var): ^ example.py:11:17: E225 missing whitespace around operator self.var=var ^
Если есть необходимость посмотреть, какие из соглашений PEP8 были нарушены, используйте ключ — show-pep8. Программа выведет список всех проверок с выдержками из PEP8 для случаев нарушений. При обработке файлов внутри директорий предусмотрена возможность фильтрации по шаблону. Pycodestyle позволяет сохранять настройки поиска в конфигурационных файлах как глобально, так и на уровне проекта.
Pydocstyle
Утилиту pydocstyle мы уже упоминали в статье Работа с документацией в Python: поиск информации и соглашения. Pydocstyle проверяет наличие docstring у модулей, классов, функций и их соответствие официальному соглашению PEP257.
$ python3 -m pydocstyle example.py example.py:1 at module level: D100: Missing docstring in public module example.py:4 in public function `Function`: D103: Missing docstring in public function example.py:7 in public class `MyClass`: D400: First line should end with a period (not 's') example.py:7 in public class `MyClass`: D210: No whitespaces allowed surrounding docstring text example.py:10 in public method `__init__`: D107: Missing docstring in __init__ example.py:13 in public method `out`: D102: Missing docstring in public method
Как мы видим из листинга, программа указала нам на отсутствие документации в определениях функции, методов класса и ошибки оформления в docstring класса. Вывод можно сделать более информативным, если использовать ключи –explain и –source при вызове программы. Функционал pydocstyle практически идентичен описанному выше для pycodestyle, различия касаются лишь названий ключей.
Pyflakes
В отличие от уже рассмотренных инструментов для анализа кода Python pyflakes не делает проверок стиля. Цель этого анализатора кода — поиск логических и синтаксических ошибок. Разработчики pyflakes сделали упор на скорость работы программы, безопасность и простоту. Несмотря на то, что данная утилита не импортирует проверяемый файл, она прекрасно справляется c поиском синтаксических ошибок и делает это быстро. С другой стороны, такой подход сильно сужает область проверок.
Функциональность pyflakes — “нулевая”, все что он умеет делать — это выводить результаты анализа в консоль:
$ python3 -m pyflakes example.py example.py:1: 'os' imported but unused
В нашем тестовом скрипте, он нашел только импорт не используемого модуля os. Вы можете самостоятельно поэкспериментировать с запуском программы и передачей ей в качестве параметра командной строки Python файла, содержащего синтаксические ошибки. Данная утилита имеет еще одну особенность — если вы используете обе версии Python, вам придется установить отдельные утилиты для каждой из версий.
Pylint
До сих пор мы рассматривали утилиты, которые проводили проверки на наличие либо стилистических, либо логических ошибок. Следующий в обзоре статический инструмент для анализа кода Python — Pylint, который совместил в себе обе возможности. Этот мощный, гибко настраиваемый инструмент для анализа кода Python отличается большим количеством проверок и разнообразием отчетов. Это один из самых “придирчивых” и “многословных” анализаторов кода. Анализ нашего тестового скрипта выдает весьма обширный отчет, состоящий из списка найденных в ходе анализа недочетов, статистических отчетов, представленных в виде таблиц, и общей оценки кода:
$ python3.6 -m pylint --reports=y text example.py ************* Module text /home/ququshka77/.local/lib/python3.6/site-packages/pylint/reporters/text.py:79:22: W0212: Access to a protected member _splitstrip of a client class (protected-access) ************* Module example example.py:4:16: C0326: Exactly one space required after comma def Function(num,num_two): ^ (bad-whitespace) example.py:10:21: C0326: Exactly one space required after comma def __init__(self,var): ^ (bad-whitespace) example.py:11:16: C0326: Exactly one space required around assignment self.var=var ^ (bad-whitespace) example.py:1:0: C0111: Missing module docstring (missing-docstring) example.py:2:0: E0401: Unable to import 'notexistmodule' (import-error) example.py:4:0: C0103: Function name "Function" doesn't conform to snake_case naming style (invalid-name) example.py:4:0: C0111: Missing function docstring (missing-docstring) example.py:4:17: W0613: Unused argument 'num_two' (unused-argument) example.py:13:4: C0111: Missing method docstring (missing-docstring) example.py:13:4: E0213: Method should have "self" as first argument (no-self-argument) example.py:7:0: R0903: Too few public methods (1/2) (too-few-public-methods) example.py:18:4: C0103: Constant name "my_class" doesn't conform to UPPER_CASE naming style (invalid-name) example.py:19:4: E1121: Too many positional arguments for method call (too-many-function-args) example.py:1:0: W0611: Unused import os (unused-import) Report ====== 112 statements analysed. Statistics by type +----------+----------+---------------+-------------+-------------------+---------------+ |type |number |old number |difference |%documented |%badname | +======+======+========+========+===========+========+ |module |2 |2 |= |50.00 |0.00 | +-----------+----------+---------------+-------------+-------------------+---------------+ |class |5 |5 |= |100.00 |0.00 | +-----------+----------+---------------+-------------+-------------------+---------------+ |method |11 |11 |= |90.91 |0.00 | +-----------+----------+---------------+-------------+-------------------+---------------+ |function |4 |4 |= |75.00 |25.00 | +-----------+----------+---------------+-------------+-------------------+---------------+ External dependencies :: pylint -interfaces (text) -reporters (text) | -ureports | -text_writer (text) -utils (text) Raw metrics +-------------+----------+-------+-----------+-------------+ |type |number |% |previous |difference | +=======+======+=====+=====+========+ |code |128 |48.30 |128 |= | +-------------+----------+--------+-----------+------------+ |docstring |84 |31.70 |84 |= | +-------------+----------+--------+-----------+------------+ |comment |16 |6.04 |16 |= | +-------------+----------+--------+-----------+------------+ |empty |37 |13.96 |37 |= | +-------------+----------+--------+-----------+------------+ Duplication +-------------------------------+------+------------+-------------+ | |now |previous |difference | +=================+=====+======+========+ |nb duplicated lines |0 |0 |= | +-------------------------------+-------+------------+------------+ |percent duplicated lines |0.000 |0.000 |= | +-------------------------------+-------+------------+------------+ Messages by category +--------------+----------+-----------+-------------+ |type |number |previous |difference | +========+======+======+========+ |convention |8 |8 |= | +--------------+----------+-----------+-------------+ |refactor |1 |1 |= | +--------------+-----------+----------+-------------+ |warning |3 |3 |= | +--------------+-----------+----------+-------------+ |error |3 |3 |= | +--------------+-----------+----------+-------------+ % errors / warnings by module +-----------+--------+-----------+----------+--------------+ |module |error |warning |refactor |convention | +======+=====+======+======+========+ |example |100.00 |66.67 |100.00 |100.00 | +-----------+---------+----------+-----------+-------------+ |text |0.00 |33.33 |0.00 |0.00 | +-----------+---------+----------+-----------+-------------+ Messages +-----------------------------+----------------+ |message id |occurrences | +=================+=========+ |missing-docstring |3 | +-----------------------------+----------------+ |bad-whitespace |3 | +------------------------------+---------------+ |invalid-name |2 | +------------------------------+---------------+ |unused-import |1 | +------------------------------+---------------+ |unused-argument |1 | +------------------------------+---------------+ |too-many-function-args |1 | +------------------------------+---------------+ |too-few-public-methods |1 | +------------------------------+---------------+ |protected-access |1 | +------------------------------+---------------+ |no-self-argument |1 | +------------------------------+---------------+ |import-error |1 | +------------------------------+---------------+ ------------------------------------------------------------------------------------------ Your code has been rated at 7.59/10 (previous run: 7.59/10, +0.00)
Программа имеет свою внутреннюю маркировку проблемных мест в коде:
[R]efactor — требуется рефакторинг,
[C]onvention — нарушено следование стилистике и соглашениям,
[W]arning — потенциальная ошибка,
[E]rror — ошибка,
[F]atal — ошибка, которая препятствует дальнейшей работе программы.
Для вывода подробного отчета мы использовали ключ командной строки –reports=y.
Более гибко настроить вывод команды позволяют разнообразные ключи командной строки. Настройки можно сохранять в файле настроек rcfile. Мы не будем приводить подробное описание ключей и настроек, для этого есть официальная документация — https://pylint.readthedocs.io/en/latest/index.html#, остановимся лишь на наиболее интересных, с нашей точки зрения, возможностях утилиты:
— Генерация файла настроек (–generate-rcfile). Позволяет не писать конфигурационный файл с нуля. В созданном rcfile содержатся все текущие настройки с подробными комментариями к ним, вам остается только отредактировать его под собственные требования.
— Отключение вывода в коде. При редактировании кода есть возможность вставить блокирующие вывод сообщений комментарии. Чтобы продемонстрировать это, в определение функции в файле примера example.py добавим строку:
# pylint: disable=unused-argument
и запустим pylint. Из результатов проверки “исчезло” сообщение:
example.py:4:17: W0613: Unused argument 'num_two' (unused-argument)
— Создание отчетов в формате json (–output-format=json). Полезно, если необходимо сохранение или дальнейшая обработка результатов работы линтера. Вы также можете создать собственный формат вывода данных.
— Параллельный запуск (-j 4). Запуск в нескольких параллельных потоках на многоядерных процессорах сокращает время проверки.
— Встроенная документация. Вызов программы с ключом –help-msg=<key> выведет справку по ключевому слову key. В качестве ключевого слова может быть код сообщения (например: E0401) или символическое имя сообщения (например: import-error). Ниже приведен листинг получения справки по ключу import-error:
$ python3.6 -m pylint --help-msg=import-error :import-error (E0401): *Unable to import %s* Used when pylint has been unable to import a module. This message belongs to the imports checker.
— Система оценки сохраняет последний результат и при последующих запусках показывает изменения, что позволяет количественно оценить прогресс исправлений.
— Плагины — отличная возможность изменять поведение pylint. Их применение может оказаться полезным в случаях, когда pylint неправильно обрабатывает код и есть “ложные” срабатывания, или когда требуется отличный от стандартного формат вывода результатов.
Vulture
Vulture — небольшая утилита для поиска “мертвого” кода в программах Python. Она использует модуль ast стандартной библиотеки и создает абстрактные синтаксические деревья для всех файлов исходного кода в проекте. Далее осуществляется поиск всех объектов, которые были определены, но не используются. Vulture полезно применять для очистки и нахождения ошибок в больших базовых кодах.
Продолжение следует
Во второй части мы продолжим разговор об инструментах для анализа кода Python. Будут рассмотрены линтеры, представляющие собой наборы утилит. Также мы посмотрим, какие программы можно использовать для автоматического форматирования кода.
Еще статьи по Python
- 26 полезных возможностей Python: букварь разработки от А до Z;
- ТОП-15 трюков в Python 3, делающих код понятнее и быстрее;
- Новый Python: 7 возможностей, которые вам понравятся;
- Крупнейшая подборка Python-каналов на Youtube;
- Изучение Python: ТОП-10 вопросов разной направленности.
Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером
Отладка
—
Основы Python
Даже у самых опытных разработчиков код редко работает идеально с первого раза. Но чем выше навыки разработчика, тем лучше он отлаживает код, то есть анализирует ошибки и устраняет их.
Навык отладки сам по себе не появится: его необходимо развивать, причем начинать нужно как можно раньше. По ходу обучения вы будете выполнять задания и практиковаться — все это поможет набраться опыта. Со временем анализ и устранение ошибок войдут в привычку, если уделять достаточно внимания практике.
Как найти ошибку в коде
Можно отлаживать неисправный код методом тыка, но это долго и непродуктивно. Будет намного проще, если вы сначала поймете проблему, а уже потом начнете устранять ее. Понимание — это ключевой этап, без которого дальнейшие шаги невозможны.
Перед отладкой кода надо понять, что в нем не так. Это можно сделать за два шага.
Шаг 1. Изучить трейсбек (traceback) — список всех вызовов функций от запуска программы до места с ошибкой. Трейсбек помогает отследить, как прошло выполнение программы: какие функции у нее получилось вызвать успешно, а с какими — возникли сложности. Каждая запись в трейсбеке указывает на файл и строчку, а затем на выполняемую функцию.
Представим, что вы написали код в файле users.py
и решили запустить функцию main()
в четвертой строчке. Запись в трейсбеке будет выглядеть так:
File "users.py", line 4, in <module>
main()
Как видите, здесь указаны не только файл и строчка, но еще и название модуля. По нему можно с легкостью определить, где возникла проблема: в вашем коде или в какой-то библиотеке, которую вы не писали, но используете.
Шаг 2. Когда трейсбек дойдет до проблемного места, он выдаст сообщение об ошибке. Например, такое:
NameError: name 'create' is not defined
Если владеете английским, то быстрее поймете, о чем идет речь в сообщении: «Название create
не определено». Эта ошибка чаще всего происходит из-за опечатки в названии — нужно проверить этот момент. Без знания английского тоже можно разобраться, если обратиться к словарю или онлайн-переводчику.
Теперь посмотрим, как трейсбек и сообщение об ошибке выглядят вместе:
Traceback (most recent call last):
File "users.py", line 4, in <module>
main()
File "users.py", line 2, in main
create()
NameError: name 'create' is not defined
В примере выше видно всю цепочку событий: программа успешно справилась с функцией main()
, а потом перешла к функции create()
и столкнулась с ошибкой в названии.
Кроме NameError, в Python есть еще множество разных ошибок, которые можно разделить на три группы.
Типы ошибок
Самые простые и понятные ошибки — синтаксические. Они связаны исключительно с тем, что код неверно оформлен: например, использованы неправильные кавычки.
В выводе таких ошибок всегда присутствует фраза SyntaxError:
. Чтобы отладить код в этом случае, нужно внимательно взглянуть на место с ошибкой. Посмотрим на примере. Здесь синтаксическая ошибка произошла потому, что использована кавычка '
вместо "
:
Traceback (most recent call last):
File "users.py", line 2
print("Hello" + "world')
^
SyntaxError: EOL while scanning string literal
Вторая большая группа ошибок — это ошибки программирования. Например, к ним относятся:
- Вызов несуществующей функции
- Использование необъявленной переменной
- Передача неверных аргументов (например, аргументов неверного типа)
Эти ошибки исправить труднее, чем синтаксические. Обычно они возникают в результате неправильной логики в другом, более раннем вызове.
Последний тип ошибок — логические ошибки. Исправить ситуацию бывает очень сложно, потому что программа в целом работает, но при некоторых значениях выдает неверный результат. В большинстве случаев проблема кроется в неверной логике. Например, вместо сложения в программе выполняется вычитание:
# Функция должна считать сумму чисел, но считает разность:
def sum(a, b):
return a - b
# при таком вызове ошибка неочевидна, потому что
# и при сложении, и при вычитании будет один и тот же результат
sum(4, 0) # 4
Способы отладки
Есть множество способов отладки программ, но у всех одна общая идея — нужно проанализировать, как меняются значения переменных в процессе работы кода.
Рассмотрим на конкретном примере. Ниже описана функция, которая считает сумму чисел от числа start
до числа finish
. Если start
равно трем, а finish
— пяти, то программа должна вычислить: 3 + 4 + 5
.
def sum_of_series(start, finish):
result = 0
n = start
while n < finish:
result = result + n
n = n + 1
return result
В этом коде допущена ошибка. Глядя на код функции sum_of_series()
замечаем, что основных переменных там две: n
и result
. Из этого можно сделать такой вывод — нужно посмотреть, какие значения даются переменным на каждой итерации. После этого найти ошибку не составит труда.
Есть удобный способ отслеживать значения переменных во время выполнения кода — это визуальные отладчики. Они встраиваются в популярные редакторы кода и позволяют выполнить программу по шагам и наглядно увидеть все изменения. Если интересно узнать больше об отладчиках, можно поискать в Google по запросу Python Debuggers.
В среде Хекслета отладчика нет, поэтому здесь используется другой подход — отладочная печать. Суть такая же, как и в визуальном отладчике. Разница только в том, что для вывода значений переменных используется обычная печать на экран. То, что печатается на экран, отображается во вкладке OUTPUT
, на которую автоматически переключается редактор во время проверки. Посмотрим на примере:
def sum_of_series(start, finish):
result = 0
n = start
while n < finish:
print('new iteration !!!!')
print(n)
result = result + n
n = n + 1
print(result)
return result
sum_of_series(3, 5)
# new iteration !!!!
# 3
# 3
# new iteration !!!!
# 4
# 7
Здесь видно, что итераций цикла на одну меньше, чем нужно. Почему-то не выполняется сложение для последнего числа, которое обозначено как finish
. И действительно, если посмотреть на определение, то видно, что там используется n < finish
вместо n <= finish
. Так с помощью отладки удалось найти ошибку — оказывается, был выбран знак <
вместо <=
.
Новички часто расстраиваются из-за ошибок, начинают считать себя невнимательными. На самом деле, в ошибках нет ничего страшного: опытные разработчики допускают их не реже новичков. Сложно научиться писать идеальный код, но можно развивать навыки отладки и насмотренность на ошибки.
Еще новички думают, что опытный разработчик может взглянуть на код и сразу же понять, в чем ошибка. Да, с опытом приходит насмотренность, но все не так просто. По фрагменту кода сложно понять, что пошло не так. Если хотите спросить совет у опытного разработчика, лучше покажите не сам неисправный код, а сообщение об ошибке.
Дополнительные материалы
- Как найти ошибки в коде?
- pdb — The Python Debugger
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты.
Анализ кода в Python может быть трудной темой, но очень полезной в тех случаях, когда вам нужно повысить производительность вашей программы. Существует несколько анализаторов кода для Python, которые вы можете использовать для проверки своего кода и выяснить, соответствует ли он стандартам. Самым популярным можно назвать pylint. Он очень удобен в настойках и подключениях. Он также проверяет ваш код на соответствие с PEP8, официальным руководством по стилю ядра Python, а также ищет программные ошибки. Обратите внимание на то, что pylint проверяет ваш код на большую часть стандартов PEP8, но не на все. Также мы уделим наше внимание тому, чтобы научиться работать с другим анализатором кода, а именно pyflakes.
Начнем с pylint
Пакет pylint не входит в Python, так что вам нужно будет посетить PyPI (Python Package Index), или непосредственно сайт пакета для загрузки. Вы можете использовать следующую команду, которая сделает всю работу за вас:
Если все идет по плану, то pylint установится, и мы сможем пойти дальше.
Анализ вашего кода
После установки pylint вы можете запустить его в командной строке, без каких либо аргументов, что бы увидеть, какие опции он принимает. Если это не сработало, можете прописать полный путь, вот так:
c:Python34Scriptspylint |
Теперь нам нужен какой-нибудь код для анализа. Вот часть кода, которая содержит четыре ошибки. Сохраните её в файле под названием crummy_code.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import sys class CarClass: “””””” def __init__(self, color, make, model, year): “””Constructor””” self.color = color self.make = make self.model = model self.year = year if “Windows” in platform.platform(): print(“You’re using Windows!”) self.weight = self.getWeight(1, 2, 3) def getWeight(this): “””””” return “2000 lbs” |
Можете увидеть ошибки не запуская код? Давайте посмотрим, может ли pylint найти их!
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Telegram Чат & Канал
Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
После запуска этой команды вы увидите большую выдачу на вашем экране. Вот частичный пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
c:py101>c:Python34Scriptspylint crummy_code.py No config file found, using default configuration ************* Module crummy_code C: 2, 0: Trailing whitespace (trailing-whitespace) C: 5, 0: Trailing whitespace (trailing-whitespace) C: 12, 0: Trailing whitespace (trailing-whitespace) C: 15, 0: Trailing whitespace (trailing-whitespace) C: 17, 0: Trailing whitespace (trailing-whitespace) C: 1, 0: Missing module docstring (missing-docstring) C: 3, 0: Empty class docstring (empty-docstring) C: 3, 0: Old-style class defined. (old-style-class) E: 13,24: Undefined variable ‘platform’ (undefined-variable) E: 16,36: Too many positional arguments for function call (too-many-function-args) C: 18, 4: Invalid method name “getWeight” (invalid-name) C: 18, 4: Empty method docstring (empty-docstring) E: 18, 4: Method should have “self” as first argument (no-self-argument) R: 18, 4: Method could be a function (no-self-use) R: 3, 0: Too few public methods (1/2) (too-few-public-methods) W: 1, 0: Unused import sys (unused-import) |
Давайте немного притормозим и разберемся. Сначала нам нужно понять, что означают буквы:
- С – конвенция (convention)
- R – рефакторинг (refactor)
- W – предупреждение (warning)
- E – ошибка (error)
Наш pylint нашел 3 ошибки, 4 проблемы с конвенцией, 2 строки, которые нуждаются в рефакторинге и одно предупреждение. Предупреждение и 3 ошибки – это как раз то, что я искал. Мы попытаемся исправить этот код и устранить ряд проблем. Для начала мы наведем порядок в импортах, и изменить функцию getWeight на get_weight, в связи с тем, что camelCase не используется в названиях методов. Нам также нужно исправить вызов get_weight, чтобы он передавал правильное количество аргументов и исправить его, чтобы “self” выступал в качестве первого аргумента. Взглянем на новый код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# crummy_code_fixed.py import platform class CarClass: “””””” def __init__(self, color, make, model, year): “””Constructor””” self.color = color self.make = make self.model = model self.year = year if “Windows” in platform.platform(): print(“You’re using Windows!”) self.weight = self.get_weight(3) def get_weight(self, this): “””””” return “2000 lbs” |
Давайте запустим новый код с pylint и посмотрим, насколько успешно мы провели работу. Для краткости, мы еще раз рассмотрим первую часть:
c:py101>c:Python34Scriptspylint crummy_code_fixed.py No config file found, using default configuration ************* Module crummy_code_fixed C: 1,0: Missing docstring C: 4,0: CarClass: Empty docstring C: 21,4: CarClass.get_weight: Empty docstring W: 21,25: CarClass.get_weight: Unused argument ‘this’ R: 21,4: CarClass.get_weight: Method could be a function R: 4,0: CarClass: Too few public methods (1/2) |
Как мы видим, это очень помогло. Если мы добавим docstrings, мы можем снизить количество ошибок вдвое. Теперь мы готовы перейти к pyflakes!
Работаем с pyflakes
Проект pyflakes это часть чего-то, что называется Divmod Project. Pyflakes на самом деле не выполняет проверяемый код также, как и pylint. Вы можете установить pyflakes при помощи pip, easy_install, или из другого источника.
Данный сервис может предложить Вам персональные условия при заказе классов на посты и фото в Одноклассники. Приобретайте необходимый ресурс не только со скидками, но и с возможностью подобрать наилучшее качество и скорость поступления.
Мы начнем с запуска pyflakes в изначальной версии той же части кода, которую мы использовали для проверки pylint. Вот и он:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import sys class CarClass: “””””” def __init__(self, color, make, model, year): “””Constructor””” self.color = color self.make = make self.model = model self.year = year if “Windows” in platform.platform(): print(“You’re using Windows!”) self.weight = self.getWeight(1, 2, 3) def getWeight(this): “””””” return “2000 lbs” |
Как мы отмечали в предыдущем разделе, в этом поломанном коде четыре ошибки, три из которых препятствуют работе программы. Давайте посмотрим, что же pyflakes может найти. Попытайтесь запустить данную команду и на выходе вы должны получить следующее:
c:py101>c:Python34Scriptspyflakes.exe crummy_code.py crummy_code.py:1: ‘sys’ imported but unused crummy_code.py:13: undefined name ‘platform’ |
Несмотря на суперски быструю скорость возврата выдачи, pyflakes не нашел все ошибки. Вызов метода getWeight передает слишком много аргументов, также метод getWeight сам по себе определен некорректно, так как у него нет аргумента self. Что-же, вы, собственно, можете называть первый аргумент так, как вам угодно, но в конвенции он всегда называется self. Если вы исправили код, оперируя тем, что вам сказал pyflakes, код не заработает, несмотря на это.
Подведем итоги
Следующим шагом должна быть попытка запуска pylint и pyflakes в вашем собственном коде, либо же в пакете Python, вроде SQLAlchemy, после чего следует изучить полученные в выдаче данные. Вы можете многое узнать о своем коде, используя данные инструменты. pylint интегрирован с Wingware, Editra, и PyDev. Некоторые предупреждения pylint могут показаться вам раздражительными, или не особо уместными. Существует несколько способов избавиться от таких моментов, как предупреждения об устаревании, через опции командной строки. Вы также можете использовать -generate-rcfile для создания примера файла config, который поможет вам контролировать работу pylint. Обратите внимание на то, что pylint и pyflakes не импортируют ваш код, так что вам не нужно беспокоиться о нежелательных побочных эффектах.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»
Линтеры
В сообществе Python, как и в любой другой группе людей, существует некое
коллективное знание. Множество людей прошлось по всем возможным граблям
и получило опыт через набитые шишки. Затем через какое-то время,
благодаря выступлениям на конференциях, официальным заявлениям,
документам, статьям в блогах, код-ревью и личному общению,
это знание стало коллективным. Теперь мы просто называем его
“хорошими практиками”.
К таким хорошим практикам можно отнести, например, следующие.
- Форматировать код по PEP8
— если этого не делать, то другим людям будет намного сложнее понимать
ваш код; в плохо оформленном коде сложнее увидеть суть,
потому что мозг постоянно отвлекается на не несущие смысловой нагрузки
особенности оформления. - Не допускать объявленных, но неиспользуемых переменных/функций/импортов
— опять же, это усложняет восприятие кода; читателю потребуется потратить
время на то, чтобы осознать, что вот на эту сущность обращать внимания не
нужно. - Писать короткие функции — слишком сложные функции с большим
количеством ветвлений и циклов тяжело понимать. - Не использовать изменяемый объект в качестве значения аргумента
функции по умолчанию — иначе в результате можно получить
очень неожиданные эффекты.
Соблюдать (и даже просто помнить) все хорошие практики — не самая простая
задача. Зачастую люди плохо справляются с тем, чтобы отсчитывать пробелы
и контролировать переменные, и вообще склонны допускать ошибки по
невнимательности. Таковы люди, ничего не поделаешь. Машины, наоборот,
прекрасно справляются с такими хорошо определёнными задачами, поэтому
появились инструменты, которые контролируют следование хорошим практикам.
В компилируемых языках ещё на этапе компиляции программист может получить
по щщам первый полезный фидбэк о написанном коде.
Компилятор проверит, что код валиден и может быть скомпилирован, а также может
выдать предупреждения и рекомендации, как сделать код лучше или читаемее.
Т.к. Python является интерпретируемым языком, где этап компиляции как таковой
отсутствует, линтеры особенно полезны. На самом деле, это очень важно и
круто — узнать, что твой код как минимум является валидным Python-кодом,
даже не запуская его.
В этом посте я рассмотрю два самых популярных линтера для Python:
- flake8;
- pylint.
Термин “lint” впервые начал использоваться в таком значении в 1979 году.
Так называлась программа для статического анализа кода на C,
которая предупреждала об использовании непортабельных на другие архитектуры
языковых конструкций. С тех пор “линтерами” называют любые статические
анализаторы кода, которые помогают находить распространённые ошибки, делать
его однообразным и более читаемым. А названо оно “lint” в честь вот такой
штуки:
flake8
flake8
— это утилита-комбайн, которая органично объединяет в себе несколько
других анализаторов кода (pycodestyle
, pyflakes
и mccabe
), а также
имеет огромную экосистему плагинов, которые могут добавить к стандартной
поставке ещё кучу различных проверок. На данный момент, это самый
популярный линтер для Python-кода. Кроме того, он предельно прост в
настройке и использовании.
Установка
flake8
устанавливается, как и любой другой Python-пакет,
через pip
. Внутри виртуального окружения проекта выполните:
Если вы пользуетесь pipenv
, то flake8
нужно устанавливать
как dev-зависимость (ведь для работы программы линтер не нужен,
он нужен только для разработчика):
$ pipenv install --dev flake8
Аналогично с poetry
:
$ poetry add --dev flake8
Проверим установку:
$ flake8 --version
3.8.1 (mccabe: 0.6.1, pycodestyle: 2.6.0, pyflakes: 2.2.0) CPython 3.8.2 on Linux
Использование
Для работы flake8
нужно просто указать файл или директорию, которые
нужно проверять, например:
# проверить один файл
$ flake8 file.py
# проверить директорию рекурсивно
$ flake8 src/
# проверить текущую директорию рекурсивно
$ flake8 .
Давайте для демонстрации попытаемся написать программу с как можно большим
количеством “плохих практик”:
Возможно, вам не видно всего, но в этом коде точно есть следующие “запахи кода”:
import *
— импортирование всех имен из модуля, хотя используется
из них только одно;import itertools
— ненужный импорт;- во множестве мест стоят лишние или отсутствующие пробелы;
- название функции написано в стиле PascalCase;
- в некоторых местах используются табы для отступов;
- используется список (изменяемый объект) в качестве значения аргумента
функции по умолчанию; - используется слишком “широкое” выражение
except:
без указания
конкретного исключения.
Давайте посмотрим, что flake8
скажет по поводу этого файла:
$ flake8 bad_code.py
bad_code.py:1:1: F403 'from math import *' used; unable to detect undefined names
bad_code.py:2:1: F401 'itertools' imported but unused
bad_code.py:4:1: E302 expected 2 blank lines, found 1
bad_code.py:4:4: E271 multiple spaces after keyword
bad_code.py:4:25: E211 whitespace before '('
bad_code.py:4:33: E202 whitespace before ')'
bad_code.py:5:1: W191 indentation contains tabs
bad_code.py:5:8: E271 multiple spaces after keyword
bad_code.py:5:10: F405 'sqrt' may be undefined, or defined from star imports: math
bad_code.py:5:21: E202 whitespace before ')'
bad_code.py:7:1: E302 expected 2 blank lines, found 1
bad_code.py:7:23: E741 ambiguous variable name 'l'
bad_code.py:8:1: E101 indentation contains mixed spaces and tabs
bad_code.py:9:1: E101 indentation contains mixed spaces and tabs
bad_code.py:11:1: E305 expected 2 blank lines after class or function definition, found 1
bad_code.py:12:1: E101 indentation contains mixed spaces and tabs
bad_code.py:13:1: E101 indentation contains mixed spaces and tabs
bad_code.py:13:20: E225 missing whitespace around operator
bad_code.py:14:1: E101 indentation contains mixed spaces and tabs
bad_code.py:14:67: W291 trailing whitespace
bad_code.py:15:1: E101 indentation contains mixed spaces and tabs
bad_code.py:15:14: W291 trailing whitespace
bad_code.py:16:1: E101 indentation contains mixed spaces and tabs
bad_code.py:16:5: E722 do not use bare 'except'
bad_code.py:17:1: E101 indentation contains mixed spaces and tabs
Как видите, flake8
нашёл кучу ошибок. Для каждой ошибки указана строка
и номер символа в строке (не всегда точный), где произошла ошибка.
Также у каждой категории ошибок есть свой код: E101
, W291
и т.д.
Эти коды ошибок могут использоваться для включения/отключения правил.
Тем не менее, не все ошибки были найдены. Давайте установим пару плагинов,
чтобы добавить ещё правил!
Плагины
Как я уже говорил, для flake8
написано множество плагинов.
Обычно плагины легко гуглятся или находятся в списках плагинов.
Есть плагины для всех популярных фреймворков и библиотек — пользуйтесь ими!
Давайте для нашего простого примера установим
flake8-bugbear
(находит распространённые логические ошибки) и
pep8-naming
(проверяет имена на соответствие PEP8).
Плагины устанавливаются так же, как и сам flake8
(для краткости я
не буду писать примеры для pipenv
и poetry
— сами сможете обобщить):
$ pip install flake8-bugbear pep8-naming
Давайте убедимся, что плагины действительно установились
и flake8
может их найти:
$ flake8 --version
3.8.1 (flake8-bugbear: 20.1.4, mccabe: 0.6.1, naming: 0.10.0, pycodestyle: 2.6.0, pyflakes: 2.2.0) CPython 3.8.2 on Linux
Если вы видите в списке в скобках названия ваших плагинов, то всё хорошо.
Теперь снова проверим наш файл:
$ flake8 bad_code.py
bad_code.py:1:1: F403 'from math import *' used; unable to detect undefined names
bad_code.py:2:1: F401 'itertools' imported but unused
bad_code.py:4:1: E302 expected 2 blank lines, found 1
bad_code.py:4:4: E271 multiple spaces after keyword
bad_code.py:4:6: N802 function name 'CalculateSquareRoot' should be lowercase
bad_code.py:4:25: E211 whitespace before '('
bad_code.py:4:28: N803 argument name 'Number' should be lowercase
bad_code.py:4:33: E202 whitespace before ')'
bad_code.py:5:1: W191 indentation contains tabs
bad_code.py:5:8: E271 multiple spaces after keyword
bad_code.py:5:10: F405 'sqrt' may be undefined, or defined from star imports: math
bad_code.py:5:21: E202 whitespace before ')'
bad_code.py:7:1: E302 expected 2 blank lines, found 1
bad_code.py:7:23: E741 ambiguous variable name 'l'
bad_code.py:7:25: B006 Do not use mutable data structures for argument defaults. They are created during function definition time. All calls to the function reuse this one instance of that data structure, persisting changes between them.
bad_code.py:8:1: E101 indentation contains mixed spaces and tabs
bad_code.py:9:1: E101 indentation contains mixed spaces and tabs
bad_code.py:11:1: E305 expected 2 blank lines after class or function definition, found 1
bad_code.py:12:1: E101 indentation contains mixed spaces and tabs
bad_code.py:13:1: E101 indentation contains mixed spaces and tabs
bad_code.py:13:20: E225 missing whitespace around operator
bad_code.py:14:1: E101 indentation contains mixed spaces and tabs
bad_code.py:14:67: W291 trailing whitespace
bad_code.py:15:1: E101 indentation contains mixed spaces and tabs
bad_code.py:15:14: W291 trailing whitespace
bad_code.py:16:1: E101 indentation contains mixed spaces and tabs
bad_code.py:16:5: E722 do not use bare 'except'
bad_code.py:16:5: B001 Do not use bare `except:`, it also catches unexpected events like memory errors, interrupts, system exit, and so on. Prefer `except Exception:`. If you're sure what you're doing, be explicit and write `except BaseException:`.
bad_code.py:17:1: E101 indentation contains mixed spaces and tabs
В выводе появились новые категории ошибок (N802
, B006
)
— они как раз добавлены плагинами. На этот раз, как мне кажется,
найдены все ошибки. К сожалению, flake8
не умеет сам чинить
найденные ошибки, поэтому давайте сделаем это вручную:
Обратите внимание на строки 8 и 10, там содержится комментарии # noqa
.
При помощи этих комментариев можно заставить flake8
игнорировать ошибки.
Это бывает полезно, когда по какой-то причине код должен остаться именно
таким, например:
- он автоматически сгенерирован и исправление в нём ошибок не имеет смысла;
- исправление этой ошибки породит куда более уродливый код,
чем комментарий# noqa
; - у вас просто сейчас нет времени, чтобы исправлять эту ошибку
(плохая отмазка, серьёзно).
Если не указать код ошибки, то будут проигнорированы все ошибки в строке
— я не рекомендую так делать, потому что так можно пропустить
и на самом деле плохие ошибки. Если указать номер правила, то
flake8
будет игнорировать только указанную категорию,
а о других ошибках в этой же строке доложит.
Вообще, комментариями # noqa
нужно пользоваться с большой осторожностью.
Считайте, что каждый раз, когда вы это делаете, вы берёте на
себя ответственность за эту строку кода. Если программа сломается
в этом месте, то пеняйте на себя — минздрав линтер вас предупреждал.
Конфигурация
flake8
для работы не требует никакой конфигурации.
Он имеет достаточно (но не слишком) строгие настройки по умолчанию,
которые подойдут большинству пользователей, но иногда бывает нужно
отключить (или наоборот включить) определённые правила на уровне всего проекта.
Сделать это можно через файлы .flake8
или setup.cfg
в корне проекта.
Если у вас в проекте уже есть файл setup.cfg
, то можно добавить конфигурацию
flake8
в него. Если вы предпочитаете для каждой утилиты держать
отдельный файл конфигурации, то используйте .flake8
. В любом случае,
формат для обоих этих файлов совпадает:
[flake8]
ignore = D203,E741
exclude =
# No need to traverse our git directory
.git,
# There's no value in checking cache directories
__pycache__,
# The conf file is mostly autogenerated, ignore it
docs/source/conf.py,
# The old directory contains Flake8 2.0
old,
# This contains our built documentation
build,
# This contains builds of flake8 that we don't want to check
dist
max-complexity = 10
В конфигурации можно перечислить игнорируемые правила и директории,
в которые flake8
заглядывать не будет, а также максимальную
цикломатическую сложность
для функций. Все эти настройки будут автоматически применяться
к запускам flake8
во всех поддиректориях проекта.
Если же вам не хватает какого-нибудь правила, и его нет даже в уже
готовых плагинах, то написание собственного плагина
— не такая уж и сложная задача.
Я попробовал,
у меня на это ушло 2-3 часа.
pylint
pylint
— это ещё один популярный линтер для Python.
Этот линтер значительно умнее и продвинутее flake8
.
В pylint
из коробки заложено очень много правил и рекомендаций,
и по умолчанию они все включены, так что он достаточно строгий и придирчивый.
Чтобы интегрировать его в существующий большой проект придётся потратить
некоторое время, чтобы выбрать те правила, которые для вас важны.
Так же как и flake8
, pylint
поддерживает плагины для расширения
базовой функциональности, но насколько я вижу, экосистема плагинов у pylint
значительно беднее.
Также при каждом запуске pylint
выводит оценку качества кода
по десятибалльной шкале, а также следит, как эта оценка меняется
с течением времени. Достичь десятки очень сложно, но это благородная цель,
к которой нужно стремиться.
Установка
Установка pylint
принципиально ничем не отличается от установки flake8
.
Выполнить внутри виртуального окружения проекта:
Для pipenv
:
$ pipenv install --dev pylint
Для poetry
:
$ poetry add --dev pylint
Использование
pylint
можно натравить на определённый файл:
С директориями у pylint
дела обстоят чуть сложнее. Все директории он
обрабатывает как питоновские модули, поэтому если в директории нет хотя бы
пустого файла __init__.py
, то работать с ней pylint
не сможет. Имейте
это ввиду.
Давайте попросим pylint
прокомментировать файл с плохими практиками
из предыдущего примера:
$ pylint bad_code.py
************* Module bad_code
bad_code.py:4:25: C0326: No space allowed before bracket
def CalculateSquareRoot (Number ):
^ (bad-whitespace)
bad_code.py:4:33: C0326: No space allowed before bracket
def CalculateSquareRoot (Number ):
^ (bad-whitespace)
bad_code.py:5:0: W0312: Found indentation with tabs instead of spaces (mixed-indentation)
bad_code.py:5:21: C0326: No space allowed before bracket
return sqrt(Number )
^ (bad-whitespace)
bad_code.py:13:19: C0326: Exactly one space required around assignment
your_number=float(input('Enter your number: '))
^ (bad-whitespace)
bad_code.py:14:66: C0303: Trailing whitespace (trailing-whitespace)
bad_code.py:15:13: C0303: Trailing whitespace (trailing-whitespace)
bad_code.py:1:0: W0622: Redefining built-in 'pow' (redefined-builtin)
bad_code.py:1:0: C0114: Missing module docstring (missing-module-docstring)
bad_code.py:1:0: W0401: Wildcard import math (wildcard-import)
bad_code.py:4:0: C0103: Function name "CalculateSquareRoot" doesn't conform to snake_case naming style (invalid-name)
bad_code.py:4:0: C0103: Argument name "Number" doesn't conform to snake_case naming style (invalid-name)
bad_code.py:4:0: C0116: Missing function or method docstring (missing-function-docstring)
bad_code.py:7:0: W0102: Dangerous default value [] as argument (dangerous-default-value)
bad_code.py:7:0: C0103: Argument name "l" doesn't conform to snake_case naming style (invalid-name)
bad_code.py:7:0: C0116: Missing function or method docstring (missing-function-docstring)
bad_code.py:16:4: W0702: No exception type(s) specified (bare-except)
bad_code.py:1:0: W0614: Unused import acos from wildcard import (unused-wildcard-import)
bad_code.py:1:0: W0614: Unused import acosh from wildcard import (unused-wildcard-import)
bad_code.py:1:0: W0614: Unused import asin from wildcard import (unused-wildcard-import)
bad_code.py:1:0: W0614: Unused import asinh from wildcard import (unused-wildcard-import)
...
bad_code.py:2:0: W0611: Unused import itertools (unused-import)
-------------------------------------
Your code has been rated at -41.43/10
Я немного сократил вывод. Как видите, даже без плагинов pylint
нашёл
все ожидаемые ошибки, и даже больше — например, он даже предлагает написать
документацию.
По каждой ошибке можно запросить более подробную справку, используя
название правила из конца строки с ошибкой или код:
$ pylint --help-msg=missing-docstring
$ pylint --help-msg=R0902
Вот какие ошибки pylint
находит для файла, который с точки зрения flake8
не содержит никаких ошибок:
$ pylint not_so_bad_code.py
************* Module not_so_bad_code
not_so_bad_code.py:1:0: C0114: Missing module docstring (missing-module-docstring)
not_so_bad_code.py:4:0: C0116: Missing function or method docstring (missing-function-docstring)
not_so_bad_code.py:8:0: C0103: Argument name "l" doesn't conform to snake_case naming style (invalid-name)
not_so_bad_code.py:8:0: C0116: Missing function or method docstring (missing-function-docstring)
not_so_bad_code.py:20:11: W0703: Catching too general exception Exception (broad-except)
-----------------------------------
Your code has been rated at 6.67/10
А вот так в pylint
можно игнорировать отдельную ошибку на строке прямо в файлах
с кодом:
def append_item(item, l=None): # pylint: disable=C0103
...
Ещё pylint
умеет игнорировать ошибки в блоках кода:
def test():
# Disable all the no-member violations in this function
# pylint: disable=no-member
...
И для файлов целиком. Вот так можно отключить все ошибки из категорий
Warning, Convention и Refactor:
А можно не проверять файл вообще:
Подробнее о правилах управления сообщениями
смотрите в документации.
Для более сложной настройки правил, придётся по-настоящему сконфигурировать
pylint
.
Конфигурация
pylint
настраивается через файл .pylintrc
в корне проекта. Чтобы создать
дефолтный файл конфигурации, нужно выполнить следующую команду:
$ pylint --generate-rcfile > .pylintrc
Созданный файл содержит все поддерживаемые pylint
опции с довольно
подробными комментариями, так что углубляться я не буду.
Плагины
Давайте установим какой-нибудь популярный плагин, например,
pylint-django
:
$ pip install pylint-django
Теперь запускать pylint
нужно вот так:
$ pylint --load-plugins pylint_django [..other options..] <path_to_your_sources>
либо в .pylintrc
нужно исправить директиву load-plugins
:
load-plugins=pylint_django
Интеграция линтера в проект
Интегрировать линтер в проект можно на трёх уровнях.
Я рекомендую по возможности использовать все три, но обязательным
является как минимум один (лучше всего, чтобы это была CI система).
Редактор кода или IDE
Популярные IDE для Python умеют легко интегрировать с линтерами и
подсвечивать ошибки линтера прямо в редактируемом файле.
Это удобно, потому что позволяет не выходя из редактора получить
полезную обратную связь.
PyCharm автоматически находить установленные flake8
и pylint
внутри
интерпретатора проекта
и подключается к ним.
VS Code требует небольшой настройки, которая
описана здесь.
Git-хуки
Также читайте пост про Git-хуки и
pre-commit
.
В git есть возможность запрограммировать
определенные скрипты (хуки) в ответ на действия пользователя.
Например, можно запускать
какие-нибудь проверки перед коммитом, заново скачивать зависимости проекта
при переключении веток, высылать сообщение в рабочий чат
после пуша в удалённый репозиторий и вообще что угодно.
Нас интересует возможность запускать линтер перед коммитом так,
чтобы если линтер найдёт какие-нибудь проблемы, операция коммита прерывалась.
Git-хуки можно настроить, написав несложный shell-скрипт,
но я рекомендую использовать для этого специальные утилиты,
такие как pre-commit.
Вот здесь
можно найти описание процесса настройки запуска flake8
через pre-commit
.
Обратите внимание, что Git-хуки нужно будет настроить на машине каждого
разработчика в проекте.
Continuous Integration (CI)
Последний эшелоном защиты от попадания “сломанного” кода в основную ветку
репозитория является система непрерывной интеграции (CI) — такая, как:
- GitHub Actions;
- GitLab CI
(а ещё читайте пост в блоге моего хорошего товарища про
основы GitLab CI); - Travis CI;
- или другая.
На каждый пуш в репозиторий система непрерывной интеграции должна
запускать проверки (включая все линтеры и тесты), и если что-то идёт
не так, рядом с коммитом должен появиться красный крестик.
Ветку с таким коммитом на конце нельзя будет слить с основной
веткой проекта через пулл-реквест на GitHub (или мёрдж-реквест на GitLab).
Пример того, как настроить GitHub Actions
для запуска flake8
и других питоновских проверок.
CI — это единственный надёжный способ обеспечить качество кода.
Предыдущие способы нужны скорее для удобства разработчика, чтобы он
как можно скорее получал обратную связь, но разработчик вправе проигнорировать
или отключить эти предупреждения.
Заключение
В подзаголовке этой статьи я написал фразу, что линтер способен
сэкономить разработчику один день жизни в месяц. Фраза может показаться
кликбейтной, но, поверьте мне, это так это и работает.
Возможно, я даже преуменьшил.
Чем раньше найдена ошибка, тем быстрее идёт разработка.
Иногда линтер предотвращает баги, иногда спасает от мучительного
траблшутинга. Линтеры абсолютно точно значительно сокращают время,
потраченное коллегами на код-ревью, потому что все тривиальные
ошибки будут отловлены автоматикой.
Не стоит недооценивать линтеры. Это те инструменты,
которые делают из “кодера” настоящего “software engineer”,
из мальчика — мужчину. Если вы до сих пор не пользуетесь каким-нибудь
линтером, то рекомендую всерьез задуматься над внедрением!
Я предпочитаю использовать flake8
, потому что он простой
и понятный, как топор. С ним легко работать, его легко настроить
под свои нужды, а почти любые недостающие правила можно получить
через уже готовые плагины.
У pylint
тоже есть свои последователи. Его ценят за подробный вывод
и большое количество правил в стандартной поставке.
Мне же pylint
всегда казался слишком сложным в эксплуатации.
А кто-то вообще рекомендует устанавливать flake8
и pylint
параллельно.
Если понравилась статья, то
подпишитесь на уведомления
о новых постах в блоге, чтобы ничего не пропустить!
Дополнительное чтение
- документация
flake8
; - исходный код
flake8
; - список плагинов
flake8
; - сайт, где можно посмотреть правила
flake8
; - документация
pylint
; - исходный код
pylint
; - обсуждение “flake8 vs pylint” на Reddit;
- пост на RealPython про качество кода;
- статья на Хабре про линтеры.
Обложка: Sa Mu, Traffic Light
How to use the free code checker
Code
Copy and paste your Python code into the editor.
Language
Select your language from the dropdown.
Check
Click the Check code button.
Improve
Use the results to improve your Python code.
Get code security right from your IDE
This free code checker can find critical vulnerabilities and security issues with a click. To take your application security to the next level, we recommend using Snyk Code for free right from your IDE.
This free web based Python code checker is powered by Snyk Code. Sign up now to get access to all the features including vulnerability alerts, real time scan results, and actionable fix advice within your IDE.
Human-in-the-Loop Python Code Checker
Snyk Code is an expert-curated, AI-powered Python code checker that analyzes your code for security issues, providing actionable advice directly from your IDE to help you fix vulnerabilities quickly.
Real-time
Scan and fix source code in minutes.
Actionable
Fix vulns with dev friendly remediation.
Integrated in IDE
Find vulns early to save time & money.
Ecosystems
Integrates into existing workflow.
More than syntax errors
Comprehensive semantic analysis.
AI powered by people
Modern ML directed by security experts.
In-workflow testing
Automatically scan every PR and repo.
CI/CD security gate
Integrate scans into the build process.