Далее в статье я поясню, что я понимаю под термином «качество кода» и какую пользу оно приносит разработчикам.

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

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

Качество кода

У всех кодеров вырабатывается (субъективное) чувство хорошего или плохого кода, и оно развивается пропорционально с опытом, который мы приобретаем в работе с той или иной технологией.

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

  • Правила форматирования.

  • Критические ошибки, например, обращение к необъявленной переменной или разыменование нулевого указателя.

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

  • Языковые идиомы.

  • Комментарии.

  • Документация.

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

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

Зачем заботиться о качестве кода?

Даже если конечные пользователи программного обеспечения могут этого не заметить, более высокое качество кода имеет очевидные преимущества:

  • Уменьшение количества багов на строку кода (LoC).

  • Последовательное форматирование приводит к более читаемому коду, что улучшает сопровождаемость.

  • Улучшение инспекции кода за счет более полной информации о типах. Явные типы параметров функций, переход к определениям, просмотр документации...

  • Более счастливые разработчики. Это важнее, чем кажется, поскольку текущие разработчики будут более продуктивны и с большей вероятностью останутся в проекте. Также это делает проект привлекательным для новых сотрудников. Согласно отчету Zenhub о счастье разработчиков за 2022 год, «качество проектов» занимает второе место в рейтинге счастья разработчиков, а «качество инструментов разработки» — четвертое.

Но ничто не дается бесплатно, и обратная сторона очевидна: в поддержание высокого качества кода необходимо вкладывать много времени и сил.

К счастью, у нас есть выход: автоматизация.

Зачем автоматизировать качество кода?

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

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

Преимущества складываются следующим образом:

  • Ошибки обнаруживаются на более ранних этапах жизненного цикла программного обеспечения (в идеале — во время написания кода разработчиком).

  • Не тратится много времени на форматирование кода. И даже лучше — затрачивается меньше умственных ресурсов, поскольку вы перестаете обращать внимание на форматирование и больше концентрируетесь на том, что делает код.

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

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

Рассматриваемые категории

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

Линтинг

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

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

В компилируемом языке (например, C, C++, Rust, Go...) компилятор отлавливает ошибки, поэтому программа не будет собираться до тех пор, пока они не будут исправлены. В интерпретируемых языках, таких как Python или Javascript, многие из этих ошибок возникают во время выполнения. Именно здесь и проявляет себя линтер, обнаруживая их до запуска программы.

Форматирование

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

  • Табуляция или пробелы?

  • Сколько пробелов на один уровень отступа? 2? 4? 8?

  • Одинарные кавычки (') или двойные кавычки ("")?

  • Ставить открывающую скобку при объявлении функции или в следующей строке?

  • Как оформлять определения функций? Параметры на одной или на разных строках? Один параметр на строку или до максимальной ширины строки?

  • Сколько пустых строк оставлять между объявлениями модулей?

  • Как сортировать импорты?

  • И так далее.

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

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

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

Подсказки типов

Это характерно для Python и Javascript/Typescript (Как наиболее популярных языков с опциональной подсказкой типов. Я не утверждаю, что других не существует!)

И Python, и Javascript являются динамически типизированными языками. Это дает возможность изменять тип переменной во время выполнения программы, в отличие от статически типизированных языков, в которых все типы задаются и проверяются во время компиляции и не могут изменяться во время выполнения программы.

Python является строго типизированным языком, поэтому каждая переменная имеет определенный тип в любой момент времени во время выполнения программы. С другой стороны, Javascript является слабо типизированным, поскольку переменные могут иногда менять тип в зависимости от контекста (например, '1' + 1 приведет к преобразованию 1 в строку, что приведет к неожиданному результату '11'). Typescript является строго типизированным, что является веской причиной предпочесть его Javascript.

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

В случае с Python только после выхода PEP 484 и Python 3.5 появилась возможность аннотировать код с указанием ожидаемого типа переменных, аргументов и возвратов из функций. Спустя много лет появились инструменты, позволяющие не только проверять код в соответствии с подсказками типов, но и делать другие интересные вещи — например, проверку данных во время выполнения, как это делает Pydantic.

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

А как же тесты?

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

А как насчет покрытия кода?

Покрытие кода, то есть процент покрытого тестами кода, также является распространенной метрикой качества кода. Я не включаю ее в эту статью по двум причинам:

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

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

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

Наиболее распространенным инструментом измерения покрытия кода в Python является coverage.

Инструменты обеспечения качества кода на языке Python

flake8

flake8 — это линтер, который проверяет код на широкий спектр проблем: от ошибок (например, упоминание необъявленной переменной) до предупреждений (например, неиспользуемый импорт) и стилизации (например, длинные строки или избыточное количество пробелов).

Это хорошая начальная конфигурация. Ее нужно поместить в файл .flake8 в корне проекта:

[flake8]
max-line-length = 90
extend-ignore = E501, E203, W503
per-file-ignores =
    settings*.py:E402,F403,F405
exclude =
    .git,
    __pycache__,
    .tox,
    .eggs,
    *.egg,
    .venv

max-line-length должна соответствовать тому, что вы настроили в Black (см. следующий раздел). Мне нравится 90, но вы можете взять другое значение. По умолчанию в Black используется значение 88. Я не рекомендую превышать 100, так как длинные строки может быть сложнее читать в таких инструментах для визуального сравнения, как Github's split view:

Click to see full size
Click to Пример с длинными строкамиsee full size

per-file-ignores полезен для игнорирования некоторых ошибок или предупреждений в некоторых файлах. В данном примере я игнорирую предупреждение об использовании import * в файлах настроек Django, так как это часто встречающееся явление. Не стесняйтесь приспособить его к своему конкретному случаю.

Альтернативы flake8:

  • pylint: настраиваемый линтер с поддержкой плагинов.

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

Black

Black — это форматировщик кода на Python. Он возьмет любой файл Python (если в нем нет синтаксических ошибок) и изменит его в соответствии со стилем кода Black

  • разделит длинные строки, 

  • применит двойные кавычки в строках,

  • удалит лишние пробелы и пустые строки,

  • и многое другое. 

Одним словом, Black сделает файлы совместимыми с PEP 8.

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

Итак, чтобы использовать Black, просто установите его с помощью pip, а затем добавьте в файл pyproject.toml в корне проекта:

[tool.black]
line-length = 90
target-version = ['py310']
extend-exclude = '''
(
  migrations   # Django DB migrations
)
'''

Конфигурация проста и использует хорошие значения по умолчанию. Black можно запустить вообще без конфигурации, и он будет отлично работать, только учтите, что длина строки (line-length) по умолчанию равна 88. Я отдаю предпочтение явной конфигурации, поэтому рекомендую добавить конфигурационный файл, даже если он минимален.

Обратите внимание на использование extend-exclude вместо exclude. Параметр exclude в Black имеет разумное значение по умолчанию, которое исключает большинство файлов, которые обычно нужны (.git и .hg, build, dist и т.д.). Поэтому, если только в exclude нет файлов, которые вы действительно хотите включить, лучше использовать extend-exclude для добавления к значениям exclude по умолчанию.

При внедрении Black в существующую кодовую базу можно использовать этот прием, чтобы избежать запуска git blame.

Документация по Black также содержит руководство по настройке других инструментов для совместимости с Black.

При запуске Black в CI без pre-commit используйте опцию --check, чтобы файлы не переформатировались, а просто выдавался результат pass/fail с дефектными файлами.

Некоторые альтернативы Black:

  • yapf: создан компанией Google. На момент написания статьи (июнь 2023 г.) все еще считается бета-версией, в то время как Black выпустил свою первую стабильную версию 29 января 2022 года.

  • autopep8: использует инструмент pycodestyle. Он умеет исправлять устаревший код, поэтому может быть полезен при миграции кодовых баз с Python 2 на 3.x.

isort

isort автоматически сортирует все импорты в Python-файлах, следуя заданной конфигурации, или, как говорится на их домашней странице: “isort your imports, so you don't have to” (используйте isort для автоматической сортировки импорта, чтобы не пришлось это делать вручную).

Импорт будет разбит на группы и отсортирован в алфавитном порядке. Группами являются:

  1. Стандартная библиотека Python.

  2. Сторонние зависимости.

  3. Зависимости текущего проекта.

Конфигурация помещается в тот же файл pyproject.toml, который используется в Black. Это отправная точка:

[tool.isort]
profile = "black"
line_length = 90
multi_line_output = 3
skip_gitignore = true
skip_glob = ["**/migrations/*", "**/settings/*"]
src_paths = ["<your_code_dir>"]

Самое главное, это использовать profile = "black", чтобы сделать его совместимым с тем, как Black ожидает сортировки импорта. Таким образом, мы получаем лучшее от обоих инструментов: Black форматирует файлы, а isort сортирует импорт, не мешая друг другу. У isort есть много других профилей.

И конечно, сделайте так, чтобы line_length соответствовал настройкам Black и flake8.

В skip_globs можно поместить файлы, которые не должны автоматически обрабатываться с помощью isort. В примере я исключаю миграции Django DB, так как они генерируются автоматически, и файлы настроек, которые в некоторых проектах имеют плохой стиль импорта, например, import*.

У isort есть флаг --check, как и у Black, используйте его в CI, если работаете без pre-commit.

mypy

Подсказки типов — относительно новое дополнение к языку Python, начиная с PEP 484 в Python 3.5. Поскольку Python всегда был динамически типизированным языком, любое расширение типизации является чем-то необязательным, и разработчики могут выбирать, использовать его или нет.

Mypy — это статическая программа проверки типов для Python. Он анализирует подсказки типов и определяет, корректна ли программа или в ней есть несоответствия. Например, если переменная может быть строкой (string) или null, а вы пытаетесь вызвать для нее строковый метод, mypy выдаст ошибку. Тут вы можете проверить код и проконтролировать случай когда переменная равна null, избежав тем самым потенциального бага во время выполнения.

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

Итак, перейдем к настройке mypy — он имеет обширную конфигурацию. Он поддерживает либо файл mypy.ini, либо может использовать pyproject.toml. Я рекомендую использовать pyproject.toml, поскольку он становится стандартным файлом для всех инструментов Python, другие конфигурационные файлы я считаю устаревшими.

Вот пример конфигурации mypy в файле pyproject.toml:

[tool.mypy]
mypy_path = "./src"
follow_imports = "silent"
strict_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
disallow_any_generics = true
check_untyped_defs = true
no_implicit_reexport = true

[mypy-pyproj.*]
# use for dependencies that don't have typing stubs
ignore_missing_imports = True

[tool.pytest.ini_options]
addopts = [
    "--import-mode=importlib",
]
pythonpath = "src"

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

Для больших кодовых баз лучше воспользоваться mypy, запущенный как отдельный процесс-демон, чтобы ускорить проверку.

В этом случае ознакомьтесь с документацией mypy по добавлению к существующей кодовой базе.

Альтернативы mypy:

  • pyright: проект компании Microsoft с открытым исходным кодом, представляет собой средство проверки типов данных. Есть также инструмент с закрытым исходным кодом, расширяющий возможности pyright — языковой сервер pylance.

  • pyre: программа проверки типов с упором на производительность. Утверждается, что «работает на больших кодовых базах с миллионами строк Python». Создан компанией Facebook.

Другие инструменты, которые рекомендую рассмотреть

bandit проводит проверку на потенциальные ошибки безопасности. Может интегрироваться с pre-commit. Также есть плагин flake8-bandit.

flake8-bugbear — плагин для flake8, добавляющий дополнительные проверки на некоторые «подводные камни» Python, т.е. код, который может привести к неожиданным результатам.

pydocstyle автоматически форматирует docstrings. Если вы используете flake8, обратите внимание на плагин flake8-docstrings.

В этих списках вы найдете десятки других инструментов для работы с Python:

Интеграции

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

Этот раздел состоит из трех частей:

  • IDE (Integrated Development Environment), т.е. ваш редактор кода.

  • CI (Continuous Integration) — непрерывная интеграция.

  • pre-commit хуки.

IDE

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

Прежде чем перейти к конкретным рекомендациям по IDE, я хочу упомянуть о протоколе языкового сервера (Language Server Protocol, или LSP). Современные IDE не реализуют такие фичи, как автозаполнение, переход к определению, поиск ссылок, операции рефакторинга и т.д. Они взаимодействуют с языковым сервером, который предлагает эти операции. Например, для Python существует python-lsp-server, который имеет множество плагинов для интеграции с различными инструментами. Таким образом, вашему редактору не обязательно использовать Black для форматирования или flake8 для линтинга, достаточно просто взаимодействовать с LSP-сервером, который их поддерживает.

Что касается рекомендаций по IDE, то лично я использую Emacs и не думаю, что это когда-либо изменится. Ее кривая обучения не похожа ни на одну другую, но по этой же причине она может дать вам больше возможностей, чем любой другой редактор. Для подключения к LSP-серверу необходимо использовать либо lsp-mode, либо eglot (который является частью самого Emacs, начиная с Emacs 29). Если вам интересна моя конфигурация Emacs, то она опубликована здесь.

Многим нравится Vim. Это отличный редактор, быстрый и доступный практически во всех UNIX-подобных операционных системах в виде старинного vi. Я пользовался им много лет, пока не решил попробовать Emacs, услышав о нем много интересного. Еще я слышал, что Neovim — замечательный редактор, обязательно попробуйте его, если вам нравится Vim.

В настоящее время Visual Studio Code, или VSCode, является популярным относительно молодым инструментом на рынке. И не зря: великолепное удобство использования, его легко настраивать и добавлять плагины для выполнения всевозможных задач. Мне он, однако, не нравится, потому что это софт с закрытым исходным кодом, и я готов поспорить, что это вопрос времени, пока Microsoft не подставит своих пользователей каким-нибудь неприятным способом, как это уже неоднократно случалось с программным обеспечением с закрытым исходным кодом. По крайней мере, существует VSCodium, представляющий собой свободно распространяемый софт без проприетарного кода (по аналогии с Chromium у Chrome). Но кто знает, как долго это продлится.

Если вы молодой разработчик или готовы приложить усилия, я рекомендую выбрать между Emacs и Vim/Neovim. Вы не пожалеете, они прослужат вам всю жизнь.

Непрерывная интеграция (CI)

Важно отметить, что все эти инструменты и проверки кодовой базы могут быть отменены недобросовестным разработчиком. По этой причине хорошей идеей (а некоторые считают, что и требованием) является принудительная проверка, не позволяющая разработчикам мержить код, который приведет к ошибкам. Обычно это делается с помощью системы, выполняющей необходимые проверки кода перед его слиянием с основной веткой. Такая система называется системой непрерывной интеграции (Continuous Integration), или CI.

В этом случае схема работы по внесению изменений в проект выглядит следующим образом:

  1. Разработчик коммитит изменения в ветке и создает пул-реквест (PR) в сервисе хостинга кода (например, Github или Gitlab).

  2. Сервис хостинга кода уведомляет об этом CI-инструмент, который запускает новую проверку на текущем коммите.

  3. Разработчик не может выполнить слияние до тех пор, пока проверка не завершится успешно (обычно для некоторых пользователей существуют специальные права, позволяющие отменить это действие и выполнить слияние в любом случае).

  4. При успешном завершении проверки разработчик может объединить изменения. При добавлении новых изменений будет запущен CI, и процесс начнется заново.

  5. При ошибках разработчик может посмотреть журнал заданий и выяснить, что не так, исправить это и запушить новые изменения. Это вызовет новую проверку CI и перезапустит поток.

Есть десятки CI платформ, но я перечислю несколько, с которыми мне довелось работать:

pre-commit

При внедрении проверок с использованием CI разработчикам становится больно вносить изменения, а через несколько минут увидеть ошибку. Для достижения максимальной производительности (и счастья!) разработчиков цикл обратной связи между ошибками и их исправлением должен быть как можно короче. По этой причине целесообразно не просто разрешить разработчикам выполнять проверки локально, а сделать это легко и быстро.

pre-commit — это фреймворк для управления и поддержки мультиязычных pre-commit хуков. Хуки git — это программы, которые могут быть установлены в git репозиториях для выполнения при срабатывании git на определенные действия. Как следует из названия, pre-commit обычно запускается перед созданием коммита, но при необходимости его можно настроить на запуск перед git push или в другой момент.

Преимущества pre-commit:

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

  2. Быстрое и простое добавление сторонних хуков и их обновление (pre-commit autoupdate).

  3. Возможность выбора для разработчиков. Они могут либо вообще не использовать pre-commit (просто не устанавливая хуки), либо пропускать проверки для определенных коммитов (используя git commit -n). Но тогда не надо жаловаться, что CI не работает! ;-)

Минусы pre-commit:

  1. Еще одна зависимость/инструмент, которым нужно будет управлять.

  2. pre-commit устанавливает хуки в отдельное окружение, что может быть расточительно, если вы уже устанавливаете некоторые инструменты в виртуальную среду проекта.

Это пример конфигурации pre-commit, в которую интегрированы рекомендуемые мною инструменты. Просто поместите его в .pre-commit-config.yaml:

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v4.4.0
  hooks:
  - id: check-json
  - id: check-toml
  - id: check-yaml
  - id: check-merge-conflict
  - id: debug-statements
- repo: https://github.com/PyCQA/isort
  rev: 5.12.0
  hooks:
  - id: isort
    args: [--filter-files, src/]
- repo: https://github.com/psf/black
  rev: 23.3.0
  hooks:
  - id: black
    exclude: migrations
    args: [--check,--config=pyproject.toml]
- repo: https://github.com/PyCQA/flake8
  rev: 6.0.0
  hooks:
  - id: flake8
    args: [--config=pyproject.toml]
- repo: local
  hooks:
    - id: mypy
      name: mypy
      entry: "sh mypy_run.sh"
      language: system
      # run if any Python file is changed
      types: [python]
      # mypy will always check all files
      pass_filenames: false
      # use require_serial so that script is only called once per commit
      require_serial: true

