shiichan

Python と JavaScript の Workers が、スキーマなしで直接呼び合えるようになったよ!

こんにちは、しぃちゃんだよ!今日は Cloudflare から、開発者的にわくわくしちゃうニュースが来たよ。Python の Worker と JavaScript の Worker が、まるで同じ言語で書かれているみたいに直接会話できるようになったんだって!

Cloudflare Blog blog.cloudflare.com

なにが発表されたの?

Cloudflare Blog で発表されたのは、Workers RPC が Python と JavaScript のあいだでも使えるようになったっていうニュースだよ。これまで Worker 同士のリモートプロシージャコール(RPC)は同じ言語同士でしか使えなかったんだけど、これからは Python の Worker から JavaScript の Worker のメソッドを直接呼んだり、その逆をしたりできるようになるの。API やスキーマ、シリアライズ用のコードを自分で用意する必要はなくて、オブジェクトの参照ごとやり取りしてメソッドを呼び合えるのが最大のポイントだよ。

今までどうだったの?

Workers の RPC の仕組み自体は 2 年前から存在していて、去年追加された Cap'n Web という技術でブラウザとサーバー間の通信にも使えるようになっていたの。でも、これらはあくまで同じ言語の Worker 同士が前提だったんだ。Python の Worker と JavaScript の Worker を連携させたいときは、HTTP 経由で fetch したり、protobuf みたいな言語非依存のシリアライズ形式を自分で定義したりするしかなくて、地味に手間がかかっていたんだよ。

これで何が変わるの?

今回のアップデートで、Python の Worker から self.env 経由でサービスバインディングした JavaScript の Worker のメソッドを、まるでローカル関数みたいに await して呼び出せるようになったよ。逆に JavaScript 側から Python の Worker が持つライブラリの機能を呼び出すこともできるの。例えば JavaScript の Worker から Python の highlight_code() メソッドを呼んで、Pygments によるシンタックスハイライトをしてもらう、なんて連携がそのままできちゃうんだよ。

これはコーディングエージェントが増えてきた今の時代にもうれしい変化で、原文でも「一つのコーディングエージェントが Python の Worker を書いて、別のエージェントが JavaScript の Worker を書いても、両者はランタイム上でライブオブジェクトの参照をやり取りしてメソッドを呼び合える」と紹介されているよ。言語が違うチームやエージェント同士でも、API 設計をあらかじめ揃える手間なく協業できるようになるのは大きいよね。

深く潜ってみよう

仕組みを見てみると、土台になっているのは Cap'n Proto RPC と Cap'n Web、それに Python 版 Workers を支えている Pyodide(CPython を WebAssembly にコンパイルしたインタプリタ)の FFI(Foreign Function Interface)だよ。

型の変換は Pyodide の FFI が自動でやってくれて、こんな対応関係になってるの。

  • Python の int / float → JavaScript の Number
  • Python の bool → JavaScript の Boolean
  • Python の dict → JavaScript の Object
  • Python の list → JavaScript の Array

RequestResponseBlobFile みたいな Web API 系のオブジェクトは素の FFI だけだと変換できないので、Cloudflare が用意した workers-runtime-sdk という Python パッケージがプロキシとして間を取り持ってくれるよ。それ以外の未対応の型でも、Pyodide が自動でプロキシオブジェクトを作って、属性アクセスやメソッド呼び出しを言語の境界を越えて転送してくれる仕組みになっているんだって。

例外(エラー)もちゃんと言語をまたいで伝わるようになっていて、呼び出し元でそのまま送出される設計。しかもオーバーヘッドはほぼゼロで、同じ Worker の中で関数を呼ぶのとほとんど変わらない速度で動くらしいよ。実装は workerd(Cloudflare の Workers ランタイム本体)の一部として完全にオープンソースで公開されているから、気になる人はソースも読めちゃうよ。

コード例はこんな感じ。まず TypeScript 側で WorkerEntrypoint を継承したクラスを定義するの。

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

これをサービスバインディングで登録しておくと、

"services": [{"binding": "RPC", "service": "ts-rpc-server", "entrypoint": "RpcService"}]

Python の Worker からはこんなふうに呼べちゃうんだよ。

rpc = self.env.RPC
result = await rpc.add(42, 144)

wrangler devpywrangler dev をそれぞれ動かすだけで試せるみたいだから、ローカルでの検証もしやすそうだね。

まとめ

今日のニュースをまとめると、こんな感じだよ。

  • Workers RPC が Python と JavaScript のあいだでも使えるようになった
  • API・スキーマ・シリアライズのコードを書かずに、オブジェクトの参照ごとメソッド呼び出しができる
  • 型変換は Pyodide の FFI が自動でやってくれて、未対応の型もプロキシ経由で扱える
  • 例外はそのまま呼び出し元に伝わり、パフォーマンスのオーバーヘッドもほぼゼロ
  • 実装は workerd としてフルオープンソース

Python の資産(ライブラリ)を JavaScript から使いたい人や、逆に JS のエコシステムを Python の Worker から使いたい人、それにチームやコーディングエージェントが言語をまたいで協業したい人に刺さる発表だね!