Время прочтения: 7 мин.

Библиотека Pylint позволяет искать ошибки в коде и сделать его стилистически согласованным со стандартами Python. Надеюсь, что пост обогатит и ваш опыт работы с анализаторами кода.

PEP-8 является обобщённым стандартом для кода Python. Если код не форматировать по рекомендациям PEP-8, то читабельность кода резко понижается, следовательно, работа над проектом усложняется.

Для помощи разработчикам существует множество автоматизированных инструментов для оптимизации и улучшении кода. Для выявления недочётов в нашем проекте я использовала библиотекy Python – Pylint.

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

  • import os
  • import shutil
  • import time
  • import pandas as pd
  • import pylint

Шаг первый – подготовительный

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

os.mkdir(‘./home/files/py_script’)

Создаю класс и инициализирую заранее в нём все необходимые для работы переменные:

class OptimizationCode:
    def __init__(self):
        self.save_path = '/home/'
        self.path_main_dir = '/home/files/py_script'
        self.path = '/home/py_project'
        self.col_error = 'error'
        self.col_filename = 'filename'
        self.col_rating = 'rating'
        self.col_count = 'count'

Для последнего этапа подготовки к работе — копирую все «.py» файлы в созданную ранее директорию. Для этого добавляю классу соответствующий метод:

def move_py_files(self):
    nom = 0
    for root, dirs, files in os.walk(self.path, topdown=False):
        for file in files:
            path_file = os.path.join(root, file)
            if path_file.endswith('.py'):
                shutil.copy(path_file, self.path_main_dir)
                time.sleep(3)
                new_file = str(nom) + '_' + file
                shutil.copy(path_file, self.path_main_dir)
                nom = nom + 1
                shutil.move(self.path_main_dir + '/' + file, self.path_main_dir + '/' + new_file)

Итак, директория сформирована. Всего собралось 111 «.py» файлов.

Шаг второй – формирование DataFrame

Теперь нужно сформировать единый DataFrame, где собраны все найденные недочёты. Для этого необходима библиотека Pylint. Добавляю классу соответствующий метод:

def forming_df_file(self):
    ress = []
    dirs = os.listdir(self.path_main_dir)
    for file_dirs in dirs:
        command = "pylint " + "'" + str(self.path_main_dir) + '/' + str(file_dirs) + "'"
        pipe = os.popen(command)
        text_cmd = pipe.read()
        for sp in range(0, len(text_cmd.split('\n'))):
            res = []
            str_plt = text_cmd.split('\n')[sp]
            res.append(str(file_dirs))
            res.append(str_plt)
            ress.append(res)
    df = pd.DataFrame(ress)
    df.columns = [self.col_filename, self.col_error]
    df_error = df[self.col_error].str.split(': ', expand=True)
    df_err = pd.concat([df, df_error], axis=1)
    return df_err

Первый столбец – наименование файла, второй – обобщённая общая ошибка, которую разделила по разделителю «:» на три столбца: полный путь до файла; код ошибки; текст ошибки.

Шаг третий – подготовка к анализу данных

1) Общая оценка кода

Pylint – один из самых придирчивых автоматизированных инструментов для анализа кода, который выдаёт обширный отчёт, состоящий из списка найденных недочётов, представленных в виде таблиц и общей оценки кода.

Выделяю общую оценку кода в отдельный столбец.

Максимальная оценка – 10 баллов, следовательно, чем меньше, тем хуже для pylint воспринимается код.

2) Общее количество ошибок

Необходимо сделать подсчёт, сколько в каждом файле найдено с помощью pylint недочётов и присоединить к ним их рейтинг.

Файлы __init__ исключаю, поскольку значимости не имеют (по умолчанию пустые файлы).

Собираю метод в класс:

def get_count_and_rating(self, df_err): 
    df_count_err = df_err.copy()
    df_count_err.dropna(subset=[1], inplace=True)
    df_count_err = df_count_err[df_count_err[1].str.match('(\w{1}\d{4})')].reset_index()
    df_count_err = pd.DataFrame(df_count_err.groupby([self.col_filename]).count()[1]).reset_index()

    df_error_rate = df_err[df_err[self.col_error].str.contains('rated') == True]
    df_error_rate = df_error_rate[[self.col_filename, self.col_error]]
    df_error_rate[self.col_rating] = ''
    df_error_rate[self.col_rating] = df_error_rate[self.col_error].str.extract(r'at ((.*?))/')
    df_error_rate[self.col_rating] = df_error_rate[self.col_rating].astype(float)

    df_error_rate = df_error_rate[[self.col_filename, self.col_rating]]
    df_corr_all = pd.merge(df_error_rate, df_count_err,
                           left_on=[self.col_filename], right_on=[self.col_filename], how='left')
    df_corr_all.columns = [self.col_filename, self.col_rating, self.col_count]
    df_corr_all[self.col_rating] = df_corr_all[self.col_rating].astype(float)
    df_corr_all[self.col_count] = df_corr_all[self.col_count].astype(int)
    df_corr_all_sort = df_corr_all.sort_values(by=self.col_count)
    return df_corr_all_sort

