D103 missing docstring in public function как исправить

For example:

from typing import overload

@overload
def func(a: int) -> str:
     ...
@overload
def func(a: str) -> str:
     ...
def func(a):
     """Foo bar documentation."""
     return str(a)

will result in

/home/raabf/Dokumente/overload_test.py:4 in public function `func`:
    D103: Missing docstring in public function
/home/raabf/Dokumente/overload_test.py:7 in public function `func`:
    D103: Missing docstring in public function

Which is wrong since only the last def should have a docstring.

Two things should be done:

First, prevent the error D103 for overload functions. We could make use of the --ignore-decorators='overload', but since it is universally valid, it should be a fixed part of it, and the user should not set it by himself.

Second, since @overload functions should never have docstrings, it would be useful if there is an additional error code in pydocstyle, if someone is setting a docstring for an @overload function.

Flake8 is given me the D103 Missing docstring in public function error for my unit tests. I would not consider my unit tests public functions. I get:

test/base_classes/test_component.py:7:1: D103 Missing docstring in public function
test/base_classes/test_component.py:13:1: D103 Missing docstring in public function
test/base_classes/test_component.py:25:1: D103 Missing docstring in public function
test/base_classes/test_component.py:35:1: D103 Missing docstring in public function
test/base_classes/test_component.py:41:1: D103 Missing docstring in public function

How do I disable this check for my tests?

asked Jul 6, 2022 at 20:18

Jorrick Sleijster's user avatar

0

You can add the following to your .flake8 file at the root of your repository to ignore all D103 errors for test code:

