SlideShare a Scribd company logo

Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Я работаю над продуктом Max Patrol компании Positive Technologies. Кодовая база нашего проекта насчитывает более 50 тысяч строк кода. Без хороших тестов работа с таким объемом кода превратилась бы в кошмар. Многие программисты стремятся к 100% покрытию кода тестами и считают, что это избавит их от множества проблем. Я расскажу о том, с какими трудностями мы столкнулись и почему заветные 100% ничего не говорят о покрытии тестируемого кода. Я приведу примеры кода и тестов, которые показывают 100% покрытие и покажу почему это не так. Я рассмотрю как работает библиотека coverage.py и объясню почему не стоит слепо верить результатам ее работы. Так же я поделюсь идеей получения честной метрики покрытия кода тестами и представлю прототип библиотеки, в которую воплотилась эта идея.

1 of 96
Download to read offline
Цыганов Иван
Positive Technologies
Почему 100% покрытие
это плохо
Обо мне
✤ Спикер PyCon Russia 2016,
PiterPy, PyCon Siberia 2016
✤ Люблю OpenSource
✤ Не умею frontend
✤ 15 лет практического опыта на рынке ИБ
✤ Более 650 сотрудников в 9 странах
✤ Каждый год находим более 200 уязвимостей
нулевого дня
✤ Проводим более 200 аудитов безопасности в
крупнейших компаниях мира ежегодно
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
MaxPatrol
✤ Pentest. Тестирование на проникновение.
✤ Audit. Системные проверки.
✤ Compliance. Соответствие стандартам.
✤ Одна из крупнейших баз знаний в мире
Система контроля защищенности и соответствия
стандартам.
✤ Тестирование на проникновение (Pentest)
✤ Системные проверки (Audit)
✤ Соответствие стандартам (Compliance)
✤ Одна из крупнейших баз знаний в мире
Система контроля защищенности и соответствия
стандартам.
✤ Системные проверки (Audit)
MaxPatrol
Ad

Recommended

Как команда PVS-Studio может улучшить код операционной системы Tizen
Как команда PVS-Studio может улучшить код операционной системы TizenКак команда PVS-Studio может улучшить код операционной системы Tizen
Как команда PVS-Studio может улучшить код операционной системы TizenAndrey Karpov
 
Статический анализ как ответ на вопрос о повышении качества кода
Статический анализ как ответ на вопрос о повышении качества кодаСтатический анализ как ответ на вопрос о повышении качества кода
Статический анализ как ответ на вопрос о повышении качества кодаAndrey Karpov
 
Easy authcache 2 кеширование для pro родионов игорь
Easy authcache 2   кеширование для pro родионов игорьEasy authcache 2   кеширование для pro родионов игорь
Easy authcache 2 кеширование для pro родионов игорьdrupalconf
 
DevPoint 2016: Признаки плохого кода и как с ним бороться в PHP проектах - Па...
DevPoint 2016: Признаки плохого кода и как с ним бороться в PHP проектах - Па...DevPoint 2016: Признаки плохого кода и как с ним бороться в PHP проектах - Па...
DevPoint 2016: Признаки плохого кода и как с ним бороться в PHP проектах - Па...DevPoint Kyiv
 
Техники пентеста для активной защиты - Николай Овчарук
Техники пентеста для активной защиты - Николай ОвчарукТехники пентеста для активной защиты - Николай Овчарук
Техники пентеста для активной защиты - Николай ОвчарукHackIT Ukraine
 
С чего начать свой путь этичного хакера? - Вадим Чакрян
С чего начать свой путь этичного хакера? - Вадим ЧакрянС чего начать свой путь этичного хакера? - Вадим Чакрян
С чего начать свой путь этичного хакера? - Вадим ЧакрянHackIT Ukraine
 

More Related Content

Viewers also liked

Урок 8. Модели покупки рекламы
Урок 8. Модели покупки рекламыУрок 8. Модели покупки рекламы
Урок 8. Модели покупки рекламыMobio
 
CLASSWORK-11 BY 17 BOTTOM TITLE
CLASSWORK-11 BY 17 BOTTOM TITLECLASSWORK-11 BY 17 BOTTOM TITLE
CLASSWORK-11 BY 17 BOTTOM TITLETimothy Loepp
 
9 Урок. Заключительный урок введения
9 Урок. Заключительный урок введения9 Урок. Заключительный урок введения
9 Урок. Заключительный урок введенияMobio
 
PyCon Siberia 2016. Не доверяйте тестам!
PyCon Siberia 2016. Не доверяйте тестам!PyCon Siberia 2016. Не доверяйте тестам!
PyCon Siberia 2016. Не доверяйте тестам!Ivan Tsyganov
 
