Анализ кода в 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
Linting highlights syntactical and stylistic problems in your Python source code, which often helps you identify and correct subtle programming errors or unconventional coding practices that can lead to errors. For example, linting detects use of an uninitialized or undefined variable, calls to undefined functions, missing parentheses, and even more subtle issues such as attempting to redefine built-in types or functions. Linting is thus distinct from Formatting because linting analyzes how the code runs and detects errors whereas formatting only restructures how code appears.
Note: Stylistic and syntactical code detection is enabled by the Language Server. To enable third-party linters for additional problem detection, you can enable them by using the Python: Select Linter command and selecting the appropriate linter.
Enable linting
To enable linters, open the Command Palette (⇧⌘P (Windows, Linux Ctrl+Shift+P)) and select the Python: Select Linter command. The Select Linter command adds "python.linting.<linter>Enabled": true to your settings, where <linter> is the name of the chosen linter. See Specific linters for details.
Enabling a linter prompts you to install the required packages in your selected environment for the chosen linter.

Note: If you’re using a global environment and VS Code is not running elevated, linter installation may fail. In that case, either run VS Code elevated, or manually run the Python package manager to install the linter at an elevated command prompt for the same environment: for example
sudo pip3 install pylint(macOS/Linux) orpip install pylint(Windows, at an elevated prompt).
Disable linting
You can easily toggle between enabling and disabling your linter. To switch, open the Command Palette (⇧⌘P (Windows, Linux Ctrl+Shift+P)) and select the Python: Enable/Disable Linting command.
This will populate a dropdown with the current linting state and options to Enable or Disable Python linting.

Run linting
To perform linting, open the Command Palette (⇧⌘P (Windows, Linux Ctrl+Shift+P)), filter on «linting», and select Python: Run Linting. Linting will run automatically when you save a file.
Issues are shown in the Problems panel and as wavy underlines in the code editor. Hovering over an underlined issue displays the details:

General linting settings
You can add any of the linting settings to your user settings.json file (opened with the File > Preferences > Settings command ⌘, (Windows, Linux Ctrl+,)). Refer to User and Workspace settings to find out more about working with settings in VS Code.
To change the linting behavior across all enabled linters, modify the following settings:
| Feature | Setting (python.linting.) |
Default value |
|---|---|---|
| Linting in general | enabled | true |
| Linting on file save | lintOnSave | true |
| Maximum number of linting messages | maxNumberOfProblems | 100 |
| Exclude file and folder patterns | ignorePatterns | [".vscode/*.py", "**/site-packages/**/*.py"] |
When enabling lintOnSave, you might also want to enable the generic files.autoSave option (see Save / Auto Save). The combination provides frequent linting feedback in your code as you type.
Specific linters
The following table provides a summary of available Python linters and their basic settings. For descriptions of individual settings, see the Linter settings reference.
| Linter | Package name for pip install command |
True/false enable setting (python.linting.) |
Arguments setting (python.linting.) |
Custom path setting (python.linting.) |
|---|---|---|---|---|
| Pylint | pylint | pylintEnabled | pylintArgs | pylintPath |
| Flake8 | flake8 | flake8Enabled | flake8Args | flake8Path |
| mypy | mypy | mypyEnabled | mypyArgs | mypyPath |
| pycodestyle (pep8) | pycodestyle | pycodestyleEnabled | pycodestyleArgs | pycodestylePath |
| prospector | prospector | prospectorEnabled | prospectorArgs | prospectorPath |
| pylama | pylama | pylamaEnabled | pylamaArgs | pylamaPath |
| bandit | bandit | banditEnabled | banditArgs | banditPath |
Note: If you don’t find your preferred linter in the table above, you can add support via an extension. The Python Extension Template makes it easy to integrate new Python tools into VS Code.
To select a different linter, use the Python: Select Linter command. You can also edit your settings manually to enable multiple linters. Note, that using the Select Linter command overwrites those edits.
Custom arguments are specified in the appropriate arguments setting for each linter. Each top-level element of an argument string that’s separated by a space on the command line must be a separate item in the args list. For example:
"python.linting.pylintArgs": ["--reports", "12", "--disable", "I0011"],
"python.linting.flake8Args": ["--ignore=E24,W504", "--verbose"]
"python.linting.pydocstyleArgs": ["--ignore=D400", "--ignore=D4"]
If a top-level element is a single value (delineated by quotation marks or braces), it still appears as a single item in the list even if the value itself contains spaces.
A custom path is generally unnecessary as the Python extension resolves the path to the linter based on the Python interpreter being used (see Environments). To use a different version of a linter, specify its path in the appropriate custom path setting. For example, if your selected interpreter is a virtual environment but you want to use a linter that’s installed in a global environment, then set the appropriate path setting to point to the global environment’s linter.
Note: The following sections provide additional details for the individual linters linked in the Specific linters table above. In general, custom rules must be specified in a separate file as required by the linter you’re using.
Pylint
Pylint messages fall into the categories in the following table with the indicated mapping to VS Code categories. You can change the setting to change the mapping.
| Pylint category | Description | VS Code category mapping | Applicable setting (python.linting.) |
|---|---|---|---|
| Convention (C) | Programming standard violation | Information (underline) | pylintCategorySeverity.convention |
| Refactor (R) | Bad code smell | Hint (light bulbs) | pylintCategorySeverity.refactor |
| Warning (W) | Python-specific problems | Warning | pylintCategorySeverity.warning |
| Error (E) | Likely code bugs | Error (underline) | pylintCategorySeverity.error |
| Fatal (F) | An error prevented further Pylint processing | Error | pylintCategorySeverity.fatal |
Command-line arguments and configuration files
You can easily generate an options file using different methods. See Pylint command-line arguments for general switches.
If you’re using command-line arguments:
Command-line arguments can be used to load Pylint plugins, such as the plugin for Django:
"python.linting.pylintArgs": ["--load-plugins", "pylint_django"]
If you’re using a pylintrc file:
Options can also be specified in a pylintrc or .pylintrc options file in the workspace folder, as described on Pylint command line arguments.
To control which Pylint messages are shown, add the following contents to an options file:
[MESSAGES CONTROL]
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time.
#enable=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
#disable=
If you’re using Pylint:
You can create an options file using Pylint itself, by running the command below.
# Using an *nix shell or cmd on Windows
pylint --generate-rcfile > .pylintrc
If you are running Pylint in PowerShell, you have to explicitly specify a UTF-8 output encoding. This file contains sections for all the Pylint options, along with documentation in the comments.
pylint --generate-rcfile | Out-File -Encoding utf8 .pylintrc
pydocstyle
Command-line arguments and configuration files
See pydocstyle Command Line Interface for general options. For example, to ignore error D400 (first line should end with a period), add the following line to your settings.json file:
"python.linting.pydocstyleArgs": ["--ignore=D400"]
A code prefix also instructs pydocstyle to ignore specific categories of errors. For example, to ignore all Docstring Content issues (D4XXX errors), add the following line to settings.json:
"python.linting.pydocstyleArgs": ["--ignore=D4"]
More details can be found in the pydocstyle documentation.
Options can also be read from a [pydocstyle] section of any of the following configuration files:
setup.cfgtox.ini.pydocstyle.pydocstyle.ini.pydocstylerc.pydocstylerc.ini
For more information, see Configuration Files.
Message category mapping
The Python extension maps all pydocstyle errors to the Convention (C) category.
pycodestyle (pep8)
Command-line arguments and configuration files
See pycodestyle example usage and output for general switches. For example, to ignore error E303 (too many blank lines), add the following line to your settings.json file:
"python.linting.pycodestyleArgs": ["--ignore=E303"]
pycodestyle options are read from the [pycodestyle] section of a tox.ini or setup.cfg file located in any parent folder of the path(s) being processed. For details, see pycodestyle configuration.
Message category mapping
The Python extension maps pycodestyle message categories to VS Code categories through the following settings. If desired, change the setting to change the mapping.
| pycodestyle category | Applicable setting (python.linting.) |
VS Code category mapping |
|---|---|---|
| W | pycodestyleCategorySeverity.W | Warning |
| E | pycodestyleCategorySeverity.E | Error |
Prospector
Command-line arguments and configuration files
See Prospector Command Line Usage for general options. For example, to set a strictness level of «very high,» add the following line to your settings.json file:
"python.linting.prospectorArgs": ["-s", "veryhigh"]
It’s common with Prospector to use profiles to configure how Prospector runs. By default, Prospector loads the profile from a .prospector.yaml file in the current folder.
Because Prospector calls other tools, such as Pylint, any configuration files for those tools override tool-specific settings in .prospector.yaml. For example, suppose you specify the following, in .prospector.yaml:
pylint:
disable:
- too-many-arguments
If you also have a .pylintrc file that enables the too-many-arguments warning, you continue to see the warning from Pylint within VS Code.
Message category mapping
The Python extension maps all Prospector errors and warnings to the Error (E) category.
Flake8
Command-line arguments and configuration files
See Invoking Flake8 for general switches. For example, to ignore error E303 (too many blank lines), use the following setting:
"python.linting.flake8Args": ["--ignore=E303"]
By default, Flake8 ignores E121, E123, E126, E226, E24, and E704.
Flake8 user options are read from the C:Users<username>.flake8 (Windows) or ~/.config/flake8 (macOS/Linux) file.
At the project level, options are read from the [flake8] section of a tox.ini, setup.cfg, or .flake8 file.
For details, see Flake8 configuration.
Message category mapping
The Python extension maps flake8 message categories to VS Code categories through the following settings. If desired, change the setting to change the mapping.
| Flake8 category | Applicable setting (python.linting.) |
VS Code category mapping |
|---|---|---|
| F | flake8CategorySeverity.F | Error |
| E | flake8CategorySeverity.E | Error |
| W | flake8CategorySeverity.W | Warning |
mypy
Message category mapping
The Python extension maps mypy message categories to VS Code categories through the following settings. If desired, change the setting to change the mapping.
| mypy category | Applicable setting (python.linting.) |
VS Code category mapping |
|---|---|---|
| error | mypyCategorySeverity.error | Error |
| note | mypyCategorySeverity.note | Information |
Troubleshooting linting
| Error message | Cause | Solution |
|---|---|---|
| … unable to import <module_name> | The Python extension is using the wrong version of Pylint. | Ensure that selected interpreter is a valid Python installation where Pylint is installed. Alternately, set the python.linting.pylintPath to an appropriate version of Pylint for the Python interpreter being used. |
| Linting with <linter> failed … | The path to the Python interpreter is incorrect. | Make sure you selected a valid interpreter path by running the Python: Select Interpreter command (see Environments). |
| The linter has not been installed in the current Python environment. | Open a command prompt, navigate to the location where your selecter interpreter is, and run pip install for the linter. |
|
| The path to the linter is incorrect. | Ensure that the appropriate python.linting.<linter>Path setting for the linter is correct. |
|
| Custom arguments are defined incorrectly. | Check the appropriate python.linting.<linter>Args settings, and that the value of the setting is a list of the argument elements that are separated by spaces. For example, "python.linting.pylintPath": "pylint --load-plugins pylint_django" is incorrect. The correct syntax is "python.linting.pylintArgs": ["--load-plugins", "pylint_django"]. |
Next steps
- Debugging — Learn to debug Python both locally and remotely.
- Testing — Configure test environments and discover, run, and debug tests.
- Basic Editing — Learn about the powerful VS Code editor.
- Code Navigation — Move quickly through your source code.
- Python Extension Template — Create an extension to integrate your favorite linter into VS Code.
4/30/2022
Хочешь знать больше о Python?
Подпишись на наш канал о Python в Telegram!
Подписаться
×

