Сбор покрытия Flask (Python) в Runtime

от автора

Всем привет, меня зовут Осипов Станислав. Я занимаюсь AppSec/DevOps с 2021 года. В этой статье я хочу рассказать как можно собрать покрытие Python приложения в runtime незавершая работу приложения.

Соображения Midjourney по поводу статьи

Соображения Midjourney по поводу статьи

Что было использовано в статье:

https://github.com/pallets/flask — Flask 3.03
https://github.com/nedbat/coveragepy — coverage 7.5.1

Подготовка Flask

Выполним установку Flask согласно инструкции:

https://github.com/pallets/flask/blob/main/docs/installation.rst

В директории приложения создадим app.py, взяв за основу пример из репозитория Flask, и добавим пару роутов:

import coverage, hashlib  from flask import Flask  #Регистрация rout'ов def main():    @app.route("/")    def hello():     return "Main route"      @app.route("/1")   def hello1():      return "First route"      @app.route("/2")   def hello2():       return hashlib.sha256(b"Nobody inspects the spammish repetition").hexdigest()    #Запуск веб-сервера   app.run()  #Инициализация app = Flask(__name__)  main()

Подготовка coverage

Установим модуль для сборки покрытия:

pip install coverage

Для отображения покрытия third-party модулей в отчёте закомментим следующие строки:

.venv/lib/python3.11/site-packages/coverage/inorout.py-16478:  if self.third_match.match(filename) and not self.source_in_third_match.match(filename):  .venv/lib/python3.11/site-packages/coverage/inorout.py:16579:   return "inside --source, but is third-party"  

Инструментация кода

Согласно https://coverage.readthedocs.io/en/7.5.1/api.html сбор покрытия осуществляется следующим образом:

import coverage  cov = coverage.Coverage() #Инициализация модуля cov.start() # Запуск сбора покрытия  # .. call your code ..  cov.stop() # Остановить сбор покрытия - в нашем случае является избыточным cov.save() # Сохранить покрытие  cov.html_report() # Сгенерировать отчет в формате .html

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

Для этого в файле выполним следующие изменения:

.venv/lib/python3.11/site-packages/flask/app.py

Подключим модуль coverage указав флаги:
cover_pylib — отображать покрытие стандартых модулей Python
auto_data — продолжать запись покрытия в один файл
source — список директорий исходного кода, которые будут учитываться в отчете

Подключения модуля coverage

13a14  > import coverage

Встраивание модуля покрытия

Добавим инициализацию модуля coverage в аттрибуте класса Flask и метод запуска сбора покрытия:

253c253,254 #Добавляем инициализацию сбора покрытия в объект класса Flask и запуск сбора покрытия в метод "run" класса Flask <  --- >         self.cov = coverage.Coverage(cover_pylib=True, auto_data=True, source=["./", ".venv"]) >         self.cov.start()

Поиск метода для сохранения покрытия

Найдем метод, который будет вызываться при обработке запроса.
В случае Flask я выбрал метод make_response, который отвечает за отправку ответа на запорс.
В методе этом будем сохранять/обновлять покрытие

1230c1229,1230  <  --- >         self.cov.save() >         self.cov.html_report()  

Проверяем сбор покрытия в Runtime

  1. Запустим приложение: python3 app.py

  2. В браузере перейдем по адресу, содержащий main route — 127.0.0.1:5000

  3. Откроем страницу, содержащую покрытие — firefox htmlcov/index.html

Результат сбора покрытия

Результат сбора покрытия

Покрытие: Составило 11% вместе исходным кодом сторонних зависимостей.

  1. В браузере перейдем по адресу, содержащий второй route — 127.0.0.1:5000/2

  2. Обновим страницу, содержащую покрытие

Результат сбора покрытия

Результат сбора покрытия

Покрытие: Увеличилось на 5 процентов после обращения к 2 rout’у.

Итоги

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

Благодарю за внимание!
Предложения, вопросы, замечания, конструктивная критика приветствуется в комментариях.


ссылка на оригинал статьи https://habr.com/ru/articles/812861/