Константин Бажин, ТОП 10 не могу или что нужно сделать, чтобы жить по Agile
Константин Бажин, ТОП 10 не могу или что нужно сделать, чтобы жить по AgileКонстантин Бажин, ТОП 10 не могу или что нужно сделать, чтобы жить по Agile
Константин Бажин, ТОП 10 не могу или что нужно сделать, чтобы жить по AgileScrumTrek
 
Подарки на Новый год 2016 - Вкусная помощь
Подарки на Новый год 2016 - Вкусная помощьПодарки на Новый год 2016 - Вкусная помощь
Подарки на Новый год 2016 - Вкусная помощьВкусная помощь
 
Модель системы Continuous Integration в компании Positive Technologies | Тиму...
Модель системы Continuous Integration в компании Positive Technologies | Тиму...Модель системы Continuous Integration в компании Positive Technologies | Тиму...
Модель системы Continuous Integration в компании Positive Technologies | Тиму...Positive Hack Days
 
новости. 10 11 сентября. - акция - безопасность детей - забота родителей.
новости. 10 11 сентября. - акция - безопасность детей - забота родителей.новости. 10 11 сентября. - акция - безопасность детей - забота родителей.
новости. 10 11 сентября. - акция - безопасность детей - забота родителей.virtualtaganrog
 
новости. 25 сентября день воспитателя и всех дошкольных работников.
новости. 25 сентября   день воспитателя и всех дошкольных работников.новости. 25 сентября   день воспитателя и всех дошкольных работников.
новости. 25 сентября день воспитателя и всех дошкольных работников.virtualtaganrog
 
Положення про членство в ГЛК
Положення про членство в ГЛК Положення про членство в ГЛК
Положення про членство в ГЛК Seva Borovets
 
A hybrid cloud approach for secure authorized
A hybrid cloud approach for secure authorizedA hybrid cloud approach for secure authorized
A hybrid cloud approach for secure authorizedNinad Samel
 
Innovative Solutions for AREA Surveillance & Intrusion Detection
Innovative Solutions for AREA Surveillance & Intrusion DetectionInnovative Solutions for AREA Surveillance & Intrusion Detection
Innovative Solutions for AREA Surveillance & Intrusion DetectionTristan Wiggill
 
Intership Letter - Abhishek Pareek
Intership Letter - Abhishek PareekIntership Letter - Abhishek Pareek
Intership Letter - Abhishek PareekAbhishek Pareek
 
Общая концепция системы развёртывания серверного окружения на базе SaltStack ...
Общая концепция системы развёртывания серверного окружения на базе SaltStack ...Общая концепция системы развёртывания серверного окружения на базе SaltStack ...
Общая концепция системы развёртывания серверного окружения на базе SaltStack ...Positive Hack Days
 

Viewers also liked (20)

Урок 8. Модели покупки рекламы
Урок 8. Модели покупки рекламыУрок 8. Модели покупки рекламы
Урок 8. Модели покупки рекламы
 
CLASSWORK-11 BY 17 BOTTOM TITLE
CLASSWORK-11 BY 17 BOTTOM TITLECLASSWORK-11 BY 17 BOTTOM TITLE
CLASSWORK-11 BY 17 BOTTOM TITLE
 
9 Урок. Заключительный урок введения
9 Урок. Заключительный урок введения9 Урок. Заключительный урок введения
9 Урок. Заключительный урок введения
 
Adobe Media server family
Adobe Media server familyAdobe Media server family
Adobe Media server family
 
PyCon Siberia 2016. Не доверяйте тестам!
PyCon Siberia 2016. Не доверяйте тестам!PyCon Siberia 2016. Не доверяйте тестам!
PyCon Siberia 2016. Не доверяйте тестам!
 
Константин Бажин, ТОП 10 не могу или что нужно сделать, чтобы жить по Agile
Константин Бажин, ТОП 10 не могу или что нужно сделать, чтобы жить по AgileКонстантин Бажин, ТОП 10 не могу или что нужно сделать, чтобы жить по Agile
Константин Бажин, ТОП 10 не могу или что нужно сделать, чтобы жить по Agile
 
Подарки на Новый год 2016 - Вкусная помощь
Подарки на Новый год 2016 - Вкусная помощьПодарки на Новый год 2016 - Вкусная помощь
Подарки на Новый год 2016 - Вкусная помощь
 
Модель системы Continuous Integration в компании Positive Technologies | Тиму...
Модель системы Continuous Integration в компании Positive Technologies | Тиму...Модель системы Continuous Integration в компании Positive Technologies | Тиму...
Модель системы Continuous Integration в компании Positive Technologies | Тиму...
 
