Стильный код на Python, или учимся использовать Flake8

30 декабря 2016
Анатолий Соловей
Стильный код на Python, или учимся использовать Flake8

Язык программирования Python очень востребован на современном рынке, он развивается изо дня в день, и вокруг него сложилось активное сообщество. Во избежание конфликтов между разработчиками-питонистами, создатели языка написали соглашение PEP 8, описывающее правила оформления кода, однако даже там отмечено, что: 

Many projects have their own coding style guidelines. In the event of any conflicts, such project-specific guides take precedence for that project.

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

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

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

На помощь в этом случае приходят линтеры — инструменты, контролирующие оформление кода в проекте. Именно они помогают поддерживать его чистоту и, в нашем случае, предотвращать создание коммитов, которые могут содержать ошибки. Я для контроля качества использую Flake8 и сейчас постараюсь объяснить, почему выбрал именно его, и расскажу, как его настроить, чтобы получить максимальный результат. Заинтересовались? Добро пожаловать под кат.

Flake8: Your Tool For Style Guide Enforcement

Сам Flake8 — инструмент, позволяющий просканировать код проекта и обнаружить в нем стилистические ошибки и нарушения различных конвенций кода на Python.

Flake8 умеет работать не только с PEP 8, но и с другими правилами, к тому же поддерживает кастомные плагины, поэтому в дальнейшем в этой статье я буду отталкиваться от правил из Google Python Style Guide

Почему Flake8?

Flake8: pep8 + pyflakes + more

Создатель Flake8 Тарек Зиаде ставил перед собой цель объединить главные популярные инструменты контроля кодстайла в одной библиотеке, с чем в итоге успешно справился — Flake8 получился действительно универсальным.

Легкость установки и конфигурации

Чтобы проверить, отвечает ли код в вашем проекте основным требованиям PEP 8, достаточно установить Flake:

$ pip install flake8

и запустить его — просто ввести в командной строке:

$ flake8 my_project

после чего вы получите список с именами файлов и номерами строк, где были допущены ошибки, и подробное описание самих ошибок:

$ flake8 my_project  myfile.py:1: 'sys' imported but unused  myfile.py:4:1: E302 expected 2 blank lines, found 1  

Великолепно, не правда ли? Но и это не всё. Если вы не любитель работать с консолью, то вы можете настроить интеграцию Flake8 с IDE или редактором, который вы предпочитаете использовать.

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

Интеграция с PyCharm
Так же актуально и для любой другой IDE от JetBrains.
Интеграция проводится всего за пару несложных шагов. 

Откройте настройки External Tools в File->Settings->Tools и нажмите на “+”, затем заполните поля по этому шаблону:



После этого нажмите на Output Filters, а затем на “+”, чтобы добавить новое правило для вывода сообщений от флейка:


здесь мы говорим PyCharm, что хотим, чтобы в выводе строки с ошибками были кликабельными и открывали в редакторе файл и место с ошибкой

Все. Интеграция Flake8 с PyCharm закончена. Чтобы вызвать флейк и проверить свой код, кликаем правой кнопкой мыши на файл/директорию, которую мы хотим проверить, и в контекстном меню выбираем External Tools -> Flake8.


В выводе PyCharm появится кликабельный список нарушений в выбранном файле/директории:



Интеграция с Atom
Чтобы установить инструмент Flake8 для Atom, используйте Atom package manager в Settings и найдите там linter-flake8:



или вызовите 

apm install flake8

из командной строки.

Затем перейдите в linter-flake8 settings и укажите путь к директории, где установлен flake8:

У linter-flake8 есть собственный ReadMe по настройке, с которым при желании вы можете ознакомиться на странице самого linter-flake8 в Atom.

Наличие Version Control Hooks

Именно это я считаю главным достоинством Flake8, которое выделяет его среди других линтеров. В отличии от большинства подобных инструментов, где для настройки VCS-хуков используются целые отдельные библиотеки и модули (как, например, в Pylint), настройка хуков в флейке проводится буквально в две строчки. 

На момент написания этой статьи, Flake8 умеет использовать pre-commit-хуки для Git и Mercurial. Эти хуки позволяют, например, не допускать создания коммита при нарушении каких-либо правил оформления.

Установить хук для Git:

$ flake8 --install-hook git

И настроить сам гит, чтобы учитывать правила Flake8:

$ git config --bool flake8.strict true

Я продемонстрирую, как Git hook работает на проекте, который я использовал для примера интеграции Flake8 с PyCharm. В модуле flake8tutorial.py мы видим очевидные ошибки: импортированные и неиспользованные модули, остсутствие докстринга и пустой строки в конце файла. 

Первым делом проинициализируем в этом проекте git-0репозиторий, установим flake8 хук и скажем нашему git, что он должен прогонять флейк перед коммитами:

Затем попробуем провести первый коммит:

Как видите, flake8 был вызван перед коммитом и не позволил нам закоммитить невалидные изменения. 

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

Коммит успешно создан. Отлично!

Настройка Flake8 для Mercurial практически идентична. Для начала нужно установить Flake8 Mercurial Hook:

$ flake8 --install-hook mercurial

И настроить сам Mercurial:

$ hg config flake8.strict true

Вот и все, хук для Меrcurial установлен, настроен и готов к использованию!

Подробнее о конфигурации Flake8

Базовая конфигурация

Список дополнительных опций и правил можно передать прямо при вызове из командной строки таким образом:

flake8 --select E123

(в этом примере опцией select мы говорим, чтобы Flake сообщал о нарушениях только правила E123 (это код правила “closing bracket does not match indentation of opening bracket's line”)).

Кстати, полный список опций с описанием вы можете найти в документации к самой библиотеке.

На мой взгляд, куда предпочтительнее настраивать Flake с помощью конфигурационных файлов, вы можете хранить настройки в одном из файлов setup.cfg, tox.ini или.flake8. Для ясности я предпочитаю использовать последний вариант.

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

[flake8]  ignore = D203  exclude = .git,__pycache__,docs/source/conf.py,old,build,dist

В этом файле мы сообщаем Flake, что он не должен оповещать нас о нарушениях правила D203 (“1 blank line required before class docstring”), а также не должен проверять файлы .git,__pycache__,docs/source/conf.py и директории old, build, dist.

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

[flake8]  # it's not a bug that we aren't using all of hacking  ignore =      # F812: list comprehension redefines ...      F812,      # H101: Use TODO(NAME)      H101,      # H202: assertRaises Exception too broad      H202,      # H233: Python 3.x incompatible use of print operator      H233,      # H301: one import per line      H301,      # H306: imports not in alphabetical order (time, os)      H306,      # H401: docstring should not start with a space      H401,      # H403: multi line docstrings should end on a new line      H403,      # H404: multi line docstring should start without a leading new line      H404,      # H405: multi line docstring summary not separated with an empty line      H405,      # H501: Do not use self.__dict__ for string formatting      H501  

Также можно добавить в исключения отдельную строку в вашем модуле, просто оставив на этой строке комментарий noqa. Тогда при проверке модуля Flake8 будет игнорировать ошибки, найденные в строках, помеченных этим комментарием:

import sys # noqa

Модули, расширяющие функциональность

Так как Flake позволяет создавать и использовать кастомные плагины, для него можно найти большое количество open-source плагинов. Я опишу только те, которые использую сам и считаю особенно полезными:

flake8-import-order

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

Этот плагин расширяет список предупреждений Flake, добавляя туда три новых:

  • I100: Your import statements are in the wrong order.
  • I101: The names in your from import are in the wrong order.
  • I201: Missing newline between sections or imports.

Установка:

pip install flake8-import-order


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

[flake8]  application-import-names = my_project, tests # Указываем флейку директории, в которых хранятся локальные пакеты.  import-order-style = google # Указываем флейку на то, в каком порядке должны идти импорты. Как я уже говорил выше, я предпочитаю использовать Google Style Guide.

Более подробно о настройке flake8-import-order можно прочитать на странице библиотеки на Github.

flake8-docstrings

Плагин, добавляющий поддержку функционала из pydocstyle — проверку докстрингов на соответствие конвенциям Питона.

Установка:

pip install flake8_docstrings

Список добавляемых этой библиотекой правил можно найти в документации pydocstyle.

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

Сама по себе эта библиотека никак не настраивается, однако добавленные правила можно внести в исключения, если какое-то из них неактуально для вашего проекта:

[flake8]  ignore = D101 # Игнорировать docstrings предупреждение “Missing docstring in public class”  

Страница библиотеки на Github тут.

flake8-builtins

Плагин, проверяющий код на использование встроенных имен в качестве переменных или параметров.

Установка:

pip install flake8-builtins

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

Как и в случае с flake8-docstrings, у плагина нет дополнительных настроек, но добавленные им правила можно, например, внести в исключения флейка:

[flake8]  ignore = B001 # Игнорировать builtins предупреждение “<some_builtin> is a python builtin and is being shadowed, consider renaming the variable”

Более подробную информацию об этом плагине можно найти на странице этого плагина на Github.

flake8-quotes

Плагин, позволяющий контролировать тип кавычек, которые будут использоваться в проекте.

Установка:

pip install flake8-quotes

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

[flake8]  inline-quotes = " # Указываем, какой тип кавычек должен использоваться в вашем проекте

Более подробную информацию об этом плагине можно найти на странице этого плагина на Github.

Послесловие

Хотя настройки, описанные выше, в 97,5 % случаев смогут предотвратить появление некачественного кода в репозитории, он так или иначе может оказаться запушенным (например, если деву было лень вводить две строчки для настройки pre-commit hook). Поэтому я настоятельно рекомендую добавить вызов Flake8 на этапе билда пул-реквестов в используемой вами системе continuous integration, чтобы предотвратить мердж невалидных пул-реквестов и попадание ошибок в мастер.

Надеюсь, эта статья была вам полезна и позволит в дальнейшем максимально гибко и качественно настраивать рабочий процесс и стайлгайды в ваших Python-проектах. Всех благ.

Список источников: