stliteのソースコードをPythonファイルから読み込む方法

## 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アプリも実現できるそうです！  

## ソースコードをPythonファイルから読み込む

GitHubに書かれている最初のサンプルや[私が以前書いた記事](https://qiita.com/ak-sakatoku/items/dda0d127a7c2ac488556)ではHTMLファイルに直接、Pythonのコードを記述しています。  
でもこれだと書きづらいし読みづらいですよね。ちゃんとPythonファイルから読み込む方法が用意されています。  

```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.4/build/stlite.css"
    />
  </head>
  <body>
    <div id="root"></div>
    <script src="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.58.4/build/stlite.js"></script>
    <script>
      stlite.mount(
        {
          requirements: ["pandas", "numpy"],
          entrypoint: "streamlit_app.py",
          files: {
            "streamlit_app.py": {
              url: "./stlite_app.py",
            },
          },
        },
        document.getElementById("root")
      );
    </script>
  </body>
</html>
```

```py:stlite_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)
```

「 ` 」で囲んでコードを記述していたところを```{ url: "ファイルパス" }```に置き換えるだけです。  

フォルダ構成は以下のような感じです。記述したファイルパスに合わせて配置するだけなので簡単ですね。  

![Directory](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/90b419dd-1ccc-b0a4-545a-e78df0fd8d6e.png)

## ブラウザの設定

ローカルフォルダから上記のHTMLファイルを開くとほとんどの環境で```Error during booting up: Failed to fetch```というエラーが発生します。  

![Error](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/e87c17cb-6934-2ae4-5c45-0c199e64d36f.png)

これはセキュリティ上の理由により、ブラウザ上で動いているスクリプトはローカルファイルへのアクセス方法が制限されているためです。  

Chromeであれば、セキュリティ的にはよくないのですが、```--allow-file-access-from-files```を起動オプションで指定すれば制限を回避することができます。  

![Shortcut](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/56ae71bc-9205-361d-3723-65095d78e5c8.png)

また、PythonファイルをWebサーバに置いてURL経由で参照すれば普通にアクセスすることができます。  
例えばGitHubでも問題ありません。  

![Fetch from GitHub](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/4b55263b-660b-440a-9efc-5355de4fe62e.png)

## 様々なファイルの読み込み方法

GitHubの[Various ways to load files](https://github.com/whitphx/stlite?tab=readme-ov-file#various-ways-to-load-files)の節に記載されている通り、いくつかのオプションが用意されています。  

### HTMLファイルに埋め込む

標準的な方法です。バイナリファイルも扱えるそうです。  
1つのHTMLファイルにすべてを詰め込めるのでアプリを配布したいときに便利ですね。  

```
"path/to/text_file.txt": "file content",
"path/to/binary_file.bin": new Uint8Array([0x00, 0x01, 0x02, 0x03]),
```

### 外部ファイルから読み込む

今回の記事で解説した方法です。開発しやすさがいいですね。   

```
"path/to/file": {
url: "https://example.com/path/to/file",
},
"path/to/file2": {
url: "./path/to/file",
},
```

### アーカイブファイルから読み込む

アーカイブファイルに色んなファイルを詰め込んでそれを動的に展開することも可能です。  
```files:```ではなく```archives:```で定義します。前述の方法とも併用できます。  
色々なファイルを参照するアプリを作りたいときによさそうですね！  

```
archives: [
  {
    url: "./foo.zip",
    // buffer: new Uint8Array([...archive file binary...]), // You can also pass the binary data directly
    format: "zip",
    options: {},
  },
],
```