новости. 10 11 сентября. - акция - безопасность детей - забота родителей.
новости. 10 11 сентября. - акция - безопасность детей - забота родителей.новости. 10 11 сентября. - акция - безопасность детей - забота родителей.
новости. 10 11 сентября. - акция - безопасность детей - забота родителей.
 
новости. 25 сентября день воспитателя и всех дошкольных работников.
новости. 25 сентября   день воспитателя и всех дошкольных работников.новости. 25 сентября   день воспитателя и всех дошкольных работников.
новости. 25 сентября день воспитателя и всех дошкольных работников.
 
MS Degree
MS DegreeMS Degree
MS Degree
 
Kick off febr 2016
Kick off febr 2016Kick off febr 2016
Kick off febr 2016
 
Canales max min 20121202
Canales max min 20121202Canales max min 20121202
Canales max min 20121202
 
Положення про членство в ГЛК
Положення про членство в ГЛК Положення про членство в ГЛК
Положення про членство в ГЛК
 
Untitled Presentation
Untitled PresentationUntitled Presentation
Untitled Presentation
 
A hybrid cloud approach for secure authorized
A hybrid cloud approach for secure authorizedA hybrid cloud approach for secure authorized
A hybrid cloud approach for secure authorized
 
Innovative Solutions for AREA Surveillance & Intrusion Detection
Innovative Solutions for AREA Surveillance & Intrusion DetectionInnovative Solutions for AREA Surveillance & Intrusion Detection
Innovative Solutions for AREA Surveillance & Intrusion Detection
 
Presentasjon fra Ole Sejer Iversen - forsker ved Aarhus Universitet
Presentasjon fra Ole Sejer Iversen - forsker ved Aarhus UniversitetPresentasjon fra Ole Sejer Iversen - forsker ved Aarhus Universitet
Presentasjon fra Ole Sejer Iversen - forsker ved Aarhus Universitet
 
Intership Letter - Abhishek Pareek
Intership Letter - Abhishek PareekIntership Letter - Abhishek Pareek
Intership Letter - Abhishek Pareek
 
Общая концепция системы развёртывания серверного окружения на базе SaltStack ...
Общая концепция системы развёртывания серверного окружения на базе SaltStack ...Общая концепция системы развёртывания серверного окружения на базе SaltStack ...
Общая концепция системы развёртывания серверного окружения на базе SaltStack ...
 

Similar to Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Easy authcache 2 кэширование для pro. Родионов Игорь
Easy authcache 2   кэширование для pro. Родионов ИгорьEasy authcache 2   кэширование для pro. Родионов Игорь
Easy authcache 2 кэширование для pro. Родионов ИгорьPVasili
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода Pavel Tsukanov
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кодаAndrey Karpov
 
Автоматизация задач с помощью EEM
Автоматизация задач с помощью EEMАвтоматизация задач с помощью EEM
Автоматизация задач с помощью EEMCisco Russia
 
Инструментируй это
Инструментируй этоИнструментируй это
Инструментируй этоRoman Dvornov
 
«Статический анализ: гордость и предубеждения», Алексей Кузьменко, аналитик И...
«Статический анализ: гордость и предубеждения», Алексей Кузьменко, аналитик И...«Статический анализ: гордость и предубеждения», Алексей Кузьменко, аналитик И...
«Статический анализ: гордость и предубеждения», Алексей Кузьменко, аналитик И...Mail.ru Group
 
Поговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложенийПоговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложенийAndrey Akinshin
 
Превышаем скоросные лимиты с Angular 2
Превышаем скоросные лимиты с Angular 2Превышаем скоросные лимиты с Angular 2
Превышаем скоросные лимиты с Angular 2Oleksii Okhrymenko
 
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полнойОмские ИТ-субботники
 
разработка бизнес приложений (8)
разработка бизнес приложений (8)разработка бизнес приложений (8)
разработка бизнес приложений (8)Alexander Gornik
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioAndrey Karpov
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Andrey Karpov
 
automation is iOS development
automation is iOS developmentautomation is iOS development
automation is iOS developmentIvan Trifonov
 
Алексей Ильенко "In real-time with Apache Kafka"
Алексей Ильенко "In real-time with Apache Kafka"Алексей Ильенко "In real-time with Apache Kafka"
Алексей Ильенко "In real-time with Apache Kafka"Fwdays
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыcorehard_by
 
Александр Коротин. Безопасность систем управления турбинами в электроэнергетике
Александр Коротин. Безопасность систем управления турбинами в электроэнергетикеАлександр Коротин. Безопасность систем управления турбинами в электроэнергетике
Александр Коротин. Безопасность систем управления турбинами в электроэнергетикеKaspersky
 