В первой части статьи, опубликованной на pythonist.ru (с которой мы уже знакомили вас ранее), разбиралось, почему качество кода имеет такое большое значение, какой код можно считать качественным и на какие стандарты можно ориентироваться.
Представляем вам вторую часть статьи, в которой подробнее рассматривается, на что способны различные линтеры и как выглядит результат их работы.
Для сравнения пропустим одинаковый код через несколько линтеров с дефолтными настройками.
Проверять будем следующий код. В нем есть целый ряд логических и стилистических ошибок:
"""
code_with_lint.py
Example Code with lots of lint!
"""
import io
from math import *
from time import time
some_global_var = 'GLOBAL VAR NAMES SHOULD BE IN ALL_CAPS_WITH_UNDERSCOES'
def multiply(x, y):
"""
This returns the result of a multiplation of the inputs
"""
some_global_var = 'this is actually a local variable...'
result = x* y
return result
if result == 777:
print("jackpot!")
def is_sum_lucky(x, y):
"""This returns a string describing whether or not the sum of input is lucky
This function first makes sure the inputs are valid and then calculates the
sum. Then, it will determine a message to return based on whether or not
that sum should be considered "lucky"
"""
if x != None:
if y is not None:
result = x+y;
if result == 7:
return 'a lucky number!'
else:
return( 'an unlucky number!')
return ('just a normal number')
class SomeClass:
def __init__(self, some_arg, some_other_arg, verbose = False):
self.some_other_arg = some_other_arg
self.some_arg = some_arg
list_comprehension = [((100/value)*pi) for value in some_arg if value != 0]
time = time()
from datetime import datetime
date_and_time = datetime.now()
return
В таблице ниже мы разместили список используемых линтеров и время, которое им понадобилось на анализ этого файла. Следует отметить, что все эти инструменты служат разным целям, поэтому сравнивать, возможно, не совсем правильно. PyFlakes, например, не выявляет стилистические ошибки, как это делает Pylint.
| ЛИНТЕР | КОМАНДА | ВРЕМЯ |
|---|---|---|
| Pylint | pylint code_with_lint.py | 1,16 с |
| PyFlakes | pyflakes code_with_lint.py | 0,15 с |
| pycodestyle | pycodestyle code_with_lint.py | 0,14 с |
| pydocstyle | pydocstyle code_with_lint.py | 0,21 с |
Теперь давайте посмотрим на результаты.
Pylint
Pylint это один из самых старых линтеров (работает с 2006 года), но при этом он хорошо поддерживается. Можно сказать, что этот инструмент проверен временем. Контрибьюторы уже давно пофиксили все основные баги, а главный функционал хорошо отшлифовали.
Самые распространенные жалобы на Pylint — медленная работа, излишняя многословность по умолчанию и необходимость долго копаться в настройках, чтобы сделать все по своему вкусу. Если отбросить скорость работы, все остальные пункты — палка о двух концах. Многословность объясняется скрупулезностью. Большое количество настроек позволяет подогнать под свои нужды очень многие вещи.
Итак, вот результат запуска Pylint для приведенного выше кода:
No config file found, using default configuration * Module code_with_lint W: 23, 0: Unnecessary semicolon (unnecessary-semicolon) C: 27, 0: Unnecessary parens after 'return' keyword (superfluous-parens) C: 27, 0: No space allowed after bracket return( 'an unlucky number!') ^ (bad-whitespace) C: 29, 0: Unnecessary parens after 'return' keyword (superfluous-parens) C: 33, 0: Exactly one space required after comma def init(self, some_arg, some_other_arg, verbose = False): ^ (bad-whitespace) C: 33, 0: No space allowed around keyword argument assignment def init(self, some_arg, some_other_arg, verbose = False): ^ (bad-whitespace) C: 34, 0: Exactly one space required around assignment self.some_other_arg = some_other_arg ^ (bad-whitespace) C: 35, 0: Exactly one space required around assignment self.some_arg = some_arg ^ (bad-whitespace) C: 40, 0: Final newline missing (missing-final-newline) W: 6, 0: Redefining built-in 'pow' (redefined-builtin) W: 6, 0: Wildcard import math (wildcard-import) C: 11, 0: Constant name "some_global_var" doesn't conform to UPPER_CASE naming style (invalid-name) C: 13, 0: Argument name "x" doesn't conform to snake_case naming style (invalid-name) C: 13, 0: Argument name "y" doesn't conform to snake_case naming style (invalid-name) C: 13, 0: Missing function docstring (missing-docstring) W: 14, 4: Redefining name 'some_global_var' from outer scope (line 11) (redefined-outer-name) W: 17, 4: Unreachable code (unreachable) W: 14, 4: Unused variable 'some_global_var' (unused-variable) … R: 24,12: Unnecessary "else" after "return" (no-else-return) R: 20, 0: Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements) C: 31, 0: Missing class docstring (missing-docstring) W: 37, 8: Redefining name 'time' from outer scope (line 9) (redefined-outer-name) E: 37,15: Using variable 'time' before assignment (used-before-assignment) W: 33,50: Unused argument 'verbose' (unused-argument) W: 36, 8: Unused variable 'list_comprehension' (unused-variable) W: 39, 8: Unused variable 'date_and_time' (unused-variable) R: 31, 0: Too few public methods (0/2) (too-few-public-methods) W: 5, 0: Unused import io (unused-import) W: 6, 0: Unused import acos from wildcard import (unused-wildcard-import) … W: 9, 0: Unused time imported from time (unused-import)
Имейте в виду, что похожие строки в тексте заменены многоточиями. Разобраться довольно сложно, но в этом коде много ошибок.
Обратите внимание, что Pylint добавляет к каждой проблемной области префикс R, C, W, E или F, что означает:
- [R]efactor — нужен рефакторинг, поскольку показатель «good practice» не на должном уровне.
- [C]onvention — нарушение соглашения о стандарте кода
- [W]arning — предупреждение о стилистических проблемах или минорных программных проблемах
- [E]rror — существенные проблемы в программе (скорее всего баг)
- [F]atal — ошибки, мешающие дальнейшей работе.
Приведенный список — из пользовательского руководства Pylint.
PyFlakes
Pyflakes «торжественно клянется никогда не жаловаться на стиль и очень усердно стараться не допускать ложнопозитивных срабатываний». То есть Pyflakes не сообщит вам о пропущенных docstrings или о том, что имена аргументов не соответствуют стилю нейминга. Он фокусируется на логических проблемах в коде и потенциальных ошибках.
Преимущество этого инструмента в скорости. PyFlakes обработал файл лишь за небольшую долю времени, которое потребовалось Pylint.
Вывод после запуска Pyflakes для приведенного выше кода:
code_with_lint.py:5: 'io' imported but unused code_with_lint.py:6: 'from math import *' used; unable to detect undefined names code_with_lint.py:14: local variable 'some_global_var' is assigned to but never used code_with_lint.py:36: 'pi' may be undefined, or defined from star imports: math code_with_lint.py:36: local variable 'list_comprehension' is assigned to but never used code_with_lint.py:37: local variable 'time' (defined in enclosing scope on line 9) referenced before assignment code_with_lint.py:37: local variable 'time' is assigned to but never used code_with_lint.py:39: local variable 'date_and_time' is assigned to but never used
Недостаток Pyflakes в том, что в результатах его работы немного труднее разобраться. Различные проблемы и ошибки никак не помечены и не упорядочены. Но будет ли это для вас проблемой, зависит от вашего использования этого инструмента.
pycodestyle (прежде — pep8)
Этот инструмент проверяет код на соответствие некоторым соглашениям из PEP 8. Нейминг не проверяется, так же как и docstrings. Ошибки и предупреждения, выдаваемые этим инструментом, можно посмотреть в таблице.
Результат использования pycodestyle для приведенного выше кода:
code_with_lint.py:13:1: E302 expected 2 blank lines, found 1
code_with_lint.py:15:15: E225 missing whitespace around operator
code_with_lint.py:20:1: E302 expected 2 blank lines, found 1
code_with_lint.py:21:10: E711 comparison to None should be 'if cond is not None:'
code_with_lint.py:23:25: E703 statement ends with a semicolon
code_with_lint.py:27:24: E201 whitespace after '('
code_with_lint.py:31:1: E302 expected 2 blank lines, found 1
code_with_lint.py:33:58: E251 unexpected spaces around keyword / parameter equals
code_with_lint.py:33:60: E251 unexpected spaces around keyword / parameter equals
code_with_lint.py:34:28: E221 multiple spaces before operator
code_with_lint.py:34:31: E222 multiple spaces after operator
code_with_lint.py:35:22: E221 multiple spaces before operator
code_with_lint.py:35:31: E222 multiple spaces after operator
code_with_lint.py:36:80: E501 line too long (83 > 79 characters)
code_with_lint.py:40:15: W292 no newline at end of file
Что здесь хорошо, это то, что ошибки имеют метки категорий. Вы можете игнорировать определенные ошибки, если соответствие какому-то конкретному соглашению вас не заботит.
pydocstyle (прежде — pep257)
Этот инструмент очень похож на предыдущий, pycodestyle, за исключением того, что проверяет код не на соответствие PEP 8, а на соответствие PEP 257.
Результат запуска для приведенного выше кода:
code_with_lint.py:1 at module level:
D200: One-line docstring should fit on one line with quotes (found 3)
code_with_lint.py:1 at module level:
D400: First line should end with a period (not '!')
code_with_lint.py:13 in public function `multiply`:
D103: Missing docstring in public function
code_with_lint.py:20 in public function `is_sum_lucky`:
D103: Missing docstring in public function
code_with_lint.py:31 in public class `SomeClass`:
D101: Missing docstring in public class
code_with_lint.py:33 in public method `__init__`:
D107: Missing docstring in __init__
Как и pycodestyle, pydocstyle помечает и разбивает по категориям найденные ошибки. Этот список не конфликтует ни с чем из pycodestyle, поскольку все ошибки имеют приставку D (означающую docstring). Список ошибок можно посмотреть здесь.
Код без ошибок
Если учесть предупреждения и исправить ошибки, найденные линтерами, вы получите примерно такой код:
"""Example Code with less lint."""
from math import pi
from time import time
from datetime import datetime
SOME_GLOBAL_VAR = 'GLOBAL VAR NAMES SHOULD BE IN ALL_CAPS_WITH_UNDERSCOES'
def multiply(first_value, second_value):
"""Return the result of a multiplation of the inputs."""
result = first_value * second_value
if result == 777:
print("jackpot!")
return result
def is_sum_lucky(first_value, second_value):
"""
Return a string describing whether or not the sum of input is lucky.
This function first makes sure the inputs are valid and then calculates the
sum. Then, it will determine a message to return based on whether or not
that sum should be considered "lucky".
"""
if first_value is not None and second_value is not None:
result = first_value + second_value
if result == 7:
message = 'a lucky number!'
else:
message = 'an unlucky number!'
else:
message = 'an unknown number! Could not calculate sum...'
return message
class SomeClass:
"""Is a class docstring."""
def __init__(self, some_arg, some_other_arg):
"""Initialize an instance of SomeClass."""
self.some_other_arg = some_other_arg
self.some_arg = some_arg
list_comprehension = [
((100/value)*pi)
for value in some_arg
if value != 0
]
current_time = time()
date_and_time = datetime.now()
print(f'created SomeClass instance at unix time: {current_time}')
print(f'datetime: {date_and_time}')
print(f'some calculated values: {list_comprehension}')
def some_public_method(self):
"""Is a method docstring."""
pass
def some_other_public_method(self):
"""Is a method docstring."""
pass
Согласно «мнению» приведенных выше линтеров, этот код больше не имеет «ворсинок». И хотя логика сама по себе бессмысленная, вы можете заметить, что, как минимум, этот код отличается единообразием.
В рассмотренном случае мы запускали линтеры на уже написанном коде. Но это не единственный способ проверки качества кода.