pre-commit autoupdate находит новые версии используемых pre-commit хуков и автоматически обновляет файл .pre-commit-config.yaml для их использования.

Mypy и pre-commit

Заставить mypy работать в pre-commit может быть непросто, поскольку pre-commit работает в собственном окружении и не видит установленных зависимостей проекта. Именно по этой причине официальный хук mypy для pre-commit, на мой взгляд, имеет ограниченную полезность.

Я обычно использую shell-скрипт, который активирует виртуальное окружение проекта и вызывает mypy, а pre-commit запускает его при каждом изменении Python-файла. Вот пример файла mypy_run.sh, на который ссылается пример .pre-commit-config.yaml, приведенный выше:

./venv/bin/mypy src/

Если вы знаете более удачный способ запуска mypy с pre-commit, то напишите, пожалуйста, в комментариях.


В заключение приглашаем на бесплатные открытые уроки:

  • 25 июля в 20:00, «Аннотации типов»: обсудим различные виды типизации, заглянем в теорию типов, рассмотрим примеры и best practice по аннотированию в Python, поговорим про существующие type checker'ы. Регистрация

  • 15 августа в 20:00, «Code review»: на практических примерах рассмотрим правила и нюансы проведения Code review. Регистрация

Комментарии (10)


  1. dlinyj
    25.07.2023 12:37

    flake8 интересен тем, что его можно встроить в проверку прямо скриптами. Например, в одной организации он работал внутри git hooks и не давал сделать коммит, пока ты не поправишь по всем внутренним правилам компании. Это решение "любили" все разработчики, особенно когда ты в пятницу вечером пытаешься что-то закоммитить и тебе прилетает такое. В результате просто удаляли скрипты гитхуков.


    1. iuabtw
      25.07.2023 12:37

      Я был бы только рад хуку, который не даёт запушить что-то.

      Ваши разработчики просто не видели решение ещё лучше. В одном месте был настроен линтер на стадии юнит-тестов, но не на всех проектах, а только на нескольких. В итоге ты пушишь код, ставишь сборку пакета, через 15 минут она падает, потому что не прошла линтер.


      1. dlinyj
        25.07.2023 12:37

        Какая прелесть, вы тоже удаляли гитхуки?


        1. iuabtw
          25.07.2023 12:37

          Нет, я же говорю - их не было в принципе.


          1. dlinyj
            25.07.2023 12:37

            А, неправильно понял.


    1. rahmaevao
      25.07.2023 12:37

      Надо было в CI прописать эти же проверки.


      1. dlinyj
        25.07.2023 12:37

        Прописали, но уже после моего увольнения.


  1. axelmaker
    25.07.2023 12:37
    +2

    Советую посмотреть еще и на ruff: https://github.com/astral-sh/ruff


    1. domix32
      25.07.2023 12:37

      Перевод же. Но ruff хорош, да


  1. zubrbonasus
    25.07.2023 12:37

    Существуют бэстпрактис по код стал. Может быть легче прочитать и пользоваться ими. Например вложенность не больше 3х уровней, длина метода примерно 20 строк и т.д.