static - defcon russia 20
static  - defcon russia 20static  - defcon russia 20
static - defcon russia 20DefconRussia
 
КРИ 2008. Проектирование игр: функциональный подход
КРИ 2008. Проектирование игр: функциональный подходКРИ 2008. Проектирование игр: функциональный подход
КРИ 2008. Проектирование игр: функциональный подходKirill Lebedev
 

Similar to Moscow Python Conf 2016. Почему 100% покрытие это плохо? (20)

Easy authcache 2 кэширование для pro. Родионов Игорь
Easy authcache 2   кэширование для pro. Родионов ИгорьEasy authcache 2   кэширование для pro. Родионов Игорь
Easy authcache 2 кэширование для pro. Родионов Игорь
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Автоматизация задач с помощью EEM
Автоматизация задач с помощью EEMАвтоматизация задач с помощью EEM
Автоматизация задач с помощью EEM
 
Инструментируй это
Инструментируй этоИнструментируй это
Инструментируй это
 
«Статический анализ: гордость и предубеждения», Алексей Кузьменко, аналитик И...
«Статический анализ: гордость и предубеждения», Алексей Кузьменко, аналитик И...«Статический анализ: гордость и предубеждения», Алексей Кузьменко, аналитик И...
«Статический анализ: гордость и предубеждения», Алексей Кузьменко, аналитик И...
 
Поговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложенийПоговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложений
 
Превышаем скоросные лимиты с Angular 2
Превышаем скоросные лимиты с Angular 2Превышаем скоросные лимиты с Angular 2
Превышаем скоросные лимиты с Angular 2
 
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
 
разработка бизнес приложений (8)
разработка бизнес приложений (8)разработка бизнес приложений (8)
разработка бизнес приложений (8)
 
Спецификация WSGI (PEP-333)
Спецификация WSGI (PEP-333)Спецификация WSGI (PEP-333)
Спецификация WSGI (PEP-333)
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?
 
automation is iOS development
automation is iOS developmentautomation is iOS development
automation is iOS development
 
Алексей Ильенко "In real-time with Apache Kafka"
Алексей Ильенко "In real-time with Apache Kafka"Алексей Ильенко "In real-time with Apache Kafka"
Алексей Ильенко "In real-time with Apache Kafka"
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтеры
 
Александр Коротин. Безопасность систем управления турбинами в электроэнергетике
Александр Коротин. Безопасность систем управления турбинами в электроэнергетикеАлександр Коротин. Безопасность систем управления турбинами в электроэнергетике
Александр Коротин. Безопасность систем управления турбинами в электроэнергетике
 
static - defcon russia 20
static  - defcon russia 20static  - defcon russia 20
static - defcon russia 20
 
Python и Cython
Python и CythonPython и Cython
Python и Cython
 
КРИ 2008. Проектирование игр: функциональный подход
КРИ 2008. Проектирование игр: функциональный подходКРИ 2008. Проектирование игр: функциональный подход
КРИ 2008. Проектирование игр: функциональный подход
 