Когда можно проверять качество кода?
Вы можете проверять качество своего кода:
- по мере написания,
- перед отправкой,
- при запуске тестов.
Проверять код при помощи линтеров лучше почаще. Если в многочисленной команде или на большом проекте такие проверки не автоматизированы, там будет легко упустить из виду ухудшение качества кода. Оно происходит постепенно, конечно. Какая-нибудь плохо прописанная логика, какой-то код, формат которого не соответствует соседнему коду. Со временем все эти шероховатости накапливаются, и в конечном итоге у вас на руках может оказаться трудночитаемый, трудноисправляемый и гарантирующий головную боль при поддержке код с кучей багов.
Чтобы этого избежать, проверяйте качество кода почаще!
Проверка кода по мере его написания
Вы можете использовать линтеры по мере написания кода, но для этого может понадобиться дополнительно настроить вашу среду разработки. Чаще всего вам нужно будет найти подходящий плагин для вашей IDE или редактора. Но большинство IDE имеют и встроенные линтеры.
По ссылкам вы сможете найти полезную информацию по этой теме для разных редакторов:
- Sublime Text
- VS Code
- Atom
- Vim
- Emacs
Проверка кода перед его отправкой
Если вы используете Git, можно настроить Git hooks для запуска линтеров перед коммитом. Другие системы контроля версий имеют схожие методы для запуска скриптов в привязке к определенным действиям в системе. При помощи этих методов можно блокировать любой новый код, не соответствующий стандартам качества.
Это может показаться слишком радикальным подходом, но прогон каждого кусочка кода через линтеры — важный шаг на пути к обеспечению стабильно высокого качества. Автоматизация этих проверок — лучший способ избежать шероховатостей в коде.
При запуске тестов
Вы можете вставить линтеры в любую систему, которую используете для непрерывной интеграции. Линтеры при этом могут быть настроены таким образом, чтобы сборка в принципе не была возможна, если код не соответствует стандартам.
Опять же, это может показаться слишком радикальным решением, особенно если в уже существующем коде есть много ошибок, вылавливаемых линтерами. Но эту проблему можно обойти. В некоторых системах непрерывной интеграции можно выбрать опцию, при которой сборка проваливается только если новый код увеличивает число ошибок, найденных линтером. Таким образом вы сможете улучшать качество кода, не переписывая заново всю кодовую базу.
Заключение
Высококачественный код делает то, что он должен делать, не ломаясь при этом. Его легко читать, поддерживать и расширять. Он функционирует без проблем, в нем нет дефектов, а еще он написан так, чтобы следующему программисту было легко с ни м работать.
Естественно, каждый хочет, чтобы его код отличался высоким качеством. К счастью, есть методы и инструменты, позволяющие повысить качество кода.
Благодаря руководствам по стилю ваш код может стать единообразным. PEP8 — отличная отправная точка, если речь идет о Python. Линтеры помогут вам обнаружить проблемные места и стилевые несоответствия. Использовать эти инструменты можно на любой стадии процесса разработки; их можно даже автоматизировать, чтобы код с «пухом» не прошел слишком далеко.
Использование линтеров позволяет избежать ненужных дискуссий о стиле в ходе код-ревью. Некоторым людям морально легче получить объективный фидбэк от инструментов, а не от товарищей по команде. Кроме того, некоторые ревьюеры могут просто не хотеть «придираться» к стилю проверяемого кода. Линтеры не озабочены всеми этими политесами и экономией времени: они жалуются на любое несоответствие.
Кроме того, все линтеры, упомянутые в этой статье, имеют различные опции для ввода в командной строке и настройки, позволяющие подогнать инструмент под свои нужды. Важно понимать, что вы можете быть настолько строги или снисходительны, насколько захотите.
Улучшение качества кода это процесс. Вы можете предпринимать некоторые шаги в этом направлении и не выбрасывая весь несоответствующий стандарту код. Осведомленность — прекрасный первый шаг. Чтобы его сделать, нужно только осознать, насколько важно поддерживать высокое качество кода.