Шаг четвертый – анализ данных

Всего с помощью pylint было найдено 1859 недочётов по 111 файлам. Группировку по тематикам найденных недочётов можно увидеть ниже.

Код ошибкиОбщее количествоТекст ошибки
C0301356Line too long (line-too-long)
E0401332Unable to import «module_name»  (import-error)
C0116329Missing function or method docstring (missing-function-docstring)
C0103290«module_name» doesn’t conform to snake_case naming style (invalid-name)
C011494Missing module docstring (missing-module-docstring)
C011570Missing class docstring (missing-class-docstring)
W061164Unused «module_name» imported from «module_name» (unused-import)
E040260Attempted relative import beyond top-level package (relative-beyond-top-level)
R020158Method could be a function (no-self-use)
W010738Unnecessary pass statement (unnecessary-pass)
R091329Too many arguments (too-many-arguments)
E021327Method should have «self» as first argument (no-self-argument)
R090327Too few public methods (too-few-public-methods)
R090223Too many instance attributes  (too-many-instance-attributes)
R170512Unnecessary «else» after «return» (no-else-return)
C04118third party import «module_name» should be placed «module_name»  (wrong-import-order)
R09117Too many return statements (too-many-return-statements)
R09147Too many local variables (too-many-locals)
R09044Too many public methods (too-many-public-methods)
R09154Too many statements (too-many-statements)
W05114bki aggregate params # TODO
R09122Too many branches (24/12) (too-many-branches)
R17042Redefining argument with the local name (redefined-argument-from-local)
W02012Attribute ‘comment’ defined outside __init__ (attribute-defined-outside-init)
W06132Unused argument ‘ct’ (unused-argument)
C02091Formatting a regular string which could be a f-string (consider-using-f-string)
C03041Final newline missing (missing-final-newline)
C03251Unnecessary parens after ‘not’ keyword (superfluous-parens)
R09161Too many boolean expressions in if statement (too-many-boolean-expressions)
R17101Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements)
W02351Useless super delegation in method ‘is_actual’ (useless-super-delegation)
W04041Reimport «module_name»  (imported line 2) (reimported)

Предлагаю рассмотреть 10 самых часто встречающихся недочётов:

Самый часто встречающийся недочёт — это нарушение допустимой длины строки при написании кода. Встречается 356 раз. Такие фрагменты кода в IDE подчёркиваются яркой пунктирной линией.

В pylint этот недочёт соответствует нарушению кода C0301. Недочёт критическим не является, влияет лишь на удобство чтения структуры кода.

Следующий по частоте встречаемости – ошибки импорта (код ошибки -). Pylint нашёл следующие коды ошибок:  E0401- 332 случая, W0611 – 64 случая, E0402 – 60 случая. Каждый из случаев надо смотреть отдельно, поскольку невозможно оценить критичность недочёта лишь визуально в таблице. В каких-то случаях этот недочёт говорит лишь о нарушении каких-то общих стандартов стилистики импорта и никак не влияют на сам код, а в некоторых случаях могут возникнуть проблемы.

Также было выявлено в сумме 493 случаев возникновения ошибок по отсутствию строк документации (кода ошибки C0116 – 329 случаев, C0114 – 94 случаев, C0115 – 70 случаев). Недочёт считается стилистическим. В Python считается хорошим тоном документировать функции, классы, методы, объясняя суть работы и приводя примеры, как используются. В данном случае, встречаются скрипты, в которых нет документации.

Следующим по частоте встречаемости является 290 случаев несоответствия стилю наименования snake_case (код ошибки C0103).

И, наконец, 58 случаев отводится ошибкам с кодом R0201. Ошибка возникает, когда методу не предшествует @staticmethod или @classmethod, а также он не содержит никаких ссылок на класс или экземпляр класса (например, через ключевое слово self). Данная ошибка не является критической, но следует проверить, действительно ли раздел кода должен быть определен как метод класса.

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

Критических недочётов среди найденных pylint не было.

Далее рассмотрю распределение количества ошибок по файлам:

Были взяты лишь те файлы, количество недочётов в которых более 20.

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

Имя файлаОценкаКол-во ошибок
106_tree-checkpoint.py9,114
107_tree.py9,114
10_product_config.py8,572
9_product_config-checkpoint.py8,572

Проанализирую файлы, общая оценка которых выше 5. Таких 29 файлов.

Выше 8 баллов удостоились следующие файлы:

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

Предсказуемый результат: чем меньше количество недочётов в файле, тем выше общая оценка, даваемая pylint.
Pylint дает разностороннюю информацию по коду. В использовании удобен, имеет множество настроек, изучать которые можно неделями.