Closed6

Cloudflare WorkersでPython Workerを試す、再び ⑤ Standard Library / Packages

kun432kun432

標準ライブラリ

https://developers.cloudflare.com/workers/languages/python/stdlib/

Python Workers では Pyodide を使用して Pythonコードを実行する。基本的に CPythonの大部分の機能・標準ライブラリは動作するが、一部例外がある。

  • 標準ライブラリから削除されて使えないモジュール
  • WebAssembly VM の制限で、機能しないモジュール
  • 機能が制限されるモジュール

Pyodide を試したときにも確認したが、Pyodide 自身に存在する制約は Python Workers でも同じ、ということなのだろう。

https://zenn.dev/kun432/scraps/77b53e782bdeef

Cloudflareのドキュメントに記載のモジュールは、多少異なるところもあるが概ね Pyodide のドキュメントに書いてあるものと同じみたい。

あと、Python Workers では、エフェメラルなインメモリファイルシステムが利用できる。open() や pathlib.path など 標準的な I/O 経由で利用はできるが、Worker Isolate が破棄されると当然ながらデータは失われる。一時ファイルの場合はこれでもOKだが、永続的なファイルシステムとして使う場合には KVやR2 など他のリソースサービスを使用する必要がある、

kun432kun432

パッケージ

https://developers.cloudflare.com/workers/languages/python/packages/

Python Workers では、Pywrangler を使ってパッケージとPython Workers を管理できる。Pythonパッケージはデプロイ時に Worker に自動的にバンドルされて利用可能となる。

利用したいパッケージは pyproject.toml に追加する。以下はFastAPIを追加した例。

[project]
name = "YourProjectName"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.13"
dependencies = [
    "fastapi"
]

[dependency-groups]
dev = [
	"workers-py",
	"workers-runtime-sdk"
]

ただし、全てのパッケージがサポートされているわけではない。サポートされているライブラリは以下。

  • PyPI上のPure Pythonパッケージ
  • PyEmscripten Pythonパッケージ
    • (Pyodide 向けにビルドされたパッケージという認識)
  • Pyodideに含まれるパッケージ

このあたりも 基本的に Pyodide と同じような感じに思える。

https://zenn.dev/kun432/scraps/1b70e438618f6e

ドキュメントでは以下の主要なパッケージを追加する例が記載されている。

  • Django
  • FastAPI
  • Flask
  • LangChain
kun432kun432

FastAPI を試してみる。

https://developers.cloudflare.com/workers/languages/python/packages/fastapi/

プロジェクト作成

uvx --from workers-py pywrangler init my-fastapi-sample \
    --type hello-world \
    --agents \
    --git

プロジェクトディレクトリに移動

cd my-fastapi-sample 

プロジェクトの初期状態はこう。

出力
.
├── .git/
├── .gitignore
├── .python-version
├── .vscode/
│   └── settings.json
├── AGENTS.md
├── README.md
├── node_modules/
├── package-lock.json
├── package.json
├── pyproject.toml
├── src/
│   ├── entry.py
│   └── submodule.py
└── wrangler.jsonc

src の下をまるっと削除して、src/main.py を作成。

rm src/*.py
touch src/main.py
src/main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}

from workers import asgi
Default = asgi.entrypoint(app)

wrangler.jsonc のエントリーポイントを修正して、src/main.py に向ける。

wrangler.jsonc
{
    "$schema": "node_modules/wrangler/config-schema.json",
    "name": "my-fastapi-sample",
    // 以下を修正
    "main": "src/main.py",
    "compatibility_date": "2026-10-06",
    "compatibility_flags": [
        "python_workers"
    ],
    "observability": {
        "enabled": true
    }
}

pyproject.toml に fastapi パッケージを追加

pyproject.toml
[project]
name = "my-fastapi-sample"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "fastapi",
]

[dependency-groups]
dev = ["workers-py", "workers-runtime-sdk"]

開発サーバを起動

uv run pywrangler dev

curl で確認

curl -s http://localhost:8787 \
    -H "Content-Type: application/json" \
    | jq -r .
出力
{
  "Hello": "World"
}

/docs で API ドキュメント も確認できる。

ここに Workers Static Assets で、静的コンテンツを配信するフロントエンドを追加する。

静的コンテンツを用意。中身はChatGPTに作成してもらった。

mkdir public
touch public/{index.html,style.css,app.js}
public/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>FastAPI on Cloudflare Workers</title>
  <link rel="stylesheet" href="/style.css">
  <script src="/app.js" defer></script>
</head>
<body>
  <h1>FastAPI on Cloudflare Workers</h1>
  <p>これは静的HTMLです。</p>
  <button id="button">APIを呼び出す</button>
  <p id="result"></p>
</body>
</html>
public/style.css
body {
  font-family: sans-serif;
  max-width: 600px;
  margin: 50px auto;
  background: #f0f4f8;
}

h1 {
  color: #2563eb;
}

button {
  padding: 10px 20px;
  cursor: pointer;
}
public/app.js
document.getElementById("button").onclick = async () => {
  const response = await fetch("/api/hello");
  const data = await response.json();
  document.getElementById("result").textContent = data.message;
};

wrangler.jsonc を修正。ASSETS バインディングを使うことで、静的コンテンツは Worker にバンドルされることなく、Cloudflare の分散アセットストアから配信されるらしい。

wrangler.jsonc
{
    "$schema": "node_modules/wrangler/config-schema.json",
    "name": "my-fastapi-sample",
    "main": "src/main.py",
    "compatibility_date": "2026-10-06",
    "compatibility_flags": [
        "python_workers"
    ],
    "observability": {
        "enabled": true
    },
    // 以下を追加
    "assets": {
        "directory": "./public/",
        "binding": "ASSETS",
        // すべてのリクエストが最初にFastAPI Workerに送信され、
        // APIルートが静的ファイルよりも優先される
        "run_worker_first": true
    }
}

src/main.py を修正。

src/main.py
from fastapi import FastAPI, Request
from fastapi.responses import Response
from workers import asgi


app = FastAPI()
Default = asgi.entrypoint(app)

@app.get("/api/hello")
async def api_hello():
    return {"message": "Hello from the API"}

# Catch-all: 上記以外のリクエストをすべて Workers Static Assets にプロキシする
# FastASPIのネイティブな app.frontend("/", directory="dist") に相当する Workers の実装
@app.get("/{path:path}")
async def frontend(path: str, request: Request):
    env = request.scope["env"]
    asset_url = f"https://assets.local/{path}"
    resp = await env.ASSETS.fetch(asset_url)
    body = await resp.bytes()
    return Response(content=body, status_code=resp.status, headers=resp.headers)

開発サーバで試してみる。

uv run pywrangler dev

ブラウザで開いて「APIを呼び出す」をクリック。

APIの結果が反映されている。

curl でも。

curl -s http://localhost:8787/
出力
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>FastAPI on Cloudflare Workers</title>
  <link rel="stylesheet" href="/style.css">
  <script src="/app.js" defer></script>
</head>
<body>
  <h1>FastAPI on Cloudflare Workers</h1>
  <p>これは静的HTMLです。</p>
  <button id="button">APIを呼び出す</button>
  <p id="result"></p>
</body>
</html>
curl -s http://localhost:8787/api/hello \
    -H "Content-Type: application/json" \
    | jq -r .
出力
{
  "message": "Hello from the API"
}
このスクラップは22日前にクローズされました