В первой части статьи мы разобрались, почему качество кода имеет такое большое значение, какой код можно считать качественным и на какие стандарты можно ориентироваться.
Теперь давайте подробнее рассмотрим,
на что способны различные линтеры и как
выглядит результат их работы. Для этого
пропустим одинаковый код через несколько
линтеров с дефолтными настройками.
Проверять будем следующий код. В нем
есть целый ряд логических и стилистических
ошибок:
"""
code_with_lint.py
Example Code with lots of lint!
"""
import io
from math import *
from time import time
some_global_var = 'GLOBAL VAR NAMES SHOULD BE IN ALL_CAPS_WITH_UNDERSCOES'
def multiply(x, y):
"""
This returns the result of a multiplation of the inputs
"""
some_global_var = 'this is actually a local variable...'
result = x* y
return result
if result == 777:
print("jackpot!")
def is_sum_lucky(x, y):
"""This returns a string describing whether or not the sum of input is lucky
This function first makes sure the inputs are valid and then calculates the
sum. Then, it will determine a message to return based on whether or not
that sum should be considered "lucky"
"""
if x != None:
if y is not None:
result = x+y;
if result == 7:
return 'a lucky number!'
else:
return( 'an unlucky number!')
return ('just a normal number')
class SomeClass:
def __init__(self, some_arg, some_other_arg, verbose = False):
self.some_other_arg = some_other_arg
self.some_arg = some_arg
list_comprehension = [((100/value)*pi) for value in some_arg if value != 0]
time = time()
from datetime import datetime
date_and_time = datetime.now()
return
В таблице ниже мы разместили список
используемых линтеров и время, которое
им понадобилось на анализ этого файла.
Следует отметить, что все эти инструменты
служат разным целям, поэтому сравнивать,
возможно, не совсем правильно. PyFlakes,
например, не выявляет стилистические
ошибки, как это делает Pylint.
| Линтер | Команда | Время |
|---|---|---|
| Pylint | pylint code_with_lint.py | 1,16 с |
| PyFlakes | pyflakes code_with_lint.py | 0,15 с |
| pycodestyle | pycodestyle code_with_lint.py | 0,14 с |
| pydocstyle | pydocstyle code_with_lint.py | 0,21 с |
Теперь давайте посмотрим на результаты.
Pylint
Pylint это один из самых старых линтеров
(работает с 2006 года), но при этом он хорошо
поддерживается. Можно сказать, что этот
инструмент проверен временем. Контрибьюторы
уже давно пофиксили все основные баги,
а главный функционал хорошо отшлифовали.
Самые распространенные жалобы на
Pylint — медленная работа, излишняя
многословность по умолчанию и необходимость
долго копаться в настройках, чтобы
сделать все по своему вкусу. Если
отбросить скорость работы, все остальные
пункты — палка о двух концах. Многословность
объясняется скрупулезностью. Большое
количество настроек позволяет подогнать
под свои нужды очень многие вещи.
Итак, вот результат запуска Pylint для
приведенного выше кода:
No config file found, using default configuration
************* Module code_with_lint
W: 23, 0: Unnecessary semicolon (unnecessary-semicolon)
C: 27, 0: Unnecessary parens after 'return' keyword (superfluous-parens)
C: 27, 0: No space allowed after bracket
return( 'an unlucky number!')
^ (bad-whitespace)
C: 29, 0: Unnecessary parens after 'return' keyword (superfluous-parens)
C: 33, 0: Exactly one space required after comma
def __init__(self, some_arg, some_other_arg, verbose = False):
^ (bad-whitespace)
C: 33, 0: No space allowed around keyword argument assignment
def __init__(self, some_arg, some_other_arg, verbose = False):
^ (bad-whitespace)
C: 34, 0: Exactly one space required around assignment
self.some_other_arg = some_other_arg
^ (bad-whitespace)
C: 35, 0: Exactly one space required around assignment
self.some_arg = some_arg
^ (bad-whitespace)
C: 40, 0: Final newline missing (missing-final-newline)
W: 6, 0: Redefining built-in 'pow' (redefined-builtin)
W: 6, 0: Wildcard import math (wildcard-import)
C: 11, 0: Constant name "some_global_var" doesn't conform to UPPER_CASE naming style (invalid-name)
C: 13, 0: Argument name "x" doesn't conform to snake_case naming style (invalid-name)
C: 13, 0: Argument name "y" doesn't conform to snake_case naming style (invalid-name)
C: 13, 0: Missing function docstring (missing-docstring)
W: 14, 4: Redefining name 'some_global_var' from outer scope (line 11) (redefined-outer-name)
W: 17, 4: Unreachable code (unreachable)
W: 14, 4: Unused variable 'some_global_var' (unused-variable)
…
R: 24,12: Unnecessary "else" after "return" (no-else-return)
R: 20, 0: Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements)
C: 31, 0: Missing class docstring (missing-docstring)
W: 37, 8: Redefining name 'time' from outer scope (line 9) (redefined-outer-name)
E: 37,15: Using variable 'time' before assignment (used-before-assignment)
W: 33,50: Unused argument 'verbose' (unused-argument)
W: 36, 8: Unused variable 'list_comprehension' (unused-variable)
W: 39, 8: Unused variable 'date_and_time' (unused-variable)
R: 31, 0: Too few public methods (0/2) (too-few-public-methods)
W: 5, 0: Unused import io (unused-import)
W: 6, 0: Unused import acos from wildcard import (unused-wildcard-import)
…
W: 9, 0: Unused time imported from time (unused-import)
Имейте в виду, что похожие строки в
тексте заменены многоточиями. Разобраться
довольно сложно, но в этом коде много
ошибок.
Обратите внимание, что Pylint добавляет
к каждой проблемной области префикс R,
C, W, E или F, что означает:
- [R]efactor — нужен рефакторинг, поскольку
показатель «good practice» не на должном
уровне. - [C]onvention — нарушение соглашения о
стандарте кода - [W]arning — предупреждение о стилистических
проблемах или минорных программных
проблемах - [E]rror — существенные проблемы в
программе (скорее всего баг) - [F]atal — ошибки, мешающие дальнейшей
работе.
Приведенный список — из пользовательского
руководства Pylint.
PyFlakes
Pyflakes «торжественно клянется никогда
не жаловаться на стиль и очень усердно
стараться не допускать ложнопозитивных
срабатываний». То есть Pyflakes не сообщит
вам о пропущенных docstrings или о том, что
имена аргументов не соответствуют стилю
нейминга. Он фокусируется на логических
проблемах в коде и потенциальных ошибках.
Преимущество этого инструмента в
скорости. PyFlakes обработал файл лишь за
небольшую долю времени, которое
потребовалось Pylint.
Вывод после запуска Pyflakes для приведенного
выше кода:
code_with_lint.py:5: 'io' imported but unused code_with_lint.py:6: 'from math import *' used; unable to detect undefined names code_with_lint.py:14: local variable 'some_global_var' is assigned to but never used code_with_lint.py:36: 'pi' may be undefined, or defined from star imports: math code_with_lint.py:36: local variable 'list_comprehension' is assigned to but never used code_with_lint.py:37: local variable 'time' (defined in enclosing scope on line 9) referenced before assignment code_with_lint.py:37: local variable 'time' is assigned to but never used code_with_lint.py:39: local variable 'date_and_time' is assigned to but never used
Недостаток Pyflakes в том, что в результатах
его работы немного труднее разобраться.
Различные проблемы и ошибки никак не
помечены и не упорядочены. Но будет ли
это для вас проблемой, зависит от вашего
использования этого инструмента.
pycodestyle (прежде — pep8)
Этот инструмент проверяет код на соответствие некоторым соглашениям из PEP 8. Нейминг не проверяется, так же как и docstrings. Ошибки и предупреждения, выдаваемые этим инструментом, можно посмотреть в таблице.
Результат использования pycodestyle для
приведенного выше кода:
code_with_lint.py:13:1: E302 expected 2 blank lines, found 1
code_with_lint.py:15:15: E225 missing whitespace around operator
code_with_lint.py:20:1: E302 expected 2 blank lines, found 1
code_with_lint.py:21:10: E711 comparison to None should be 'if cond is not None:'
code_with_lint.py:23:25: E703 statement ends with a semicolon
code_with_lint.py:27:24: E201 whitespace after '('
code_with_lint.py:31:1: E302 expected 2 blank lines, found 1
code_with_lint.py:33:58: E251 unexpected spaces around keyword / parameter equals
code_with_lint.py:33:60: E251 unexpected spaces around keyword / parameter equals
code_with_lint.py:34:28: E221 multiple spaces before operator
code_with_lint.py:34:31: E222 multiple spaces after operator
code_with_lint.py:35:22: E221 multiple spaces before operator
code_with_lint.py:35:31: E222 multiple spaces after operator
code_with_lint.py:36:80: E501 line too long (83 > 79 characters)
code_with_lint.py:40:15: W292 no newline at end of file
Что здесь хорошо, это то, что ошибки
имеют метки категорий. Вы можете
игнорировать определенные ошибки, если
соответствие какому-то конкретному
соглашению вас не заботит.
pydocstyle (прежде — pep257)
Этот инструмент очень похож на предыдущий, pycodestyle, за исключением того, что проверяет код не на соответствие PEP 8, а на соответствие PEP 257.
Результат запуска для приведенного
выше кода:
code_with_lint.py:1 at module level:
D200: One-line docstring should fit on one line with quotes (found 3)
code_with_lint.py:1 at module level:
D400: First line should end with a period (not '!')
code_with_lint.py:13 in public function `multiply`:
D103: Missing docstring in public function
code_with_lint.py:20 in public function `is_sum_lucky`:
D103: Missing docstring in public function
code_with_lint.py:31 in public class `SomeClass`:
D101: Missing docstring in public class
code_with_lint.py:33 in public method `__init__`:
D107: Missing docstring in __init__
Как и pycodestyle, pydocstyle помечает и разбивает
по категориям найденные ошибки. Этот
список не конфликтует ни с чем из
pycodestyle, поскольку все ошибки имеют
приставку D (означающую docstring). Список
ошибок можно посмотреть здесь.
Код без ошибок
Если учесть предупреждения и исправить
ошибки, найденные линтерами, вы получите
примерно такой код:
"""Example Code with less lint."""
from math import pi
from time import time
from datetime import datetime
SOME_GLOBAL_VAR = 'GLOBAL VAR NAMES SHOULD BE IN ALL_CAPS_WITH_UNDERSCOES'
def multiply(first_value, second_value):
"""Return the result of a multiplation of the inputs."""
result = first_value * second_value
if result == 777:
print("jackpot!")
return result
def is_sum_lucky(first_value, second_value):
"""
Return a string describing whether or not the sum of input is lucky.
This function first makes sure the inputs are valid and then calculates the
sum. Then, it will determine a message to return based on whether or not
that sum should be considered "lucky".
"""
if first_value is not None and second_value is not None:
result = first_value + second_value
if result == 7:
message = 'a lucky number!'
else:
message = 'an unlucky number!'
else:
message = 'an unknown number! Could not calculate sum...'
return message
class SomeClass:
"""Is a class docstring."""
def __init__(self, some_arg, some_other_arg):
"""Initialize an instance of SomeClass."""
self.some_other_arg = some_other_arg
self.some_arg = some_arg
list_comprehension = [
((100/value)*pi)
for value in some_arg
if value != 0
]
current_time = time()
date_and_time = datetime.now()
print(f'created SomeClass instance at unix time: {current_time}')
print(f'datetime: {date_and_time}')
print(f'some calculated values: {list_comprehension}')
def some_public_method(self):
"""Is a method docstring."""
pass
def some_other_public_method(self):
"""Is a method docstring."""
pass
Согласно «мнению» приведенных выше
линтеров, этот код больше не имеет
«ворсинок». И хотя логика сама по себе
бессмысленная, вы можете заметить, что,
как минимум, этот код отличается
единообразием.
В рассмотренном случае мы запускали
линтеры на уже написанном коде. Но это
не единственный способ проверки качества
кода.

