Если вы следили за предыдущим блогом, мы обсудили, как можно реализовать новый бэкенд рендеринга на основе ‹canvas› для библиотеки matplotlib. Это потребовало повторной реализации некоторых функций Python из API элемента ‹canvas›. Может возникнуть вопрос, как функции тега ‹canvas› могут быть вызваны внутри скрипта Python — что на первый взгляд кажется немного странным и невыполнимым. Что ж, Pyodide здесь, чтобы спасти положение.

Цитата о Pyodide из официального блога здесь:

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

Настоящая сила заключается в его способности взаимодействовать с API браузера и другими библиотеками JavaScript на очень тонком уровне.

WebAssembly был разработан для простого взаимодействия с JavaScript, запущенным в браузере.

Поскольку мы скомпилировали интерпретатор Python в WebAssembly, он также имеет глубокую интеграцию со стороной JavaScript.

Звучит круто, не так ли? Достаточно подробностей, давайте сразу перейдем к использованию и некоторым примерам.

DOM из Python

Большая часть веб-API — это документ и объект window. Доступ к ним можно получить со стороны Python с помощью следующего фрагмента кода.

from js import document, window

Это еще не все, другие вещи, такие как XMLHttpRequest, и даже сторонние библиотеки (конечно, после их загрузки) также могут быть импортированы.

Поэтому, если вы получите библиотеку p5.js, вы также можете получить к ней доступ из python.

Это также напоминает мне, что вы, ребята, должны проверить https://luxapodular.github.io/Py5.js/, который:

Браузерная версия Python p5.js на основе Pyodide

from js import XMLHttpRequest, p5

Сладкий! не так ли?

Это также позволяет нам использовать функции, связанные с объектом document — это обеспечивает доступ к тегу ‹canvas› вместе с его 2D-контекст и все связанные с ним функции.

canvas_element = document.getElementById("canvas")
ctx = canvas_element.getContext("2d")
# all functions of the 2D context can now be called from Python
# so stuff shown below can be done without significant effort
# from a python script!
ctx.moveTo(0, 0)
ctx.lineTo(200, 100)
ctx.stroke()

График из средства визуализации на основе холста

Время для быстрой викторины!

Какой из двух графиков, показанных ниже, обрабатывается средством визуализации Agg по умолчанию, а какой — нашим собственным средством визуализации на основе ‹canvas›?

Подсказка. Ответ заключается в незаметном присутствии/отсутствии маркировки осей на графике. Решение также связано с моим текущим прогрессом в проекте.

Решение. Итак, второе изображение было обработано с помощью пользовательского средства визуализации на основе ‹canvas›, а первое изображение обработано по умолчанию. Аггбэкенд. Основная причина различия заключается в том, что я до сих пор не реализовал функцию draw_text(), которая отвечает за обработку текста на графиках :P

Но здесь мы заметили отличное наблюдение: если не считать маркировки осей, графики выглядят поразительно похожими. Это не шокирует и на самом деле является следствием 3-уровневой структуры matplotlib. Это демонстрирует, как переписывание Backend слоя и подключение его к Artist Layer волшебным образом справляется со всеми остальными задачами.

Тестирование инфраструктуры

Что ж, нам удалось реализовать новый рендерер на основе холста для библиотеки matplotlib (еще не все поддерживается, но опять же, мы только начали, не так ли?), который можно использовать в Интернете — теперь matplotlib работает в браузере по умолчанию! **похлопывает себя по спине**

За исключением одной вещи, о которой нам нужно позаботиться. **О боже, пусть это не тестирование**, но это действительно тестирование :(

Оказывается, визуальный осмотр каждого участка занимает много времени и неэффективен. Нам нужна тестовая инфраструктура, которая может автоматически проверять, действительно ли генерируемые нами графики являются теми графиками, которые мы ДОЛЖНЫгенерировать.

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

1. Запустите веб-драйвер Firefox или веб-драйвер Chrome в автономном режиме (без графического интерфейса). Настройте браузер на основе селена для загрузки простого HTML-файла, который также содержит скрипт для загрузки локальной копии Pyodide.

2. Нарисуйте пример рисунка, используя matplotlib (с нашим собственным средством визуализации на основе ‹canvas›) из Pyodide. Как только график был сгенерирован, это означает, что в DOM теперь существует фактический тег «canvas».

3. Сохраните содержимое тега ‹canvas› в файле PNG. Этот файл является нашим эталонным изображением, которое мы можем использовать для визуальной проверки.

4. Получить базовые данные о пикселях из элемента ‹canvas›. Их можно сохранить в виде массива numpy. Это необработанные значения пикселей, которые нельзя использовать для визуального контроля.

5. Отправьте запрос на получение базовых данных эталонного изображения PNG, которое мы сохранили ранее. Это можно сделать с помощью XMLHttpRequest и в том же скрипте Python (спасибо Pyodide!)

6. Сравните данные, полученные из PNG, с данными, полученными непосредственно из тега ‹canvas›. Они должны быть очень похожими, т. Е. Если среднее значение абсолютных отклонений для всех пикселей находится в пределах определенного порога, мы говорим, что 2 изображения похожи. Кроме того, сохранение файлов PNG позволяет внешнему пользователю также визуально проверять графики.

Это завершает часть для тестирования (которая, кстати, еще не объединена). Но, по крайней мере, мы могли бы найти способ автоматизировать все это.

Это как бы завершает первую фазу GSoC. Если кому-то интересно погрузиться в кодовую базу того, как все это делается. не стесняйтесь просматривать PR в Репозиторий Pyodide с пометкой GSoC 2019.

Прощай, амигос!