Pyodide и TypeScript: запускаем Python в браузере
-

Представьте, что вы пишете веб-приложение и вам нужно выполнить сложные расчёты или обработку данных прямо в браузере, без обращения к серверу. Python идеально подходит для таких задач, но как его туда засунуть? Тут на сцену выходит Pyodide - штука, которая позволяет запускать полноценный Python внутри браузера через WebAssembly.
Если вы разработчик, пишущий на TypeScript, то интеграция Pyodide открывает кучу возможностей: обработка данных на клиенте, запуск NumPy и Pandas, автоматизация рутинных операций. Статья расскажет, как это всё настроить и заставить работать, не прибегая к магии и не теряя прическу.
Что такое Pyodide и почему это вообще нужно
Pyodide - это порт Python на WebAssembly, который позволяет запускать Python код прямо в браузере. Звучит нереально? На самом деле это работает благодаря Emscripten, который компилирует код из C в WebAssembly. Pyodide уже идёт с большим стандартным набором библиотек и поддерживает установку чистых Python пакетов через micropip.
Почему это полезно? Во-первых, вы экономите нагрузку на сервер - тяжёлые вычисления делаются на машине пользователя. Во-вторых, пользователь получает результаты быстрее, потому что не нужно ждать ответа от сервера. В-третьих, это просто мощный инструмент для создания интерактивных приложений с поддержкой Python.
Основные возможности Pyodide:
- Запуск любого Python кода в браузере
- Поддержка чистых Python пакетов (NumPy, Pandas, SciPy и другие)
- Двусторонняя интеграция между Python и JavaScript
- Полная поддержка async/await для асинхронных операций
- Доступ ко всем Web API из Python кода
Первые шаги: загружаем Pyodide в браузер
Загрузить Pyodide в проект - это проще простого. Нужно подключить скрипт с CDN и дождаться инициализации. Звучит как две строки кода, и это почти правда, но есть пара нюансов, которые стоит знать с самого начала.
Прежде всего, инициализация Pyodide асинхронная, поэтому вы не можете просто взять и запустить Python код сразу после загрузки скрипта. Нужно дождаться, пока
loadPyodide()выполнится. Это важно, потому что под капотом происходит загрузка и распаковка больших файлов (WebAssembly бинарники, стандартная библиотека Python).Минимальная настройка в HTML:
<!doctype html> <html> <head> <script src="https://cdn.jsdelivr.net/pyodide/v0.29.3/full/pyodide.js"></script> </head> <body> <input id="code" value="sum([1, 2, 3, 4, 5])" /> <button onclick="evaluatePython()">Run</button> <textarea id="output" disabled></textarea> <script> let pyodideReadyPromise = loadPyodide(); async function evaluatePython() { let pyodide = await pyodideReadyPromise; try { let result = pyodide.runPython( document.getElementById("code").value ); document.getElementById("output").value = result; } catch (err) { document.getElementById("output").value = err; } } </script> </body> </html>Видите? Мы создаём промис
pyodideReadyPromiseодин раз при загрузке страницы, а потом просто ждём его перед каждым запуском Python кода. Это гораздо эффективнее, чем инициализировать Pyodide заново каждый раз.Что происходит под капотом:
- Загружается
pyodide.js- обёртка над WebAssembly - Скачиваются
pyodide.asm.jsиpyodide.asm.wasmфайлы - Инициализируется Python интерпретатор
- Становится доступна функция
runPython()
Интеграция с TypeScript: типизируем Pyodide
Если вы уже пишете на TypeScript, то понимаете, что работать без типов - это как ездить вслепую. Pyodide поставляется с TypeScript типами, что уже хорошо, но есть нюанс: типы для Python объектов требуют особого внимания.
Pyodide предоставляет интерфейсы для работы с Python кодом из JavaScript. Есть
PyProxy- обёртка над Python объектом, которая позволяет вызывать методы и получать результаты. ЕстьJsProxy- зеркальное отображение для доступа к JavaScript из Python. Оба работают с типами, но нужно понимать, как они себя ведут.Одна из ключевых фишек в свежих версиях - методы
asJsJson()и планируемыйas_py_json(), которые позволяют конвертировать объекты между языками без заморочек с кодировкой.Установка типов и базовая конфигурация:
Если вы используете npm, просто добавьте Pyodide в проект:
npm install "pyodide@>=0.21.0-alpha.2"А дальше в TypeScript файле:
import { loadPyodide } from 'pyodide'; async function initPyodide() { const pyodide = await loadPyodide(); const result = pyodide.runPython('1 + 1'); console.log(result); // 2 }Типы уже будут подтягиваться автоматически. IDE подскажет вам доступные методы и их сигнатуры. Это намного удобнее, чем работать с
anyили какими-то угадываниями.Часто используемые типы в Pyodide:
PyProxy- Python объект, доступный из JavaScriptJsProxy- JavaScript объект, видимый из PythonPyCallable- Python функция, которую можно вызватьPyDict,PyList- специфичные типы контейнеров
Двусторонняя интеграция: Python вызывает JavaScript
Так, мы научились запускать Python код из браузера. Но что, если нам нужно из Python вызвать JavaScript функцию? Например, нужно обновить DOM, отправить запрос на сервер или использовать браузерное API.
Pyodide позволяет добавлять JavaScript функции в глобальное пространство Python. Это делается через
pyodide.globals.set(). Магия в том, что функции работают как обычно, но можно передавать между языками данные и даже обрабатывать ошибки кроссязычно.Есть важный момент с асинхронностью. Если вы хотите из Python кода вызвать асинхронную JavaScript функцию (например, fetch), нужно использовать
async defи await. Pyodide это поддерживает, но нужно помнить о том, что runPython синхронный, а для асинхронного кода естьrunPythonAsync().Пример: вызов fetch из Python:
from js import fetch, Object from pyodide.ffi import to_js async def python_fetch(url: str, options: dict): response = await fetch(url, to_js(options, dict_converter=Object.fromEntries)) return responseА вот как добавить функцию в Python глобальное пространство из JavaScript:
const pyodide = await loadPyodide(); // Добавляем функцию pyodide.globals.set('alert', alert); pyodide.globals.set('square', (x: number) => x * x); // Вызываем из Python pyodide.runPython('square(5)'); // 25Конверсия типов при передаче данных:
- Python
int
JavaScript number - Python
str
JavaScript string - Python
list
JavaScript Array - Python
dict
JavaScript Object - Python
None
JavaScript undefined
Для сложных объектов используйте
to_js()и получайтеasJsJson()для безопасной конверсии.Node.js и SSR: Pyodide на сервере
А вот интересный момент - Pyodide работает не только в браузере, но и в Node.js. Да, это звучит странно, но работает. Это может быть полезно для тестирования, Server-Side Rendering или просто для того, чтобы иметь одну кодовую базу для фронта и бека.
На Node.js Pyodide работает медленнее, чем встроенный Python, поэтому это не замена нативному Python интерпретатору. Но если вам нужно запустить какой-то специфичный код, который работает в браузере, то это отличный способ тестировать его локально перед развёртыванием.
Установка в Node.js идентична браузеру - через npm, затем просто импортируете и используете. Есть даже REPL для экспериментов прямо в терминале.
Запуск Python в Node.js:
const { loadPyodide } = require('pyodide'); async function runPythonInNode() { const pyodide = await loadPyodide(); const result = await pyodide.runPythonAsync('import numpy as np; np.array([1,2,3])'); console.log(result); } runPythonInNode();Отметим, что в Node.js нужно использовать
runPythonAsync()вместоrunPython(), потому что асинхронность в Node.js работает по-другому.Установка пакетов: micropip и готовые wheels
Стандартная библиотека Python уже идёт с Pyodide, но что, если вам нужен NumPy, Pandas или какой-то другой пакет? Тут в дело вступает micropip - менеджер пакетов для Pyodide.
micropip может установить любой чистый Python пакет (то есть без C расширений). Пакеты с C код вроде NumPy и SciPy уже скомпилированы в WebAssembly и идут в составе Pyodide по умолчанию. Некоторые популярные пакеты предкомпилированы, а остальные можно установить, если у них есть wheel на PyPi.
Процесс установки простой - импортируете micropip, вызываете
install()и ждёте. Это может занять пару секунд, потому что скачиваются файлы, поэтому лучше показать пользователю спиннер или прогресс.Пример установки пакетов:
import micropip await micropip.install('requests') await micropip.install('beautifulsoup4') import requests import bs4Предустановленные пакеты в Pyodide:
- NumPy
- Pandas
- SciPy
- Matplotlib
- scikit-learn
- PIL (Pillow)
- И многие другие
Если пакета нет в списке предустановленных, но он доступен на PyPi как чистый Python, micropip установит его автоматически при первом импорте (если вы предварительно вызовите
micropip.install()).Практический пример: расчётчик с интерфейсом
Давайте соберём всё вместе и создадим простой пример - калькулятор, который принимает выражение Python, выполняет его и выводит результат. Добавим к этому обработку ошибок и TypeScript типизацию.
HTML структура:
<!DOCTYPE html> <html> <head> <script src="https://cdn.jsdelivr.net/pyodide/v0.29.3/full/pyodide.js"></script> <style> body { font-family: sans-serif; padding: 20px; } input, button { padding: 10px; } textarea { width: 100%; height: 200px; margin-top: 10px; } </style> </head> <body> <h1>Python калькулятор в браузере</h1> <input id="expression" placeholder="Введите выражение Python" value="2 ** 10" /> <button onclick="calculate()">Вычислить</button> <textarea id="output" readonly></textarea> <script src="calculator.js"></script> </body> </html>TypeScript код:
import { loadPyodide, PyProxy } from 'pyodide'; let pyodideReady: Promise<any>; async function initPyodide() { pyodideReady = loadPyodide(); const pyodide = await pyodideReady; // Добавляем встроенные функции из браузера pyodide.globals.set('console_log', console.log); // Загружаем NumPy если нужно await pyodide.loadPackage('numpy'); console.log('Pyodide инициализирован'); } async function calculate() { const pyodide = await pyodideReady; const expression = (document.getElementById('expression') as HTMLInputElement).value; const output = document.getElementById('output') as HTMLTextAreaElement; try { output.value = 'Выполняем...\n'; const result = pyodide.runPython(` result = ${expression} print(f'Результат: {result}') result `); output.value += `✓ ${result}\n`; } catch (err) { output.value = `✗ Ошибка: ${err}\n`; } } // Инициализируем при загрузке initPyodide(); (window as any).calculate = calculate;Вот так просто - у вас есть полнофункциональный интерпретатор Python в браузере с TypeScript типизацией.
Откуда бежать после первого запуска
Если вы начали разбираться с Pyodide, то понимаете, что это мощный инструмент, но не панацея. Во-первых, производительность Pyodide медленнее нативного Python примерно в 2-3 раза, потому что это WebAssembly. Для большинства web-приложений этого хватает, но если нужны serious вычисления, можно подумать о Worker API или делегировать часть работы на сервер.
Во-вторых, не все Python библиотеки работают в браузере - только те, которые написаны на чистом Python или уже скомпилированы под WebAssembly. Если вам нужна какая-то специфичная библиотека с C расширениями, придётся либо ждать, пока её скомпилируют для Pyodide, либо искать альтернативу.
А что насчёт будущего? TypeScript интеграция становится всё лучше, генерируются типы для Python стандартной библиотеки, развивается поддержка SSR сценариев. В целом экосистема живая и движется в правильном направлении.
Здравствуйте! Похоже, вас заинтересовала эта беседа, но у вас ещё нет аккаунта.
Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.
С вашими комментариями этот пост мог бы стать ещё лучше 💗
Зарегистрироваться Войти© 2024 - 2026 ExLends, Inc. Все права защищены.