Pyodideは今どれくらいできるのか

## Pyodide
Pyodideは、CPythonをWebAssembly/Emscriptenに移植したものです。ブラウザ向けのPythonランタイムとも言えます。

[2019年にMozillaが発表](https://hacks.mozilla.org/2019/04/pyodide-bringing-the-scientific-python-stack-to-the-browser/)し、[2021年に独立したプロジェクトになりました](https://hacks.mozilla.org/2021/04/pyodide-spin-out-and-0-17-release/)。

https://pyodide.org/en/stable/

### 発音
- https://github.com/pyodide/pyodide/discussions/1954
- https://www.youtube.com/watch?v=iUqVgykaF-k

以上を参考にカタカナにすると「パイアダイド」？

### Pyodideを使う目的
RustやGoなどソースコードをコンパイルしてWebAssemblyバイナリを生成するものは高いパフォーマンスが期待できます。
一方でPyodideはコンパイルをせず実行環境をWebAssemblyに持ってきている方式なので、パフォーマンス面の恩恵は大きくないと思われます。PythonのコードやライブラリをWebフロントエンドに持ってきて、モバイルなど様々な環境でもサーバを使わずに動かせるという点が長所になると思います。

特にPythonはデータサイエンスや科学技術計算まわりでよく使われますが、そういった資産をそのままWebフロントエンドに持ってくるのがPyodideの主な目的のひとつになると思います。

### Pyodideの試す
[Web上にコンソール](https://pyodide.org/en/stable/console.html)があり、いきなり試せます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50391/61c15a10-768c-f793-b777-c13f86d672ea.png)

## Pyodideでコードを実装する
### とりあえず動かす

[公式サイトのサンプル](https://pyodide.org/en/stable/usage/quickstart.html#quickstart)をもとに、こんなHTMLを書きます。

```html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script>
  </head>

  <body>
    <script>
      async function main() {
        let pyodide = await loadPyodide();
        const code = `
from datetime import datetime
now = datetime.now()
now.strftime("ただいま%Y年%m月%d日 %H時%M分%S秒です")
        `;
        window.alert(pyodide.runPython(code));
      };

      main();
    </script>
  </body>
</html>
```

`code`変数のテンプレートリテラルに文字列でPythonソースコードが入っています。

ポイントは下記2点です。

- `<script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script>`でPyodideを読み込む
- `pyodide.runPython()`にPythonコードを文字列で与えて実行

HTMLファイルが用意できたら、適当な方法でHTTPサーバを立ち上げます。
PythonやNode.jsがあれば`$ python -m http.server 3000`や`$ npx serve`とか。Dockerがあれば`$ docker run -it --rm -v $(pwd):/app --workdir /app -p 3000:3000  python:3.10-slim python -m http.server 3000`とか`$ docker run -it --rm -v $(pwd):/app --workdir /app -p 3000:3000 node:alpine npx serve`など、お好みの方法を使えばいいです。

これで、localhostをブラウザで開けばPythonが実行できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50391/6279f0b7-9d5b-4f80-53c3-c680bef17749.png)

### パッケージの追加
Pythonの強みのひとつは機械学習などの豊富なパッケージです。Pyodideを使えば、それらをそのままWebフロントエンドで利用できます。

下記のような、scikit-learnで単純な線形回帰をするPythonプログラムを実行したいとします。

```python:Python
import pandas
import numpy
from sklearn import linear_model

df = pandas.DataFrame([
    {"身長": 160, "体重": 57},
    {"身長": 169, "体重": 61},
    {"身長": 180, "体重": 70},
])

reg = linear_model.LinearRegression()
reg.fit(df[["身長"]].values, df["体重"].values)

reg.predict([[175]])[0] #=> 66.16611295681065
```

`pandas`,`numpy`,`scikit-learn`のパッケージは`micropip.install()`で追加できます。

```diff_javascript:index.html
       async function main() {
         let pyodide = await loadPyodide();
+        await pyodide.loadPackage("micropip");
+        const micropip = pyodide.pyimport("micropip");
+        await micropip.install('numpy');
+        await micropip.install('pandas');
+        await micropip.install('scikit-learn');
```

これで上記のPythonコードが動くようになります。

ただし、当然100%なんでも動かせるわけではありません。

https://micropip.pyodide.org/en/v0.2.2/project/api.html#micropip.install

>Micropip can only load pure Python wheels or wasm32/emscripten wheels built by Pyodide.

[Pyodideでビルドされたwheel](https://pyodide.org/en/stable/usage/packages-in-pyodide.html)か、pure Pythonで書かれたwheelしか読み込めません。

たとえば`transformers`は記事執筆現在`ValueError: Can't find a pure Python 3 wheel for 'safetensors>=0.3.1'.`というエラーで失敗します。Pyodideで使うにはなんとかしてwasm用ビルドを用意する必要があります。
[Transformers](https://github.com/huggingface/transformers)にはJavaScriptに移植された[Transformers.js](https://github.com/xenova/transformers.js)がありますが、そちらをPyodideで動くようにラップした[Transformers.js.py](https://github.com/whitphx/transformers.js.py)というのはあります。


## Pyodideを使った色々なアプリケーション
### PyScript
https://pyscript.net/

PyScriptはPyodideを利用して[^pyscript-pyodide]HTML内にPythonを埋め込んで実行するためのパッケージです。[Anaconda Inc.](https://www.anaconda.com/)が開発しています。

[^pyscript-pyodide]: 厳密にはMicroPythonもサポートしている

Web開発に慣れているなら、サンプルコードを見たほうが話が早いと思います。

```html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="stylesheet" href="https://pyscript.net/releases/2024.1.1/core.css">
    <script type="module" src="https://pyscript.net/releases/2024.1.1/core.js"></script>
    <title>PyScript Hello World</title>
  </head>

  <body>
    <section>
      Hello world!
      <script type="py">
        from pyscript import display
        from datetime import datetime
        now = datetime.now()
        display(now.strftime("ただいま%Y年%m月%d日 %H時%M分%S秒です"))
      </script>
    </section>
  </body>
</html>
```

先ほどPyodideを使った例ではJavaScript側にメインの処理があって、文字列としてPythonコードを渡したりしていましたが、PyScriptでは`<script type="py">`タグの中にPythonコードを記述しています。
パッケージの追加も簡単になります。

```html
<py-config>
{
  "packages": ["pandas", "numpy", "scikit-learn"]
}
</py-config>
<script type="py">
from pyscript import display
import pandas
import numpy
from sklearn import linear_model

df = pandas.DataFrame([
    {"身長": 160, "体重": 57},
    {"身長": 169, "体重": 61},
    {"身長": 180, "体重": 70},
])

reg = linear_model.LinearRegression()
reg.fit(df[["身長"]].values, df["体重"].values)

display(reg.predict([[175]])[0]) #=> 66.16611295681065
</script>
```

スクラッチでPyodideを利用したWebアプリを開発するなら、PyScriptを使うほうが便利だと思います。

### Jupyter Lite
https://medium.com/pydata-osaka/jupyterlite-jupyter-%EF%B8%8Fwebassembly-%EF%B8%8Fpython%E3%81%AE%E6%97%A5%E6%9C%AC%E8%AA%9E%E8%A8%B3-1f228a9d97d1

Jupyter Liteは、ブラウザだけで動作するJupyterLabです。[デモサイト](https://jupyterlite.github.io/demo/lab/index.html)で試せます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50391/25fafece-f4a1-a1af-b11d-34f8cbc65d11.png)

JupyterLabを使ったことがあれば、特に何も違和感は無いと思います。見た目はふつうのJupyterLabなので違いが分かりにくいですが、クライアントサイドで動いています。ネットワークがオフラインになっても使えます。

### GradioのPyodide版「Gradio-Lite」
https://www.gradio.app/guides/gradio-lite

[Gradio](https://www.gradio.app/)は、Pythonで機械学習アプリケーションをWebに実装して共有するための便利なフレームワークです。Gradio自体はサーバサイドで動くWebアプリケーションですが、そのGradioをPyodideを利用してブラウザに移植したのがGradio-Liteです。

```html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" />
  </head>
  <body>
    <gradio-lite>
import gradio as gr

def greet(name):
  return f"こんにちは、{name}さん"

gr.Interface(greet, "textbox", "textbox").launch()
    </gradio-lite>
  </body>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50391/ba98bef5-dff3-61af-0fef-a20a151a8407.png)

他にも[StreamlitのPyodide版「stlite」](https://www.whitphx.info/posts/20221104-streamlit-wasm-stlite/)もあります。

## まとめ
Pyodideを使ってPythonのソースコードをブラウザ上などで動かせるようになります。いくつかの主要なPythonアプリケーションがPyodideに対応しているのもあって、Pythonで開発したアプリケーションをデプロイ・配布する手段のひとつとして選択肢に入ることはあると思います。
一方で[PyTorchのサポートが難しい](https://github.com/pyodide/pyodide/issues/1625)・[GPUが使えない](https://github.com/pyodide/pyodide/issues/1911)などで、本格的な機械学習アプリケーションの開発をすべてPyodideで行うのは今は難しいと思いました。

## 参考
- [Three Python trends in 2023](https://blog.jerrycodes.com/python-trends-in-2023/)