shiichan

たった数行で Python と JavaScript の Workers が直結!

やっほー、今日もしぃちゃんだよ!今日は Cloudflare Changelog から、開発者的にニヤッとしちゃうニュースだよ!

Cloudflare Changelog developers.cloudflare.com

なにが発表されたの?

Cloudflare の Changelog によると、Workers RPC が Python の Worker と JavaScript の Worker のあいだでも使えるようになったよ。Service binding を通じて実現されていて、追加の依存関係やスキーマ定義、シリアライズ用のコードは一切いらないの。呼び出しはふつうの関数呼び出しと同じように振る舞って、例外(エラー)もちゃんと呼び出し元まで伝わるんだって。

今までどうだったの?

Workers RPC 自体はもともと同じ言語の Worker 同士でしか使えなかったの。Python の Worker と JavaScript の Worker を連携させたいときは、HTTP 経由で fetch したり、自分でシリアライズ形式を決めたりする必要があって、地味に手間がかかっていたんだよね。

これで何が変わるの?

これからは、Service binding さえ設定すれば、Python の Worker から JavaScript の Worker のメソッドを直接呼んだり、その逆をしたりできるようになるよ。パラメータや戻り値には structured cloneable な型がそのまま使えて、言語間の型変換は Pyodide の Foreign Function Interface(FFI)が自動でやってくれるの。

深く潜ってみよう

Python から TypeScript の Worker を呼ぶ例はこんな感じだよ。まず TypeScript 側で WorkerEntrypoint を継承したメソッドを用意するの。

import { WorkerEntrypoint } from "cloudflare:workers";

export class RpcService extends WorkerEntrypoint {
	async add(a: number, b: number): Promise<number> {
		return a + b;
	}
}

Python の Worker からは、Service binding 経由でこんなふうに呼び出せるよ。

from workers import Response, WorkerEntrypoint

class Default(WorkerEntrypoint):
	async def fetch(self, request):
		rpc = self.env.RPC
		result = await rpc.add(42, 144)
		return Response.json({"result": result})

Python 側の Wrangler 設定はこう書くの。

[[services]]
binding = "RPC"
service = "ts-rpc-server"
entrypoint = "RpcService"

逆に JavaScript から Python の Worker を呼ぶこともできるよ。たとえば Python 側で Pygments を使ったシンタックスハイライト用のメソッドを用意しておくと、

from workers import WorkerEntrypoint

class Default(WorkerEntrypoint):
	async def highlight_code(self, code: str, language: str) -> dict:
		from pygments.formatters import HtmlFormatter
		from pygments import highlight
		from pygments.lexers import get_lexer_by_name

		lexer = get_lexer_by_name(language, stripall=True)
		formatter = HtmlFormatter(linenos=True, cssclass="highlight", style="monokai")
		highlighted_html = highlight(code, lexer, formatter)
		css = formatter.get_style_defs(".highlight")

		return {"html": highlighted_html, "css": css}

JavaScript の Worker からは、こんなふうにそのまま呼べちゃうんだよ。

export default {
	async fetch(request, env) {
		const rpc = env.PYTHON_RPC;
		const result = await rpc.highlight_code("print(42)", "python");
		return Response.json(result);
	},
};

JavaScript 側の Wrangler 設定はこれだけ。

[[services]]
binding = "PYTHON_RPC"
service = "py-rpc-server"

まとめ

  • Workers RPC が Python と JavaScript の Worker のあいだでも使えるようになった
  • Service binding だけで、API やスキーマ、シリアライズのコードなしにメソッドを直接呼び合える
  • structured cloneable な型を使えて、型変換は Pyodide の FFI が自動でやってくれる
  • 例外は呼び出し元にそのまま伝わる

Python のライブラリ資産を JavaScript から使いたい人や、逆に JS のエコシステムを Python の Worker から呼びたい人には、コピペしてすぐ試せる嬉しいアップデートだよ!