Автор: Анатолий Соловей, developer
Язык программирования 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-проектах. Всех благ.
Список источников:
Комментарии (12)
ternaus
29.12.2016 20:10+1Спасибо.
Субъективно, насколько Pyflakes8 лучше / удобнее, чем встроенный в Pycharm linter?
immaculate
30.12.2016 01:12Мне кажется, что в PyCharm PyFlakes не нужен. Встроенные средства лучше. Правда в последний раз смотрел на Pyflakes очень давно. Но даже не знаю, что надо сделать, чтобы превзойти последние версии PyCharm.
tolyandrov
30.12.2016 16:21Для меня тут всё очень просто: PyCharm linter очень удобный при написании кода, потому что сразу подсвечивает стилистические ошибки, но относительно Flake у него есть два недостатка:
1) Он не позволяет предотвращать коммиты ошибок (т.е. пока что интеграции встроенного линтера с системами контроля версий нет, насколько я знаю)
2) Он находится только в IDE на твоей локальной машине. Ты можешь писать великолепный код, настроив пайчармовский линтер, но недобросовестный или невнимательный коллега всё равно сможет наделать стилистических ошибок в проекте, потому что, например, не настроит все необходимые правила для пайчармовского линтера. Flake же можно вызывать во время билдов на CI, внутри Make файлов и скриптов, чтобы проверить, что с кодом всё в порядке вне контекста одной конкретной среды разработки.
На мой взгляд, одно другому не мешает, так что для максимального эфекта лучше использовать оба этих инструмента вместе.
alexhouse
30.12.2016 03:58Python хорошо проверяется на ошибки встроенным в PyCharm linter'om. Flake8 видится, как еще один проект проверки кода, по типу проектов JS linter. В большинстве случаев не хватает автокорректировки кода, как делает PostCSS с плагинами.
tolyandrov
30.12.2016 16:08PyCharm'овская подсветка синтаксиса, конечно, очень хороша, но если над проектом работает больше одного человека, то её будет недостаточно. Flake8 позволяет подготовить конкретный список стилистических правил, которые должны выполняться в коде всего проекта и, в отличии от PyCharm'овского линтера, он заставляет разработчиков выполнять эти правила с помощью способов, описанных в статье: пре-коммит хуки, прогон билдов в CI и т.д.
Такая добровольно-принудительная система может быть не всем по нраву, но некоторые заказчики, например, могут быть просто очень требовательны к оформлению кода и тогда флейк поможет избежать возни с ревью и кучей обновлений с фиксами неправильного стиля.
А по поводу автокорректировки кода, в комментариях ниже рассказали про yapf, который этим как раз и занимается. На хабре по нему пока материала нет совсем, но документация у этой тулзы очень обширная, думаю, вам будет интересно ознакомиться.
el777
30.12.2016 15:14+1Экспериментировал с тулзами.
В итоге пришел к такому решению:
— flake8 добавлен и для локальных тестов в `tox` — это помогает сразу видеть если написал какую-то ерунду в процессе работы еще до подготовки коммита
— в прекоммит-хук добавлены вызовы `flake` — спасает от отправки пропущенных помарок, если недочинил их на стадии подготовки
— туда же добавлен вызов yapf — очень полезная тулза, которая не просто проверяет, а чинит форматирование кода. Причем если она изменила форматирование, то она блокирует коммит. Можно открыть код и перечитать правки. С помощью `git diff --staged` сразу видно, что именно было отформатировано и добавить предлженное в индекс, чтобы заново закоммитить.
— в планах сюда же начать пользовать pylint — он сейчас прикручен, но из-за специфических срабатываний на «необьявленных» методах класса (т.е. почти все Django методы) пришлось временно отключить.
— так же добавлена на стадии CI до прогона тестов через `travis` — если все же закоммитил лажу
nikolay_karelin
30.12.2016 21:46+1Я бы еще предложил добавлять проверку с помощью mypy — на необязательную проверку типов. Даже если в новом коде нету аннотаций, она может проверять аргументы вызовов стандартной библиотеки, и улучшать качество кода.
justhabrauser
05.01.2017 21:21«О сколько нам открытий чудных готовит просвещенья дух!» (с)
Автору — респектище!
Убил (точнее уже не «убил» и «выделил», получается) сегодня целый день на причесывание своего Django-проекта.
Узнал о себе много [не]интересного.
lynxknight
1) Пишите невалидный код
2) Добавляете файл в индекс
3) Пытаетесь сделать коммит
4) Валидатор ругается
5) Доводите файл до валидного состояния, но НЕ добавляете его повторно в индекс
6) Теперь у вас есть возможность закоммитить невалидный код из п. 2
printercu
git commit -n
проще: ничего даже править не надо, сразу есть возможность. Чтобы такого не было, в любом случае надо линтер на ci сервере запускать. Ведь кто-то мог и не настроить хуки вовсе.tolyandrov
Только что зарепродьюсил эту ошибку с самой новой на данный момент версией (3.2.1). Вроде, в списке issues ничего подобного не проскакивало, так что, возможно они сами не знают об этой ошибке. Я зарепорчу им баг в ближайшем времени, спасибо за информацию!