$30 off During Our Annual Pro Sale. View Details »

Кодовый стиль python проектов в 2023 году

Denis Anikin
September 18, 2023

Кодовый стиль python проектов в 2023 году

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

https://nastachku.ru/kodovyy-stil-python-proektov-v-2023-godu

Дальнейшее развитие темы (upd от 2024 года) можно найти тут: https://github.com/community-of-python/pylines

Denis Anikin

September 18, 2023
Tweet

More Decks by Denis Anikin

Other Decks in Programming

Transcript

  1. Денис Аникин 2 Что я делаю — работаю в Райфе

    — teamlead в 3 командах — community lead в Python Community — fullstack: typescript, python, devops — много раз выступал на конференциях и митапах https://xfenix.ru
  2. Немного слов в начале 3 —Я расскажу о том какой

    codestyle сейчас у нас (а мы работаем над ним постоянно, поэтому он может быть полезен) в community —Расскажу о том каким он был ранее —На основе этого расскажу как вам самостоятельно писать код по качеству лучше, чем в 95%+ проектов, что существуют вокруг, как защититься от типовых ошибок —Даже если вы уже работаете в крупной организации, некоторые решения могут быть для вас новыми
  3. Что я понимаю под codestyle? 4 Это может быть не

    очевидно —Стандартные соглашения по тому как писать код —Стандартные способы предовтращения появления ошибок —Стандартные способы улучшения вашего кода —Стандартные инструменты, помогающие всего этого достигать —Типовые настройки для всего вашего рабочего процесса (от локальной среды до CI/CD конвейера) —И не только
  4. Зачем нам нужен codestyle — Снижение когнитивной нагрузки — Ускорение

    восприятия информации — Как следствие, возможность проще и легче поддерживать (делать новые части и не позволять старым «гнить») ваши проекты — Унификация (многие вещи понятны, даже если не вы их писали) — Устранение многих ошибок ещё до момента их возникновения
  5. Junior у него свой меч в спине застрял Middle у

    него всё норм Senior именно он сжег деревню
  6. PEP8 и не только 22 Вместе c black —Стандартный кодовый

    стиль для питон проектов —Разработчики часто путают PEP и PEP8 —Имеет смысл использовать его как стандарт по-умолчанию и не искать ничего лучшего
  7. Black 24 Если вы его еще не используете —Имеет мало

    опций настройки и это правильно (я не очень поддерживаю grey) —Можно конфигурировать длину линии, у нас стандарт 120 —Проверяет, что код продуцирует валидное AST —За более чем 4 года работы в качестве автоматического инструмента у очень большого количества людей показал себя как действительно надежный инструмент
  8. И вновь PEP8 27 https://peps.python.org/pep-0008/#imports —Импорты надо разбивать на 3

    группы: встроенные, сторонние библиотеки и свой код —Разделять стоит 1 пустой строкой —После этого блока 2 пустых строки
  9. isort default 32 from __future__ import absolute_import import os import

    sys from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14, lib15) from my_lib import Object, Object2, Object3 print("Hey") print("yo")
  10. isort default — нормальные импорты 33 from __future__ import absolute_import

    import os import sys from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14, lib15) from my_lib import Object, Object2, Object3 print("Hey") print("yo")
  11. isort default — а вот так вообще хорошо 34 from

    __future__ import absolute_import import os import sys import third_party import my_lib print("Hey") print("yo")
  12. Дополнительные соглашения по импортам 35 —Импортируйте встроенные модули целиком (особенно

    typing) —Если у вас 2 и более импортов из модуля — импортируйте его целиком —Стандартный pep8, но все таки: не импортируйте «звездочку»
  13. Конфигурация isort pyproject.toml 36 Для соблюдения PEP8 [tool.isort] line_length =

    120 multi_line_output = 3 include_trailing_comma = true lines_after_imports = 2 lexicographical = true sections = ["FUTURE", "STDLIB", "FIRSTPARTY", "THIRDPARTY", "LOCALFOLDER"] no_lines_before = ["STDLIB", "THIRDPARTY"] known_third_party = [] known_local_folder = []
  14. Что представляет из себя pyupgrade 46 —Умеет переписывать код под

    новые версии python —Все так же умеет inplace —В постоянную работу, все же, взять его проблематично (если у вас больше 1 проекта)
  15. Код ещё лучше 49 —Внедряет некоторые хорошие практики написания кода

    (их всего 10, но они важные) —И тоже умеет inplace —Увы, слабо развивается
  16. Все тоже 53 —Берет комментарий, eval’ит его и если тот

    eval’ится, то удаляет такой комментарий —Тоже умеет inplace
  17. Битва pylint vs flake8 58 Плюсы —На мой взгляд pylint

    содержит нереальное количество хороших практик и советов из коробки —Этот инструмент сразу даёт возможность писать как минимум хорошо —Для тех, кто устал от пачек линтеров, решение «всё в одном»
  18. Битва pylint vs flake8 59 А вот дальше идут минусы

    —Нереально медленный —Очень сложная конфигурация —Fix режима нет (а так можно было?)
  19. Лучший тайп чекер — mypy 62 Альтернатива ему — pyright

    —Сейчас, в 2023 году, имеет смысл включать strict по-умолчанию —Не забывайте о mypy . --install-types —Для pydantic можно подключить комплектный плагин —У моего коллеги, Миши Гурбанова, вышел доклад с подробным исследованием текущих тайпчекеров: https://pycon.ru/da-kto-takie-ehti-vashi- tajp-chekery
  20. Это впечатляет 71 У вас есть почти всё, что вы

    вообще могли бы хотеть. И это на космической скорости —pylint —isort —около 30 плагинов flake8 —perflint (устраняет некоторые анти-паттерны, убивающие производительность) —refurb (но частично) —pyupgrade —eradicate —pydocstyle —numpy, airflow, pandas-vet, flynt, mccabe, pep8-naming, pycodstyle —свои правила
  21. Некоторые правила мы все же отключаем 81 Лишь небольшую часть

    —I так как передать конфигурацию глобально невозможно. Если вы индвидуально запускаете ruff в каждом проекте, то можно использовать pyproject.toml (информация уточняется) —TRY003 писать message в exception это нормально —D1 можно пропускать все докстринги —G004, если вы любите f-строки в логах —ANN101 можно отключать, если вы не хотите везде подписывать typing.Self, так как он нормально выводится mypy сейчас автоматически —EM если вы любите добавлять сообщения в ваши эксепшены —FBT довольно спорный набор правил, запрещающий bool аргументы
  22. Как pyproject.toml может выглядеть 82 Для конфигурации ruff (в бонус

    пресет для тестов) [tool.ruff] fix = true line-length = 120 select = ["ALL"] ignore = ["D1", "D203", "D213", "FA102", "I”, “ANN001”] [tool.ruff.extend-per-file-ignores] "tests/*.py" = ["S101", "S311"]
  23. Другие интересные нюансы 85 —Более 600 правил и число растет

    быстро —Есть кеширование —Дружит с монорепами
  24. Документация 96 —Все считают, что она должна быть —Почти у

    всех её либо нет, либо она не актуальная (что хуже её отсутствия) —Всем всегда стыдно, что её нет и, почему-то, менее стыдно, что она не актуальная —Нет хороших подходов, есть спорные —Автодокументация не работает, её почти не читают —Спеки выглядят хорошо в начале дороги, после сотой в них не разобраться
  25. Основные принципы 102 Они довольно простые —Вербозные названия всего: переменных,

    классов, функций, констант —Не используйте слово get в большинстве случаев —Меньше магических методов (лучше вообще без них) —Никаких чисел кроме 0 и 1 в коде, все остальное — только константы с человеческими именами —Используйте enum для константных перечислений —Функция должна именоваться глаголом, а не существительным. Она что-то делает! —Зен питона — не набор правил
  26. Как этого достичь? 105 —Вам нужен vscode —Вам понадобятся стандартные

    плагины mypy, black и ruff, а так же, возможно, отдельностоящий isort
  27. 106 Как выглядит конфигурация? "python.linting.mypyEnabled": true, "python.formatting.provider": "black", "python.formatting.blackArgs": [

    "--line-length=120", ], "[python]": { "editor.formatOnPaste": true, "editor.formatOnType": true, "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll": true, }, }, "ruff.args": [ "--line-length=120", "--ignore=I,EM,FBT,TRY003,S101,D101,D102,D103,D104,D105,G004,D107,FA102", "--fixable=ALL", "--select=ALL" ], "emeraldwalk.runonsave": { "commands": [ { "match": ".py", "cmd": "isort --line-length 120 --lines-after-imports 2 --no-lines-before stdlib,localfolder ${file}", "isAsync": true, } ] }
  28. Некоторые полезные решения 108 —Sourcery (правил меньше, чем у ruff,

    но несколько раз он находил что-то дополнительно). Пока пилотируем. Правил около 200 —Tabnine —Или Code whisperer —Copilot пока лучше всех, но не работает без VPN и аккаунта за 100$ —Можно использовать ChatGPT 3.5, Bard, GigaChat, TheB.AI
  29. Как стартануть? 110 Я знаю, может быть не просто —Берете

    ruff —Берете mypy —Включите режим strict в mypy —Включаете все правила в ruff —Они вас заставят J
  30. Стремитесь к покрытию кода в 100% Аннотации tips & tricks

    111 —Освойте не только стандартные типы, но и Literal и особенно Final —Final имеет смысл аннотировать все переменные вообще по-умолчанию (чаще всего изменение переменных и ведет к ошибкам) —Примитивные типы можно не аннотировать (a = 5 не надо расписывать как a: int = 5) —Дженерики иногда полезны —Annotated помогает в валидации —сast, overload, final, never, noreturn, typealias, newtype, classvar, typeguard, unpack, Protocol, assert_type, assert_never, TYPE_CHECKING — очень полезные инструменты
  31. 112 Пример с Final: до some_value = 5 some_another_value =

    'kek’ for one_element in range(100): some_another_value = some_another_value * some_value some_value += 1
  32. 113 Пример с Final: после import typing some_value: typing.Final =

    5 # <- тут само выведет typing.Final[typing.Literal[5]] some_another_value: typing.Final = 'kek' for one_element in range(100): some_another_value = some_another_value * some_value # <- тайпчекер поймает some_value += 1
  33. Серьезные советы 121 Правда (тм) —Успокойте свое чувство эстетики, оно

    совершенно неуместно когда речь заходит о больших и дорогих проектах —Общий кодовый стиль и подход стоят гораздо больше, чем «красивые» кавычки или более веселые переносы —Если гасите правила в линтерах, то обязательно записывайте в глобальные командные установки почему вы это делаете —Ваши аргументы должны быть адекватными («мне не понравилось» — не такой аргумент)
  34. Набор стандартных советов 123 —Всегда «сужайте» try-except блок до минимального

    количества строк (если возможно, до 1) —Всегда «сужайте» класс ошибки в except (не все знают, но это крайне полезная графическая иллюстрация стандартной иерархии ошибок https://docs.python.org/3/library/exceptions.html#exception-hierarchy) —Делайте ретраи, ограничивайте количество раз, делайте умный интервал и jitter (библиотека backoff поможет) —Храните всю конфигурацию линтеров в pyproject.toml —Избегайте вложенных и сложных условий с помощью инверсии —Используйте композицию и меньше наследования
  35. И кое-что ещё 124 — https://github.com/regebro/pyroma позволяет оценить «дружелюбность» вашего

    пакета — https://github.com/jendrikseipp/vulture ищет мертвый код по всему проекту — https://github.com/dropbox/pyannotate выводит аннотации для вашего кода (но работа довольно ручная) — https://github.com/JelleZijlstra/autotyping добавляет типовые аннотации (не идеально) — https://github.com/bndr/pycycle обнаруживает циклические импорты (тесты?) — https://github.com/Instagram/Fixit любопытный фреймворк для автоисправлений кода (!) — https://github.com/lyz-code/autoimport сам добавляет нужные импорты — https://github.com/pyupio/safety проверяет безопасность проектов
  36. Что по поводу SOLID & GRASP 125 —https://github.com/mschwager/cohesion проверяет связанность

    вашего кода —Mypy проверяет принцип Liskov substitution principle —Если вы хотите достичь DIP принципа, попробуйте паттерны IoC и DI: на pycon 2023 были посвященные этому доклады. Так же есть неплохие DI фреймворки, мне нравится dependency injector по своему, так же можно посмотреть di, rodi, lagom —Для некоторых других архитектурных линтеров советую доклад Коли https://pycon.ru/arhitektura-knuta-i-pryanika