Когда можно проверять качество
кода?
Вы можете проверять качество своего
кода:
- по мере написания,
- перед отправкой,
- при запуске тестов.
Проверять код при помощи линтеров
лучше почаще. Если в многочисленной
команде или на большом проекте такие
проверки не автоматизированы, там будет
легко упустить из виду ухудшение качества
кода. Оно происходит постепенно, конечно.
Какая-нибудь плохо прописанная логика,
какой-то код, формат которого не
соответствует соседнему коду. Со временем
все эти шероховатости накапливаются,
и в конечном итоге у вас на руках может
оказаться трудночитаемый, трудноисправляемый
и гарантирующий головную боль при
поддержке код с кучей багов.
Чтобы этого избежать, проверяйте
качество кода почаще!
Проверка кода по мере его
написания
Вы можете использовать линтеры по
мере написания кода, но для этого может
понадобиться дополнительно настроить
вашу среду разработки. Чаще всего вам
нужно будет найти подходящий плагин
для вашей IDE или редактора. Но большинство
IDE имеют и встроенные линтеры.
По ссылкам вы сможете найти полезную
информацию по этой теме для разных
редакторов:
- Sublime Text
- VS Code
- Atom
- Vim
- Emacs
Проверка кода перед его
отправкой
Если вы используете Git, можно настроить
Git hooks для запуска линтеров перед коммитом.
Другие системы контроля версий имеют
схожие методы для запуска скриптов в
привязке к определенным действиям в
системе. При помощи этих методов можно
блокировать любой новый код, не
соответствующий стандартам качества.
Это может показаться слишком радикальным
подходом, но прогон каждого кусочка
кода через линтеры — важный шаг на пути
к обеспечению стабильно высокого
качества. Автоматизация этих проверок
— лучший способ избежать шероховатостей
в коде.
При запуске тестов
Вы можете вставить линтеры в любую систему, которую используете для непрерывной интеграции. Линтеры при этом могут быть настроены таким образом, чтобы сборка в принципе не была возможна, если код не соответствует стандартам.
Опять же, это может показаться слишком
радикальным решением, особенно если в
уже существующем коде есть много ошибок,
вылавливаемых линтерами. Но эту проблему
можно обойти. В некоторых системах
непрерывной интеграции можно выбрать
опцию, при которой сборка проваливается
только если новый код увеличивает число
ошибок, найденных линтером. Таким образом
вы сможете улучшать качество кода, не
переписывая заново всю кодовую базу.
Заключение
Высококачественный код делает то, что
он должен делать, не ломаясь при этом.
Его легко читать, поддерживать и
расширять. Он функционирует без проблем,
в нем нет дефектов, а еще он написан так,
чтобы следующему программисту было
легко с ни м работать.
Естественно, каждый хочет, чтобы его
код отличался высоким качеством. К
счастью, есть методы и инструменты,
позволяющие повысить качество кода.
Благодаря руководствам по стилю ваш
код может стать единообразным. PEP8 —
отличная отправная точка, если речь
идет о Python. Линтеры помогут вам обнаружить
проблемные места и стилевые несоответствия.
Использовать эти инструменты можно на
любой стадии процесса разработки; их
можно даже автоматизировать, чтобы код
с «пухом» не прошел слишком далеко.
Использование линтеров позволяет
избежать ненужных дискуссий о стиле в
ходе код-ревью. Некоторым людям морально
легче получить объективный фидбэк от
инструментов, а не от товарищей по
команде. Кроме того, некоторые ревьюеры
могут просто не хотеть «придираться»
к стилю проверяемого кода. Линтеры не
озабочены всеми этими политесами и
экономией времени: они жалуются на любое
несоответствие.
Кроме того, все линтеры, упомянутые в
этой статье, имеют различные опции для
ввода в командной строке и настройки,
позволяющие подогнать инструмент под
свои нужды. Важно понимать, что вы можете
быть настолько строги или снисходительны,
насколько захотите.
Улучшение качества кода это процесс.
Вы можете предпринимать некоторые шаги
в этом направлении и не выбрасывая весь
несоответствующий стандарту код.
Осведомленность — прекрасный первый
шаг. Чтобы его сделать, нужно только
осознать, насколько важно поддерживать
высокое качество кода.
In this post , we will explore – How to Check Syntax Errors in Python Code.
You can use various options to check for any syntax errors in Python without executing the script .
Try the below options –
if( aicp_can_see_ads() ) {
}
Option 1 – Using Compilation Script :
- Using Python basic compilation . Use the code below and save it as any .py file e.g pythonSyntaxChecker.py
import sys your_python_script_name = sys.argv[1] source = open(your_python_script_name, 'r').read() + 'n' compile(source, your_python_script_name, 'exec')
- Next run your Python script(say yourPythonScript.py) against the above script
python3 pythonSyntaxChecker.py yourPythonScript.py OR python pythonSyntaxChecker.py yourPythonScript.py
Option 2 – Using Direct Compilation :
- You can directly compile your Python script
- Note the below poinst –
- python returns a non-zero exit code if the compilation fails.
- Also type errors are not detected — those are only detected at runtime
python -m py_compile yourPythonScript.py
Option 3 – Using Pychecker :
- You can use PyChecker to syntax check your python code. Use code below from the command line:
pychecker [options] YOUR_PYTHON_SCRIPT.py
- The [options] provides below features –
- –only –> only warn about files passed on the command line
- -#, –limit –> the maximum number of warnings to be displayed
- –no-shadowbuiltin –> check if a variable shadows a builtin
- -q, –stdlib –> ignore warnings from files under standard library
- -T, –argsused –> unused method/function arguments
- If you want to syntax check multiple Python scripts at one go ,
if( aicp_can_see_ads() ) {
}
pychecker <YOUR_DIR>/*.py
Sample how pychecker shows the syntax errors –
script1.py:5: Imported module (string) not used script1.py:6: Instantiating an object with arguments, but no constructor script1.py:7: Object (useless) has no attribute (valeu) script1.py:8: No local variable (var1) script1.py:9: self is not first method argument
Option 4 – Using Pyflakes :
- Another worthy option to try – https://github.com/PyCQA/pyflakes
pyflakes yourPythonScript.py
Option 5 – Using “ast” module
- The ast module helps to process trees of the Python abstract syntax grammar – basically ast helps to find out the current grammar appearance.
- More here – https://docs.python.org/3/library/ast.html
- Using CLI
python -c "import ast; ast.parse(open('yourPythonScript.py').read())"
- Check syntax of any block of code
import ast
tree = ast.parse("print ('This is Great')")
ast.dump(tree)
"Module(body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Str(s='This is Great')], keywords=[]))])"
- Alternatively use as a python script
import ast, traceback g = '<YOUR PYTHON SCRIPT NAME>' with open(g) as f: source = f.read() valid = True try: ast.parse(source) except SyntaxError: valid = False traceback.print_exc() finally: print(valid) ast.dump(source)
if( aicp_can_see_ads() ) {
}
Hope this write up was helpful.
Other Interesting Reads –
-
How To Create A Kerberos Keytab File ?
-
How To Code a PySpark Cassandra Application ?
-
How To Setup Spark Scala SBT in Eclipse
-
What Are The Most Important Metrics to Monitor in Kafka ?
python syntax check, python syntax, invalid syntax python, python for loop syntax, python if syntax, python, pycharm, django, matplotlib, python online, python programming, python list, learn python, python syntax checker,How do I check Python syntax, How do I check Python syntax online, What is Python basic syntax, What Is syntax check,check python script online,python code tester,how to find error in python code ,pep8 checker,python 3.5 syntax checker,python syntax error,python check function,bug checker python, python syntax checker command line, python 3.5 syntax checker, python syntax error fixer,syntaxerror: invalid syntax python 3,invalid syntax python print,how to resolve name error in python,invalid syntax python def,pylint syntax error,how to find error in python code,file <stdin>”, line 1 syntaxerror: invalid syntax,online python compiler, python find syntax error,How to find syntax error in python,How to solve the python syntax error,What is a syntax error in python,How does Python handle syntax errors,python syntax error,how to find error in python code,python compiler invalid syntax python ,check python script online,python code tester,occurs when a syntax error is raised for a module
if( aicp_can_see_ads() ) {
}