サーバレスなStreamlit、stliteで遊ぼう！

## stliteとは

StreamlitはPython環境上で実行されているサーバにブラウザでアクセスする仕組みですが、このサーバを同じブラウザ上で動くようにしたものが[stlite](https://github.com/whitphx/stlite)です。  
Streamlit Creator[^1]であるWhitphxさんが開発しています。  

https://github.com/whitphx/stlite

[^1]: Streamlitコミュニティへの貢献が世界トップクラスな方々のこと。https://streamlit.io/creators

PythonをWebAssembly(WASM)に移植してブラウザ上で動くようにした[Pyodide](https://pyodide.org/en/stable/)というPythonディストリビューションがあり、stliteはStreamlitをこのPyodideで動くようにすることでサーバレスを実現しているそうです[^2]。  

[^2]: 「サーバレス」ではなく「静的サイトで動かせる」と表現した方が分かりやすいかも

ちょっとした可視化アプリを配布したい場合やWebページにStreamlitのプレイグラウンドを組み込みたい場合に便利そうですね。  
例えば[Electron](https://www.electronjs.org/ja/docs/latest/)と組み合わせればデスクトップで動くStreamlitアプリも実現できるそうです！  

## 使い方

以下のようなコードを書いたHTMLファイルをローカルフォルダに保存します。  

```html:stlite_example.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <title>stlite app</title>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.58.3/build/stlite.css"
    />
  </head>
  <body>
    <div id="root"></div>
    <script src="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.58.3/build/stlite.js"></script>
    <script>
      stlite.mount(
        {
          requirements: ["pandas", "numpy"],
          entrypoint: "streamlit_app.py",
          files: {
            "streamlit_app.py": `
import streamlit as st
import pandas as pd
import numpy as np

st.title('Streamlit example')

rnd_data = pd.DataFrame(np.random.randn(50, 3), columns=["A", "B", "C"])
st.line_chart(rnd_data)
`,
          },
        },
        document.getElementById("root")
      );
    </script>
  </body>
</html>
```

おもむろにブラウザで開くと…Streamlitが動いたァァァァァァ！  

![stlite example](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/df6f8ded-0bcc-6a2b-6654-95b9f4521666.png)

ローカルフォルダから開いているのに動くのはちょっとビックリしません？  

## コードの補足

見たらだいたいわかる！ってくらいに簡単なんですが少しだけ補足を。  

### バージョン

以下に含まれる```0.58.3```の部分については最新のバージョン(あるいは自分の使いたいバージョン)を指定しましょう。  

```javascript
href="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.58.3/build/stlite.css"
src="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.58.3/build/stlite.js"
```

GitHubのReleasesに最新のバージョンが記載されています。[^2]  

![Version](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/f508b65f-77cd-e611-7f04-e37cce3cc822.png)

[^2]: 記事を書いている間にバージョンアップがあった。精力的に開発されていてすごい

### パッケージ

パッケージを導入したい場合は```stlite.mount```関数に渡すオブジェクトの```requirements```キーに指定してあげればOKです。  
今回はPandasとNumPyを導入しています。  

```javascript
requirements: ["pandas", "numpy"],
```

### filesとentrypoint

```stlite.mount```関数に渡すオブジェクトの```files```キーにファイル名とその中身のセットを列挙してあげることで、ブラウザ上のPythonが認識できるファイル一覧を定義することができます。  
今回は```streamlit_app.py```というPythonファイルのみを渡しています。  

```javascript
          entrypoint: "streamlit_app.py",
          files: {
            "streamlit_app.py": `
import streamlit as st
import pandas as pd
import numpy as np

st.title('Streamlit example')

rnd_data = pd.DataFrame(np.random.randn(50, 3), columns=["A", "B", "C"])
st.line_chart(rnd_data)
`,
```

また、stliteは```entrypoint```キーに指定されたファイル名を使って```streamlit run```コマンドを実行します。  
今回は```files```で渡した```streamlit_app.py```をそのままStreamlitアプリとして起動しているので、```streamlit_app.py```の中身である「 ` 」で囲まれている部分、すなわち  

```py
import streamlit as st
import pandas as pd
import numpy as np

st.title('Streamlit example')

rnd_data = pd.DataFrame(np.random.randn(50, 3), columns=["A", "B", "C"])
st.line_chart(rnd_data)
```

にはStreamlitアプリとして動作するPythonコードを記載してあげましょう。  

### Streamlitアプリがマウントされる場所

```stlite.mount```関数に渡す要素の中にStreamlitアプリがマウントされます。  
今回はIDが```root```の要素にStreamlitアプリをマウントしています。  

```javascript
document.getElementById("root")
```