Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Как прокачать линтер
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Maxim Mazaev
March 23, 2019
Programming
45
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Как прокачать линтер
Python Meetup Chelyabinsk #5
Maxim Mazaev
March 23, 2019
More Decks by Maxim Mazaev
See All by Maxim Mazaev
Проверка типов в большом проекте
mmazaev
0
31
Pylint изнутри. Как он это делает
mmazaev
0
100
Как поддерживать согласованность в микросервисной архитектуре
mmazaev
0
64
Other Decks in Programming
See All in Programming
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
1.2k
現場と育てるRailsモデリング 〜Spreadsheetから始めたモデル設計〜
siroemk
2
590
AmebaブログはKMPから卒業します
kmars0605
0
190
3回つくってわかった、 定期実行ジョブ基盤の本質情報
takai
0
460
[ハンズオン]AIへの指示だけで「五目並べ」を作ってみよう
satoshi256kbyte
1
380
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
180
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Kaigi on Rails 2026, Tokyo, Japan
marcoroth
2
220
npm との違いを手がかりにGo の依存管理を理解したい
optim
0
300
機械に任せるテスト、人が見るテスト ⽣成AIで引き直した、運⽤保守フェーズの線引き
dske104
0
190
Event is what happens
moro
2
770
AI時代のコードレビューは人に向けるな、仕組みに向けろ
texmeijin
5
3.7k
SalesForceを内製化!? ~ HR事業を支える顧客管理基盤のインフラを大公開 ~
oku053
0
180
Featured
See All Featured
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
550
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.5k
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
600
30 Presentation Tips
portentint
PRO
1
430
Game over? The fight for quality and originality in the time of robots
wayneb77
1
300
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
520
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
23k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
2
2.2k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
Done Done
chrislema
187
17k
AI Search: Where Are We & What Can We Do About It?
aleyda
0
8.1k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Transcript
Как прокачать линтер Максим Мазаев
• Программист в ЦИАН • Пишу на Python • Спикер
PyCon, Moscow Python Conf++ О себе
Как прокачать линтер
• 1 млн уникальных пользователей в сутки • 10x разработка
за 2014-2019 • 100+ разработчиков • 30 питонистов • Сотни строк нового кода каждый день О ЦИАН
Код-ревью в Циане 1. Одобрение от робота (тесты, линтер etc.)
2. Одобрение от 2 разработчиков
Проблемы с код-ревью • Есть внутренние договоренности по стилю кода.
Линтер не покрывает
Проблемы с код-ревью • Есть внутренние договоренности по стилю кода.
Линтер не покрывает • Линтер не покрывает наши договоренности по стилю.
Проблемы с код-ревью Программист работает вместо линтера
Решение • Инструмент который знает про наши соглашения • И
проверяет их
Решение
Решение ?
Решение А не написать ли нам свой линтер?
Решение А не написать ли нам свой линтер? Нет, у
нас уже есть pylint • он удобный • он встроен в наши процессы
Решение Написать плагин для Pylint
Пример №1
2015 Sergey # TODO: выпилить (не выпилили) 2015 Misha #
TODO: Убрать после успешного релиза (не убрали) 2017 Vadim # TODO: после эксперимента удалим (не удалили) ¯\_(ツ)_/¯
Пример №1 Проблема: В коде много комментариев «TODO» • Висят
годами • Не выполняются • Копится техдолг
class TodoIssueChecker(BaseChecker): __implements__ = IRawChecker Будем итерироваться по строкам модуля
(«сырой» чекер) Решение. Пишем свой чекер для Pylint
msgs = { 'С9999': ('Комментарий с TODO без ссылки на
задачу', 'issue-code-in-todo', 'Длинное описание')} Код и мнемоничеcкое название чекера Пишем свой чекер
С1234 [C]onvention [W]arning Произвольные 4 цифры [E]rror (уникальные в рамках
pylint) [F]atal [R]efactoring Пишем свой чекер. Код сообщения
msgs = { 'С9999': ('Комментарий с TODO без ссылки на
задачу', 'issue-code-in-todo', 'Длинное описание')} # pylint: disable=C9999 # pylint: disable=issue-code-in-todo Пишем свой чекер
def process_module(self, node): with node.stream() as stream: for (lineno, line)
in enumerate(stream): if has_todo_without_issue_code(line): self.add_message( 'issue-code-in-todo', line=lineno ) Пишем свой чекер Выводим предупреждение Ходим по строкам
def register(linter: PyLinter) -> None: linter.register_checker( TodoIssueChecker(linter) ) * Модуль
чекера должен быть в PYTHONPATH Регистрируем чекер *
$ cat work.py # TODO: Удалю через неделю, честно-честно! $
pylint work.py --load-plugins todo_checker ... И запускаем!
Тем временем внутри pylint...
1. Инициализация плагинов 1. Импортируем все модули, где есть плагины
2. module.register(self) <- Регистрируем + Проверяем, что параметры валидны + Регистрируем опции, сообщения и отчеты
2. Разбираем пул чекеров Чекер Чекер Чекер Чекер Чекер Чекер
Raw checker AST checker Token checker
3. Запуск чекера Запускаем метод process_module, определенный в чекере checker.process_module(module)
$ cat work.py # TODO: Удалю через неделю, честно-честно! $
pylint work.py --load-plugins todo_checker C: 0, 0: Комментарий с TODO без ссылки на задачу (issue-code-in-todo) Результат
get_offer_by_params( “sale”, True, 859483, ) Пример №2. Keyword-arguments Не всегда
очевидно что это за аргументы
get_offer_by_params( “sale”, True, 859483, ) Пример №2. Keyword-arguments get_offer_by_params( deal_type=”sale”,
truncate=True, cian_id=859483, ) Плохо Хорошо
Пример №2. Keyword-arguments Проблема: Сложно написать «сырой» checker
Пример №2. Keyword-arguments Проблема: Сложно написать «сырой» checker Решение: Чекер
на основе AST
Лирическое отступление про AST
Преобразование кода в AST. Вызов функции get_offer( 112, deal_type=’sale’, offer_type=’flat’,
) Call( func=Name(name='get_offer'), args=[Const(value=112)], keywords=[ Keyword( arg='deal_type', value=Const(value='sale') Keyword( arg='offer_type', value=Const(value='flat')) ]))]
Задача. • Найти все ноды Call (вызов функции) • Посчитать
количество аргументов • Проверить отсутствие позиционных аргументов Call( func=Name(name='get_offer'), args=[Const(value=1298880)], keywords=[ ... ]))]
class NonKeywordArgsChecker(BaseChecker): __implements__ =IAstroidChecker Чекер на основе AST Решение. Пишем
чекер на основе AST
msgs = { 'С9191': ('Краткое описание', 'keyword-only-args', 'Длинное описание')} Код
и мнемоничеcкое название чекера Пишем чекер на основе AST
def visit_call(self, node: Call) … visit_<Имя_ноды> вызывается при заходе на
ноду leave_<Имя_ноды> при уходе с ноды Пишем чекер на основе AST
def visit_call(self, n: Node): if n.args and len(n.args + n.keywords)
> 2: self.add_message( 'keyword-only-args', node=node ) Пишем чекер на основе AST
def register(linter: PyLinter): linter.register_checker( TodoIssueChecker(linter) ) Регистрируем чекер
$ cat work.py get_offers(1, True, deal_type=”sale”) $ pylint work.py --load-plugins
non_kwargs_checker ... И запускаем!
Тем временем внутри pylint...
1. Инициализация плагинов 1. Импортируем все модули, где есть плагины
2. module.register(self) <- Регистрируем + Проверяем, что параметры валидны + Регистрируем опции, сообщения и отчеты
2. Разбор модуля на AST Разбор выполняет библиотека Astroid (обертка
над AST-парсером) github.com/PyCQA/astroid/
Почему Astroid, а не AST (stdlib) 1. Разбирает дерево с
помощью typed_ast (c поддержкой тайпхинтов из PEP 484)
Почему Astroid, а не AST (stdlib) from module import Entity
def foo(bar): # type: (Entity) -> None return
Почему Astroid, а не AST (stdlib) 2. Собирает обратно с
дополнительными плюшками: from foo import * Импортирует и добавит в locals содержимое foo
Почему Astroid, а не AST (stdlib) 3. Transform plugins •
Поможем Astroid разобраться в динамической природе питона. • Дополняем AST полезной информацией см. pylint-django
3. Разбираем пул чекеров Чекер Чекер Чекер Чекер Чекер Чекер
AST checker Raw checker Token checker
4. Разобрать чекеры по типам нод 1. У каждого чекера
находим методы visit_<Имя ноды> leave_<Имя ноды>
4. Разобрать чекеры по типам нод 2. И сохраняем: _visit_methods
= dict( <Имя ноды>: [checker1, checker2,… ] ) _leave_methods = dict( <Имя ноды>: [checker1, checker2,… ] )
$ cat work.py get_offers(1, True, deal_type=”sale”) $ pylint work.py --load-plugins
non_kwargs_checker C: 0, 0: Функция с >2 аргументами вызывается с позиционными аргументами (keyword-only-args) Запускаем чекер!
class TestNonKwArgsChecker(CheckerTestCase): CHECKER_CLASS = NonKeywordArgsChecker Чекер, который тестируем A тесты
написать?
1. Создаем тестовую AST-ноду node = astroid.extract_node( "get_offers(3, 'magic', 'args')"
) А тесты написать?
2. Проверяем что Pylint бросает предупреждение with self.assertAddsMessages(message): self.checker.visit_call(node) А
тесты написать?
Еще один тип чекера - TokenChecker Под капотом - tokenize,
лексический сканер
Лексический анализ кода NAME NAME OP NAME OP NAME OP
OP NEWLINE def sum(a, b): INDENT NAME NAME OP NAME NEWLINE return a + b DEDENT ENDMARKER
Как Pylint работает с TokenChecker 1. Токенизирует модуль 2. Для
всех чекеров с интерфейсом ITokenChecker вызывает: process_tokens(tokens)
Применение TokenChecker 1. Проверка правописания 2. Проверка отступов 3. Работа
со строками. ...
Хочу все знать github.com/PyCQA/Pylint
Выводы Чего добились: • Переложили рутинные проверки на линтер •
Уменьшили количество отклоненных пулл-реквестов • Повысили качество ревью
Максим Мазаев
[email protected]
facebook.com/mazaev.maksim