per-file-ignores =
     test/*: D103

answered Jul 6, 2022 at 20:18

Jorrick Sleijster's user avatar

Flake8 дает мне ошибку D103 Missing docstring in public function для моих модульных тестов. Я бы не стал рассматривать свои модульные тесты как публичные функции. Я получил:

test/base_classes/test_component.py:7:1: D103 Missing docstring in public function
test/base_classes/test_component.py:13:1: D103 Missing docstring in public function
test/base_classes/test_component.py:25:1: D103 Missing docstring in public function
test/base_classes/test_component.py:35:1: D103 Missing docstring in public function
test/base_classes/test_component.py:41:1: D103 Missing docstring in public function

Как отключить эту проверку для моих тестов?

1 ответ

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

per-file-ignores =
     test/*: D103


0

Jorrick Sleijster
6 Июл 2022 в 23:18

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

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

Проверять будем следующий код. В нем
есть целый ряд логических и стилистических
ошибок:

"""
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. Линтеры помогут вам обнаружить
проблемные места и стилевые несоответствия.
Использовать эти инструменты можно на
любой стадии процесса разработки; их
можно даже автоматизировать, чтобы код
с «пухом» не прошел слишком далеко.

Использование линтеров позволяет
избежать ненужных дискуссий о стиле в
ходе код-ревью. Некоторым людям морально
легче получить объективный фидбэк от
инструментов, а не от товарищей по
команде. Кроме того, некоторые ревьюеры
могут просто не хотеть «придираться»
к стилю проверяемого кода. Линтеры не
озабочены всеми этими политесами и
экономией времени: они жалуются на любое
несоответствие.

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

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

Рассмотрим популярные инструменты для анализа кода Python и подробно расскажем об их специфике и основных принципах работы.

Инструменты для анализа кода Python. Часть 1

Автор: Валерий Шагур, teacher assistance на курсе Программирование на Python

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

Командная работа над проектом еще больше повышает требования к качеству кода, поэтому важным условием продуктивной работы команды становится описание формальных требований к написанию кода. Это могут быть соглашения, принятые в языке программирования, на котором ведется разработка, или собственное (внутрикорпоративное) руководство по стилю. Выработанные требования к оформлению кода не исключают появления “разночтений” среди разработчиков и временных затрат на их обсуждение. Кроме этого, соблюдение выработанных требований ложится на плечи программистов в виде дополнительной нагрузки. Все это привело к появлению инструментов для проверки кода на наличие стилистических и логических ошибок. О таких инструментах для языка программирования Python мы и поговорим в этой статье.

Анализаторы и автоматическое форматирование кода

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

И первая, и вторая группы включают в себя как простые утилиты командной строки для решения узкоспециализированных задач (например, проверка docstring или сортировка импортов), так и богатые по возможностям библиотеки, объединяющие в себе более простые утилиты. Средства анализа кода из первой группы принято называть линтерами (linter). Название происходит от lint — статического анализатора для языка программирования Си и со временем ставшего нарицательным. Программы второй группы называют форматировщиками (formatter).

Даже при поверхностном сравнении этих групп видны особенности работы с ними. При применении линтеров программисту, во-первых, необходимо писать код с оглядкой, дабы позже не исправлять найденные ошибки. И во вторых, принимать решение по поводу обнаруженных ошибок — какие требуют исправления, а какие можно проигнорировать. Форматировщики, напротив, автоматизируют процесс исправления ошибок, оставляя программисту возможность осуществлять контроль.

Часть 1

  • pycodestyle
  • pydocstyle
  • pyflakes
  • pylint
  • vulture

Часть 2

  • flake8
  • prospector
  • pylama
  • autopep8
  • yapf
  • black

Соглашения принятые в статье и общие замечания

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

Версия Python: во всех примерах, приведенных в статье, будет использоваться третья версия языка программирования Python.

Установка всех программ в обзоре практически однотипна и сводится к использованию пакетного менеджера pip.

$ python3.6 -m pip install --upgrade <package_name>

Некоторые из библиотек имеют готовые бинарные пакеты в репозиториях дистрибутивов linux или возможность установки с использованием git. Тем не менее для большей определенности и возможности повторения примеров из статьи, установка будет производится с помощью pip.

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

Тестовый скрипт: для примеров использования программ мы создали простенький по содержанию файл example.py. Мы сознательно не стали делать его более разнообразным по наличию в нем ошибок. Во-первых, добавление листингов с выводом некоторых анализаторов в таком случае сильно “раздуло” бы статью. Во-вторых, у нас не было цели детально показать различия в “отлове” тех или иных ошибок для каждой из утилит.

Содержание файла example.py:

import os
import notexistmodule

def Function(num,num_two):
return num

class MyClass:
"""class MyClass """

def __init__(self,var):
self.var=var

def out(var):
print(var)


if __name__ == "__main__":
my_class = MyClass("var")
my_class.out("var")
notexistmodule.func(5)

В коде допущено несколько ошибок:

  • импорт неиспользуемого модуля os,
  • импорт не существующего модуля notexistmodule,
  • имя функции начинается с заглавной буквы,
  • лишние аргументы в определении функции,
  • отсутствие self первым аргументом в методе класса,
  • неверное форматирование.

Руководства по стилям: для тех, кто впервые сталкивается с темой оформления кода, в качестве знакомства предлагаем прочитать официальные руководства по стилю для языка Python PEP8 и PEP257. В качестве примера внутрикорпоративных соглашений можно рассмотреть Google Python Style Guide — https://github.com/google/styleguide/blob/gh-pages/pyguide.md

Pycodestyle

Pycodestyle — простая консольная утилита для анализа кода Python, а именно для проверки кода на соответствие PEP8. Один из старейших анализаторов кода, до 2016 года носил название pep8, но был переименован по просьбе создателя языка Python Гвидо ван Россума.

Запустим проверку на нашем коде:

$ python3 -m pycodestyle example.py 
example.py:4:1: E302 expected 2 blank lines, found 1
example.py:4:17: E231 missing whitespace after ','
example.py:7:1: E302 expected 2 blank lines, found 1
example.py:10:22: E231 missing whitespace after ','
example.py:11:17: E225 missing whitespace around operator

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

<имя файла>: <номер строки> :<положение символа>: <код и короткая расшифровка ошибки>

Возможности программы по проверке соглашений ограничены: нет проверок на правильность именования, проверка документации сводится к проверки длины docstring. Тем не менее функционал программы нельзя назвать “спартанским”, он позволяет настроить необходимый уровень проверок и получить различную информацию о результатах анализа. Запуск с ключом –statistics -qq выводит статистику по ошибкам:

$ python3 -m pycodestyle --statistics -qq example.py 
1 E225 missing whitespace around operator
2 E231 missing whitespace after ','
2 E302 expected 2 blank lines, found 1

Более наглядный вывод можно получить при использовании ключа –show-source. После каждого сообщения об ошибке будет выведена строка исходного кода, в которой содержится ошибка.

$ python3 -m pycodestyle --show-source example.py 
example.py:4:1: E302 expected 2 blank lines, found 1
def Function(num,num_two):
^
example.py:4:17: E231 missing whitespace after ','
def Function(num,num_two):
^
example.py:7:1: E302 expected 2 blank lines, found 1
class MyClass:
^
example.py:10:22: E231 missing whitespace after ','
def __init__(self,var):
^
example.py:11:17: E225 missing whitespace around operator
self.var=var
^

Если есть необходимость посмотреть, какие из соглашений PEP8 были нарушены, используйте ключ — show-pep8. Программа выведет список всех проверок с выдержками из PEP8 для случаев нарушений. При обработке файлов внутри директорий предусмотрена возможность фильтрации по шаблону. Pycodestyle позволяет сохранять настройки поиска в конфигурационных файлах как глобально, так и на уровне проекта.

Pydocstyle

Утилиту pydocstyle мы уже упоминали в статье Работа с документацией в Python: поиск информации и соглашения. Pydocstyle проверяет наличие docstring у модулей, классов, функций и их соответствие официальному соглашению PEP257.

$ python3 -m pydocstyle example.py
example.py:1 at module level:
D100: Missing docstring in public module
example.py:4 in public function `Function`:
D103: Missing docstring in public function
example.py:7 in public class `MyClass`:
D400: First line should end with a period (not 's')
example.py:7 in public class `MyClass`:
D210: No whitespaces allowed surrounding docstring text
example.py:10 in public method `__init__`:
D107: Missing docstring in __init__
example.py:13 in public method `out`:
D102: Missing docstring in public method

Как мы видим из листинга, программа указала нам на отсутствие документации в определениях функции, методов класса и ошибки оформления в docstring класса. Вывод можно сделать более информативным, если использовать ключи –explain и –source при вызове программы. Функционал pydocstyle практически идентичен описанному выше для pycodestyle, различия касаются лишь названий ключей.

Pyflakes

В отличие от уже рассмотренных инструментов для анализа кода Python pyflakes не делает проверок стиля. Цель этого анализатора кода — поиск логических и синтаксических ошибок. Разработчики pyflakes сделали упор на скорость работы программы, безопасность и простоту. Несмотря на то, что данная утилита не импортирует проверяемый файл, она прекрасно справляется c поиском синтаксических ошибок и делает это быстро. С другой стороны, такой подход сильно сужает область проверок.
Функциональность pyflakes — “нулевая”, все что он умеет делать — это выводить результаты анализа в консоль:

$ python3 -m pyflakes example.py 
example.py:1: 'os' imported but unused

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

Pylint

До сих пор мы рассматривали утилиты, которые проводили проверки на наличие либо стилистических, либо логических ошибок. Следующий в обзоре статический инструмент для анализа кода Python — Pylint, который совместил в себе обе возможности. Этот мощный, гибко настраиваемый инструмент для анализа кода Python отличается большим количеством проверок и разнообразием отчетов. Это один из самых “придирчивых” и “многословных” анализаторов кода. Анализ нашего тестового скрипта выдает весьма обширный отчет, состоящий из списка найденных в ходе анализа недочетов, статистических отчетов, представленных в виде таблиц, и общей оценки кода:

$ python3.6 -m pylint --reports=y text example.py
************* Module text
/home/ququshka77/.local/lib/python3.6/site-packages/pylint/reporters/text.py:79:22: W0212: Access to a protected member _splitstrip of a client class (protected-access)
************* Module example
example.py:4:16: C0326: Exactly one space required after comma
def Function(num,num_two):
                           ^ (bad-whitespace)
example.py:10:21: C0326: Exactly one space required after comma
    def __init__(self,var):
                             ^ (bad-whitespace)
example.py:11:16: C0326: Exactly one space required around assignment
        self.var=var
                    ^ (bad-whitespace)
example.py:1:0: C0111: Missing module docstring (missing-docstring)
example.py:2:0: E0401: Unable to import 'notexistmodule' (import-error)
example.py:4:0: C0103: Function name "Function" doesn't conform to snake_case naming style (invalid-name)
example.py:4:0: C0111: Missing function docstring (missing-docstring)
example.py:4:17: W0613: Unused argument 'num_two' (unused-argument)
example.py:13:4: C0111: Missing method docstring (missing-docstring)
example.py:13:4: E0213: Method should have "self" as first argument (no-self-argument)
example.py:7:0: R0903: Too few public methods (1/2) (too-few-public-methods)
example.py:18:4: C0103: Constant name "my_class" doesn't conform to UPPER_CASE naming style (invalid-name)
example.py:19:4: E1121: Too many positional arguments for method call (too-many-function-args)
example.py:1:0: W0611: Unused import os (unused-import)

Report
======
112 statements analysed.

Statistics by type
+----------+----------+---------------+-------------+-------------------+---------------+
|type     |number      |old number        |difference      |%documented       |%badname |
+======+======+========+========+===========+========+
|module   |2           |2                 |=               |50.00             |0.00            |
+-----------+----------+---------------+-------------+-------------------+---------------+
|class    |5           |5                 |=               |100.00            |0.00            |
+-----------+----------+---------------+-------------+-------------------+---------------+
|method   |11          |11                |=               |90.91             |0.00            |
+-----------+----------+---------------+-------------+-------------------+---------------+
|function |4           |4                 |=               |75.00             |25.00          |
+-----------+----------+---------------+-------------+-------------------+---------------+

External dependencies
::
    pylint 
      -interfaces (text)
      -reporters (text)
      | -ureports 
      |   -text_writer (text)
      -utils (text)


Raw metrics
+-------------+----------+-------+-----------+-------------+
|type        |number |%     |previous    |difference |
+=======+======+=====+=====+========+
|code        |128    |48.30 |128         |=               |
+-------------+----------+--------+-----------+------------+
|docstring   |84     |31.70 |84          |=               |
+-------------+----------+--------+-----------+------------+
|comment     |16     |6.04  |16          |=               |
+-------------+----------+--------+-----------+------------+
|empty       |37     |13.96 |37          |=               |
+-------------+----------+--------+-----------+------------+

Duplication
+-------------------------------+------+------------+-------------+
|                            |now      |previous      |difference |
+=================+=====+======+========+
|nb duplicated lines         |0        |0             |=              |
+-------------------------------+-------+------------+------------+
|percent duplicated lines    |0.000    |0.000         |=              |
+-------------------------------+-------+------------+------------+

Messages by category
+--------------+----------+-----------+-------------+
|type            |number |previous |difference |
+========+======+======+========+
|convention      |8       |8       |=               |
+--------------+----------+-----------+-------------+
|refactor        |1       |1       |=               |
+--------------+-----------+----------+-------------+
|warning         |3       |3       |=               |
+--------------+-----------+----------+-------------+
|error           |3       |3       |=               |
+--------------+-----------+----------+-------------+

% errors / warnings by module
+-----------+--------+-----------+----------+--------------+
|module   |error    |warning |refactor |convention   |
+======+=====+======+======+========+
|example  |100.00   |66.67   |100.00   |100.00       |
+-----------+---------+----------+-----------+-------------+
|text     |0.00     |33.33   |0.00     |0.00         |
+-----------+---------+----------+-----------+-------------+

Messages
+-----------------------------+----------------+
|message id                  |occurrences |
+=================+=========+
|missing-docstring           |3                 |
+-----------------------------+----------------+
|bad-whitespace              |3                 |
+------------------------------+---------------+
|invalid-name                |2                 |
+------------------------------+---------------+
|unused-import               |1                 |
+------------------------------+---------------+
|unused-argument             |1                 |
+------------------------------+---------------+
|too-many-function-args      |1                 | 
+------------------------------+---------------+
|too-few-public-methods      |1                 |
+------------------------------+---------------+
|protected-access            |1                 |
+------------------------------+---------------+
|no-self-argument            |1                 |
+------------------------------+---------------+
|import-error                |1                 |
+------------------------------+---------------+

------------------------------------------------------------------------------------------
Your code has been rated at 7.59/10 (previous run: 7.59/10, +0.00)

Программа имеет свою внутреннюю маркировку проблемных мест в коде:

[R]efactor — требуется рефакторинг,
[C]onvention — нарушено следование стилистике и соглашениям,
[W]arning — потенциальная ошибка,
[E]rror — ошибка,
[F]atal — ошибка, которая препятствует дальнейшей работе программы.

Для вывода подробного отчета мы использовали ключ командной строки –reports=y.
Более гибко настроить вывод команды позволяют разнообразные ключи командной строки. Настройки можно сохранять в файле настроек rcfile. Мы не будем приводить подробное описание ключей и настроек, для этого есть официальная документация — https://pylint.readthedocs.io/en/latest/index.html#, остановимся лишь на наиболее интересных, с нашей точки зрения, возможностях утилиты:

— Генерация файла настроек (–generate-rcfile). Позволяет не писать конфигурационный файл с нуля. В созданном rcfile содержатся все текущие настройки с подробными комментариями к ним, вам остается только отредактировать его под собственные требования.

— Отключение вывода в коде. При редактировании кода есть возможность вставить блокирующие вывод сообщений комментарии. Чтобы продемонстрировать это, в определение функции в файле примера example.py добавим строку:

# pylint: disable=unused-argument

и запустим pylint. Из результатов проверки “исчезло” сообщение:

example.py:4:17: W0613: Unused argument 'num_two' (unused-argument)

— Создание отчетов в формате json (–output-format=json). Полезно, если необходимо сохранение или дальнейшая обработка результатов работы линтера. Вы также можете создать собственный формат вывода данных.

— Параллельный запуск (-j 4). Запуск в нескольких параллельных потоках на многоядерных процессорах сокращает время проверки.

— Встроенная документация. Вызов программы с ключом –help-msg=<key> выведет справку по ключевому слову key. В качестве ключевого слова может быть код сообщения (например: E0401) или символическое имя сообщения (например: import-error). Ниже приведен листинг получения справки по ключу import-error:

$ python3.6 -m pylint --help-msg=import-error
:import-error (E0401): *Unable to import %s*
Used when pylint has been unable to import a module. This message belongs to
the imports checker.

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

— Плагины — отличная возможность изменять поведение pylint. Их применение может оказаться полезным в случаях, когда pylint неправильно обрабатывает код и есть “ложные” срабатывания, или когда требуется отличный от стандартного формат вывода результатов.

Vulture

Vulture — небольшая утилита для поиска “мертвого” кода в программах Python. Она использует модуль ast стандартной библиотеки и создает абстрактные синтаксические деревья для всех файлов исходного кода в проекте. Далее осуществляется поиск всех объектов, которые были определены, но не используются. Vulture полезно применять для очистки и нахождения ошибок в больших базовых кодах.

Продолжение следует

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

Еще статьи по Python

  • 26 полезных возможностей Python: букварь разработки от А до Z;
  • ТОП-15 трюков в Python 3, делающих код понятнее и быстрее;
  • Новый Python: 7 возможностей, которые вам понравятся;
  • Крупнейшая подборка Python-каналов на Youtube;
  • Изучение Python: ТОП-10 вопросов разной направленности.

Добавить комментарий