Moscow Python Conf 2016. Почему 100% покрытие это плохо?

  • 1. Цыганов Иван Positive Technologies Почему 100% покрытие это плохо
  • 2. Обо мне ✤ Спикер PyCon Russia 2016, PiterPy, PyCon Siberia 2016 ✤ Люблю OpenSource ✤ Не умею frontend
  • 3. ✤ 15 лет практического опыта на рынке ИБ ✤ Более 650 сотрудников в 9 странах ✤ Каждый год находим более 200 уязвимостей нулевого дня ✤ Проводим более 200 аудитов безопасности в крупнейших компаниях мира ежегодно
  • 5. MaxPatrol ✤ Pentest. Тестирование на проникновение. ✤ Audit. Системные проверки. ✤ Compliance. Соответствие стандартам. ✤ Одна из крупнейших баз знаний в мире Система контроля защищенности и соответствия стандартам.
  • 6. ✤ Тестирование на проникновение (Pentest) ✤ Системные проверки (Audit) ✤ Соответствие стандартам (Compliance) ✤ Одна из крупнейших баз знаний в мире Система контроля защищенности и соответствия стандартам. ✤ Системные проверки (Audit) MaxPatrol
  • 7. > 50 000 строк кода
  • 8. Зачем мы тестируем? ✤ Уверенность, что написанный код работает ✤ Ревью кода становится проще ✤ Гарантия, что ничего не сломалось при изменениях
  • 9. Зачем проверять покрытие? ✤ Видно какой именно код протестирован ✤ Позволяет увидеть все ветви исполнения
  • 10. Зачем проверять покрытие? ✤ Видно какой именно код протестирован ✤ Позволяет увидеть все ветви исполнения ✤ Метрика качества тестов (?)
  • 11. Зачем нам 100%? ✤ Ачивка «У нас в проекте 100% coverage»
  • 12. Зачем нам 100%? ✤ Ачивка «У нас в проекте 100% coverage» ✤ Уверенность, что код протестирован полностью
  • 13. 100% coverage != 100% протестировано
  • 14. coverage.py ✤ Позволяет проверить покрытие кода тестами ✤ Есть плагин для pytest ✤ В основном работает
  • 15. coverage.py def get_longest(a, b): if len(a) > len(b): return a return b assert get_longest([1,2,3], [4,5]) == [1,2,3] assert get_longest([1,2], [3,4,5]) == [3,4,5] 
  • 16. coverage.py def apply_discount(prices): result = {'Total': sum(prices)} if result['Total'] >= 1000: result['Discount'] = result['Total'] * 0.25 return result['Total'] - result['Discount']   assert apply_discount([400, 600]) == 750 Name Stmts Miss Cover Missing ---------------------------------------------------------- samples/apply_discount.py 5 0 100%
  • 17. coverage.py def apply_discount(prices): result = {'Total': sum(prices)} if result['Total'] >= 1000: result['Discount'] = result['Total'] * 0.25 return result['Total'] - result['Discount']   assert apply_discount([400, 600]) == 750 >>> apply_discount([200])
  • 18. coverage.py def apply_discount(prices): result = {'Total': sum(prices)} if result['Total'] >= 1000: result['Discount'] = result['Total'] * 0.25 return result['Total'] - result['Discount']   assert apply_discount([400, 600]) == 750 >>> apply_discount([200]) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in apply_discount KeyError: 'Discount'
  • 19. coverage.py --branch 1 def apply_discount(prices): 2 result = {'Total': sum(prices)} 3 if result['Total'] >= 1000: 4 result['Discount'] = result['Total'] * 0.25 5 return result['Total'] - result['Discount'] 6   7 assert apply_discount([400, 600]) == 750 Name Stmts Miss Branch BrPart Cover Missing --------------------------------------------------------------------- samples/apply_discount.py 5 0 2 1 85.71% 3 ->5
  • 20. coverage.py --branch 1 def apply_discount(prices): 2 result = {'Total': sum(prices)} 3 if result['Total'] >= 1000: 4 result['Discount'] = result['Total'] * 0.25 5 return result['Total'] - result['Discount'] 6   7 assert apply_discount([400, 600]) == 750 Name Stmts Miss Branch BrPart Cover Missing --------------------------------------------------------------------- samples/apply_discount.py 5 0 2 1 85.71% 3 ->5
  • 21. Как считать покрытие? Все строки Реально выполненные строки- Непокрытые строки=
  • 23. coverage.parser.PythonParser ✤ Обходит все токены и отмечает «интересные» факты ✤ Компилирует код. Обходит code-object и сохраняет номера строк
  • 24. Обход токенов ✤ Запоминает определения классов ✤ «Сворачивает» многострочные выражения ✤ Исключает комментарии
  • 25. Обход байткода ✤ Полностью повторяет метод dis.findlinestarts ✤ Анализирует code_obj.co_lnotab ✤ Генерирует пару (номер байткода, номер строки)
  • 26. Как считать coverage --branch? Все переходы Реально выполненные переходы- Непокрытые переходы=
  • 28. coverage.parser.AstArcAnalyzer ✤ Обходит AST с корневой ноды ✤ Обрабатывает отдельно каждый тип нод отдельно
  • 29. Обработка ноды class While(stmt): _fields = ( 'test', 'body', 'orelse', ) while i<10: print(i) i += 1
  • 30. Обработка ноды class While(stmt): _fields = ( 'test', 'body', 'orelse', ) while i<10: print(i) i += 1 else: print('All done')
  • 31. Выполненные строки sys.settrace(tracefunc) Set the system’s trace function, which allows you to implement a Python source code debugger in Python. Trace functions should have three arguments: frame, event, and arg. frame is the current stack frame. event is a string: 'call', 'line', 'return', 'exception', 'c_call', 'c_return', or 'c_exception'. arg depends on the event type.
  • 32. PyTracer «call» event ✤ Сохраняем данные предыдущего контекста ✤ Начинаем собирать данные нового контекста ✤ Учитываем особенности генераторов
  • 33. PyTracer «line» event ✤ Запоминаем выполняемую строку ✤ Запоминаем переход между строками
  • 34. PyTracer «return» event ✤ Отмечаем выход из контекста ✤ Помним о том, что yield это тоже return
  • 35. Отчет ✤ Что выполнялось ✤ Что должно было выполниться ✤ Ругаемся
  • 36. Зачем такие сложности? 1 for i in some_list: 2 if i == 'Hello': 3 print(i + ' World!') 4 elif i == 'Skip': 5 continue 6 else: 7 break 8 else: 9 print(r'¯_(ツ)_/¯')
  • 39. Что может пойти не так? def make_user(name, email): return dict( ID=get_id(), Name=name, Role='Editor' if check_employee(email) else 'Guest' )  
  • 40. Что может пойти не так? def positive_squares(items): return map(lambda x: x **2 if x>0 else x, items)
  • 41. Что может пойти не так? def positive_squares(items): return [ item **2 for item in items if item>0 ]
  • 42. Что может пойти не так? def positive_squares(items): return [ item **2 for item in items if item>0 ] def positive_squares(items): return map(lambda x: x **2 if x>0 else x, items) def make_user(name, email): return dict( ID=get_id(), Name=name, Role='Editor' if check_employee(email) else 'Guest' )  
  • 43. Что может пойти не так? def positive_squares(items): return [ item **2 for item in items if item>0 ] def positive_squares(items): return map(lambda x: x **2 if x>0 else x, items) def make_user(name, email): return dict( ID=get_id(), Name=name, Role='Editor' if check_employee(email) else 'Guest' )  
  • 45. Непокрываемый код def some_method(a, b, c): if a and b or c: return True return False
  • 46. sys.settrace(tracefunc) ✤ Устанавливаем свою функцию трассировки ✤ Смотрим что происходит и делаем выводы
  • 49. ast.NodeTransformer ✤ Обходим ноды ✤ Оборачиваем в «нечто» каждую ноду ✤ Запускаем и отслеживаем что выполнялось
  • 50. ast.NodeTransformer ✤ Сложно обернуть код, не изменив логику ✤ Не все ноды можно обернуть
  • 51. ast.NodeTransformer ✤ Сложно обернуть код, не изменив логику ✤ Не все ноды можно обернуть
  • 52. Идея ✤ Перехватить контроль во время импорта ✤ Обойти байткод модуля ✤ Добавить вызов функции ✤ Собрать code-object
  • 53. Идея ✤ Перехватить контроль во время импорта ✤ Обойти байткод модуля ✤ Добавить вызов функции ✤ Собрать code-object
  • 55. План ✤ Устанавливаем import hook ✤ Модифицируем и подменяем code-object ✤ Запускаем тесты ✤ Анализируем результаты
  • 56. Import hook. Finder. ✤ Пропускаем неинтересные модули ✤ Создаем свой Loader для нужных модулей
  • 57. Import hook. Loader. ✤ Получаем байт-код модуля ✤ Получаем исходный код модуля ✤ Модифицируем байт-код ✤ Возвращаем измененный байт-код
  • 58. План ✤ Устанавливаем import hook ✤ Модифицируем и подменяем code-object ✤ Запускаем тесты ✤ Анализируем результаты
  • 59. Wrapper. Модифицируем байт-код. # ... wrapper = Wrapper( trace_func=self.make_visitor(module_name), mark_func=self.make_marker(module_name, source) ) new_code = wrapper.wrap_code(code) return new_code # ...
  • 60. Wrapper. Callbacks. def make_marker(self, module, source): self.module_opcodes[module] = FileOpcode(module, source) def mark(codeobj_id, opcode): self.module_opcodes[module].add(codeobj_id, opcode.offset, opcode) return mark   def make_visitor(self, module): def visit(codeobj_id, opcode): self.module_opcodes[module].visit(codeobj_id, opcode.offset, opcode) return visit
  • 61. dis.dis(some_method) def some_method(a, b, c): if a and b or c: return True return False 2 0 LOAD_FAST 0 (a) 3 POP_JUMP_IF_FALSE 18 6 LOAD_FAST 1 (b) 9 POP_JUMP_IF_TRUE 18 12 LOAD_FAST 2 (c) 15 POP_JUMP_IF_FALSE 22 3 >> 18 LOAD_CONST 1 (True) 21 RETURN_VALUE 4 >> 22 LOAD_CONST 2 (False) 25 RETURN_VALUE
  • 62. dis.get_instructions(some_method) def some_method(a, b, c): if a and b or c: return True return False Instruction(opname='LOAD_FAST', opcode=124, arg=0, ...
 Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, ...
 Instruction(opname='LOAD_FAST', opcode=124, arg=1, ...
 Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, ...
 Instruction(opname='LOAD_FAST', opcode=124, arg=2, ...
 Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, ...
 Instruction(opname='LOAD_CONST', opcode=100, arg=1, ...
 Instruction(opname='RETURN_VALUE', opcode=83, arg=None ...
 Instruction(opname='LOAD_CONST', opcode=100, arg=2, ...
 Instruction(opname='RETURN_VALUE', opcode=83, arg=None ...
  • 63. Wrap code. Все опкоды. ✤ Просто вызываем функцию, переданную из Loader’a self.mark(codeobj_id, st)
  • 64. Wrap code. Трассировка. ✤ Добавляем lambda-функцию в константы constants.append( lambda co_id=codeobj_id, opcode=st: self.visit(co_id, opcode) )
  • 65. Wrap code. Трассировка. ✤ Добавляем lambda-функцию в константы constants.append( lambda co_id=codeobj_id, opcode=st: self.visit(co_id, opcode) ) PyCodeObject* PyCode_New( /* ... */ PyObject *code, PyObject *consts, PyObject *names, /* ... */ )
  • 66. Wrap code. Трассировка. ✤ Добавляем lambda-функцию в константы ✤ Добавляем байт-код для вызова def make_trace(self, constant_index): yield opcode.opmap['LOAD_CONST'] yield from self.make_args(constant_index) yield opcode.opmap['CALL_FUNCTION'] yield from self.make_args(0) yield opcode.opmap['POP_TOP']
  • 67. Wrap code. Трассировка. ✤ Добавляем lambda-функцию в константы ✤ Добавляем байт-код для вызова ✤ Не забываем про оригинальный опкод и его параметры!
  • 68. Wrap code. Трассировка. ✤ Добавляем lambda-функцию в константы ✤ Добавляем байт-код для вызова ✤ Не забываем про оригинальный опкод и его параметры! ✤ Учитываем смещение в последующих опкодах
  • 69. Wrap сode. Результат. def some_method(a, b, c): if a and b or c: return True return False 2 0 LOAD_FAST 0 (a) 3 POP_JUMP_IF_FALSE 18 6 LOAD_FAST 1 (b) 9 POP_JUMP_IF_TRUE 18 12 LOAD_FAST 2 (c) 15 POP_JUMP_IF_FALSE 22 3 >> 18 LOAD_CONST 1 (True) 21 RETURN_VALUE 4 >> 22 LOAD_CONST 2 (False) 25 RETURN_VALUE
  • 70. Wrap сode. Результат. 6 LOAD_FAST 1 (b) 9 POP_JUMP_IF_TRUE 18 . . . 3 >> 18 LOAD_CONST 1 (True) 21 RETURN_VALUE def some_method(a, b, c): if a and b or c: return True return False
  • 71. Wrap сode. Результат. 20 LOAD_CONST 5 (<function ...<locals>.<lambda>) 23 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 26 POP_TOP 27 LOAD_FAST 1 (b) 30 LOAD_CONST 6 (<function ...<locals>.<lambda>) 33 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 36 POP_TOP 37 POP_JUMP_IF_TRUE 60 . . . >> 60 LOAD_CONST 9 (<function ...<locals>.<lambda>) 63 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 66 POP_TOP 67 LOAD_CONST 1 (True) 70 LOAD_CONST 10 (<function ...<locals>.<lambda>) 73 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 76 POP_TOP 77 RETURN_VALUE
  • 72. План ✤ Устанавливаем import hook ✤ Модифицируем и подменяем code-object ✤ Запускаем тесты ✤ Анализируем результаты
  • 73. Тестируем. Все опкоды. def some_method(a, b, c): if a and b or c: return True return False   some_method(1, 1, 0) Instruction(opname='LOAD_FAST', opcode=124, arg=0, ...
 Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, ...
 Instruction(opname='LOAD_FAST', opcode=124, arg=1, ...
 Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, ...
 Instruction(opname='LOAD_FAST', opcode=124, arg=2, ...
 Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, ...
 Instruction(opname='LOAD_CONST', opcode=100, arg=1, ...
 Instruction(opname='RETURN_VALUE', opcode=83, arg=None ...
 Instruction(opname='LOAD_CONST', opcode=100, arg=2, ...
 Instruction(opname='RETURN_VALUE', opcode=83, arg=None ...
  • 74. Тестируем. Непокрытые опкоды. def some_method(a, b, c): if a and b or c: return True return False   some_method(1, 1, 0) Instruction(opname='LOAD_FAST', arg=2, argval='c', argrepr='c', offset=12)
 Instruction(opname='POP_JUMP_IF_FALSE', arg=22, argval=22, argrepr='')
 Instruction(opname='LOAD_CONST', arg=2, argval=False, starts_line=3)
 Instruction(opname='RETURN_VALUE', arg=None, argval=None)
  • 75. Тестируем. Непокрытые опкоды. def some_method(a, b, c): if a and b or c: return True return False   some_method(1, 1, 0) Instruction(opname='LOAD_FAST', arg=2, argval='c', argrepr='c', offset=12)
 Instruction(opname='POP_JUMP_IF_FALSE', arg=22, argval=22, argrepr='')
 Instruction(opname='LOAD_CONST', arg=2, argval=False, starts_line=3)
 Instruction(opname='RETURN_VALUE', arg=None, argval=None)
  • 76. План ✤ Устанавливаем import hook ✤ Модифицируем и подменяем code-object ✤ Запускаем тесты ✤ Анализируем результаты
  • 77. Способа однозначно перевести любой опкод к строке кода не существует
  • 78. Способа однозначно перевести любой опкод к строке кода не существует
  • 79. Отчет. Ищем строки. ✤ При обходе сохраняем текущую строку ✤ При выводе опкода выводим текущую строку
  • 80. Отчет. Ищем строки. if a and b or c: Instruction(opname='LOAD_FAST', arg=2, argval='c', argrepr='c', offset=12) if a and b or c: Instruction(opname='POP_JUMP_IF_FALSE', arg=22, argval=22, argrepr='')
 return False Instruction(opname='LOAD_CONST', arg=2, argval=False, starts_line=3) return False
 Instruction(opname='RETURN_VALUE', arg=None, argval=None) ✤ При обходе сохраняем текущую строку ✤ При выводе опкода выводим текущую строку
  • 81. ✤ При обходе сохраняем текущую строку ✤ При выводе опкода выводим текущую строку Отчет. Ищем строки. if a and b or c: Instruction(opname='LOAD_FAST', arg=2, argval='c', argrepr='c', offset=12) if a and b or c: Instruction(opname='POP_JUMP_IF_FALSE', arg=22, argval=22, argrepr='')
 return False Instruction(opname='LOAD_CONST', arg=2, argval=False, starts_line=3) return False
 Instruction(opname='RETURN_VALUE', arg=None, argval=None)
  • 82. Отчет. Позиция в строке. ✤ Строка уже известна ✤ Вычислим позицию в строке для каждого типа опкода
  • 83. Отчет. Позиция в строке. if a and b or c: Instruction( opname='LOAD_FAST', opcode=124, offset=12, starts_line=None, is_jump_target=True, arg=2, argval='c', argrepr=‘c' )
  • 84. Отчет. Позиция в строке. if a and b or c: Instruction( opname='LOAD_FAST', opcode=124, offset=12, starts_line=None, is_jump_target=True, arg=2, argval='c', argrepr=‘c' ) Instruction( opname='POP_JUMP_IF_FALSE', opcode=114, offset=15, starts_line=None, is_jump_target=False, arg=22, argval=22, argrepr='' )
  • 85. Отчет. Позиция в строке. ✤ Покрыв 70 типов опкодов удалось получить отчет ✤ Многие опкоды невозможно покрыть
  • 86. Отчет. Позиция в строке. ✤ Покрыв 70 типов опкодов удалось получить отчет ✤ Многие опкоды невозможно покрыть ----------- Report tests.test_code -------------- 1: if a and b or c: ^ LOAD_FAST 1: if a and b or c: ^^^^^^^^^^^^^^^^ POP_JUMP_IF_FALSE 3: return False ^^^^^ LOAD_CONST 3: return False ^^^^^^^^^^^^ RETURN_VALUE
  • 87. Отчет. Позиция в строке. ----------- Report tests.test_code -------------- 1: if a and b or c: ^ LOAD_FAST 1: if a and b or c: ^^^^^^^^^^^^^^^^ POP_JUMP_IF_FALSE 3: return False ^^^^^ LOAD_CONST 3: return False ^^^^^^^^^^^^ RETURN_VALUE ✤ Покрыв 70 типов опкодов удалось получить отчет ✤ Многие опкоды невозможно покрыть
  • 88. OpTrace. Что не так? ✤ Переменные в отчете не всегда отмечаются правильно ✤ Часть опкодов приходится пропускать ✤ Производительность неизвестна
  • 89. OpTrace. Что так? ✤ Трассировка работает хорошо ✤ Идея имеет право на жизнь
  • 90. OpTrace. Планы. ✤ Услышать мнение и критику сообщества
  • 91. OpTrace. Планы. ✤ Услышать мнение и критику сообщества ✤ Рефакторинг ✤ Тестирование ✤ Работа над улучшением отчета ✤ Плагин для pytest
  • 94. 100% coverage расслабляет команду Библиотеки несовершенны
  • 95. 100% coverage расслабляет команду Библиотеки несовершенны 100% coverage - просто ачивка
  • 96. Спасибо за внимание! Вопросы? mi.0-0.im tsyganov-ivan.com