Python遇見JavaScript:Wasm與PythonMonkey的魔力
PythonMonkey 使開發(fā)人員能夠輕松地在 JavaScript 和 Python 代碼之間相互使用,并且?guī)缀鯖]有性能損失。
譯自Python Meets JavaScript, Wasm With the Magic of PythonMonkey,作者 Darryl K Taft。
PythonMonkey是一款創(chuàng)新的JavaScript運(yùn)行時(shí),它嵌入在Python中,彌合了世界上兩種最流行的編程語言之間的差距。
PythonMonkey 是一個(gè)運(yùn)行在 Python 中的 JavaScript 運(yùn)行時(shí),它建立在Mozilla的SpiderMonkey引擎之上。開發(fā)人員可以將其用作 Python 庫,在 Python 中運(yùn)行 JavaScript 代碼。
Distributive是一家位于加拿大安大略省金斯頓的云計(jì)算初創(chuàng)公司,他們創(chuàng)建了 PythonMonkey,以便將他們的 JavaScriptNodeJSSDK 直接移植到 Python,而無需維護(hù)兩個(gè)項(xiàng)目,從而將代碼維護(hù)成本降低了一半。
“我們希望 PythonMonkey 能幫助彌合數(shù)百萬 npm 包和 Python 開發(fā)人員之間的差距,并有朝一日能夠獨(dú)立成為一個(gè) JavaScript 運(yùn)行時(shí),與Node.js、Bun和Deno競爭,但它能夠從 JS 中使用‘任何’ Python 包,”Distributive 的軟件開發(fā)人員Will Pringle說。
事實(shí)上,PythonMonkey 使開發(fā)人員能夠輕松地在 JavaScript 和 Python 之間使用代碼,并且?guī)缀鯖]有性能損失,Pringle 在去年的一篇介紹該技術(shù)的博客文章中寫道——與此同時(shí),Distributive 計(jì)劃在下個(gè)月發(fā)布 PythonMonkey 1.0。
WebAssembly API 和引擎
“例如,可以從 JavaScript 庫中調(diào)用 Python 包,例如NumPy,或者直接從 Python 中使用 NPM 包,例如crypto-js,“Pringle 寫道,“此外,使用 SpiderMonkey 中的 WebAssembly API 和引擎,在 Python 中執(zhí)行WebAssembly(Wasm) 模塊變得微不足道。”
是的,該庫利用了 SpiderMonkey 的功能,包括其 WebAssembly 引擎,允許 Python 在沙箱中從各種語言(如 C、C++、Rust等)運(yùn)行不受信任的 Wasm 代碼。
此外,開發(fā)人員可以使用 PythonMonkey 將用 Python 編寫的緩慢的“熱循環(huán)”重構(gòu)為在 JavaScript 中執(zhí)行,利用 SpiderMonkey 的即時(shí)編譯器來實(shí)現(xiàn)接近本機(jī)的速度,Pringle 寫道。
此外,PythonMonkey 還附帶 PMJS,這是一個(gè)類似于 Node.js 的 JavaScript 運(yùn)行時(shí)環(huán)境,它支持從 JavaScript 調(diào)用 Python 庫。
簡單代碼示例
在他的文章中,Pringle 提供了一些編碼指導(dǎo),包括下面的“hello world”示例,它演示了從 JavaScript 生成的字符串被返回到 Python 上下文:
>>> import pythonmonkey as pm
>>> hello = pm.eval(” ‘Hello World’.toUpperCase(); “)
>>> print(hello)
‘HELLO WORLD’
下面的更復(fù)雜的示例演示了將 Python print 函數(shù)作為參數(shù)傳遞給 JavaScript 函數(shù),然后從 Python 調(diào)用該 JavaScript 函數(shù):
>>> import pythonmonkey as pm
>>> hello = pm.eval(“(func) => { func(‘Hello World!’)}”)
>>> hello(print)
Hello World!
此示例使用 pmjs 執(zhí)行一個(gè) JavaScript 文件,該文件使用 Python 的 print 函數(shù)(這可以通過 pmjs main.js 執(zhí)行):
main.js
const pyPrint = python.eval(“print”);
pyPrint(“Hello, World!”); // 這將輸出“Hello, World!”
項(xiàng)目目標(biāo)
該項(xiàng)目的目標(biāo)包括:
- 快速且內(nèi)存高效。
- 使在 JS 或 Python 中編寫代碼成為開發(fā)人員的偏好。
- 從 Python 中使用 JavaScript 庫。
- 從 JavaScript 中使用 Python 庫。
- 相同的過程運(yùn)行 JavaScript 和 Python 虛擬機(jī)——沒有序列化、管道等。
- Python 列表和字典的行為與 JavaScript 數(shù)組和對(duì)象相同,反之亦然,完全適應(yīng)給定的上下文。
Distributive 的 CTOWes Garland創(chuàng)建了 PythonMonkey,旨在為公司開發(fā)人員簡化工作。Garland 在 2007 年左右創(chuàng)建了 Node.js 的前身gpsee——基于 Mozilla 的 SpiderMonkey 引擎,與 PythonMonkey 類似。
“我們?cè)?Distributive 擁有一個(gè)龐大而復(fù)雜的客戶端 SDK,名為 dcp-client,用 JavaScript 編寫,”Pringle 告訴 The New Stack?!捌渲邪罅窟壿嫞虼宋覀儾幌胗?Python 重寫它并維護(hù)兩個(gè)項(xiàng)目——實(shí)際上是將 SDK 的開發(fā)成本增加一倍。PythonMonkey 使我們能夠?qū)?JavaScript 庫中的所有底層邏輯移植到 Python,同時(shí)只維護(hù)一個(gè)代碼庫。”
Distributive 主要使用 JavaScript 進(jìn)行開發(fā),因?yàn)楣拘枰?Web 堆棧中運(yùn)行。
“為了說明,我們正在構(gòu)建DCP (Distributive Compute Protocol),這是一個(gè)計(jì)算市場,人們可以在其中從其他人的家用電腦租用 CPU/GPU 周期,”Pringle 說?!斑@個(gè)想法是,如果你有一臺(tái)閑置的電腦,你可以將其連接到我們的云計(jì)算網(wǎng)絡(luò)并通過計(jì)算其他人的工作負(fù)載來賺錢。將你的電腦變成工作節(jié)點(diǎn)的程序是一個(gè) JavaScript 引擎,它可以執(zhí)行 JS 程序、WebAssembly 或任何可以編譯到 (或具有編譯到) WebAssembly 的編程語言。
“你也可以直接在瀏覽器中運(yùn)行它??傊?,我們實(shí)現(xiàn)中有很多 JavaScript 代碼,但每個(gè)人都想用 Python 編寫這種代碼,因此 PythonMonkey 使 Python 開發(fā)人員能夠使用我們的產(chǎn)品 (DCP)——而無需我們重新編寫 SDK?!?/p>
Pringle 現(xiàn)在正在開發(fā) Distributive 的 Python SDK,該公司預(yù)計(jì)將在未來幾周內(nèi)發(fā)布它。
項(xiàng)目演變
自去年 7 月推出 PythonMonkey 以來,Distributive 對(duì)該技術(shù)進(jìn)行了大量改進(jìn),包括:
Web 堆棧 API
- 從頭開始實(shí)現(xiàn)XMLHttpRequestAPI —— 使 socketio 等流行的 JavaScript 庫能夠使用標(biāo)準(zhǔn) JavaScript 網(wǎng)絡(luò) API 在 PythonMonkey 中運(yùn)行。
- 實(shí)現(xiàn)了一些計(jì)時(shí)器全局函數(shù):setInterval/clearInterval、setImmediate/clearImmediate 和 setTimeout/clearTimeout,返回 Node.js 風(fēng)格的Timeout類,帶有.ref()和.unref()方法。
- 實(shí)現(xiàn)consoleAPI 中所有缺失的方法,現(xiàn)在console的行為與 Web 規(guī)范https://console.spec.whatwg.org/相同。
- atob、btoa函數(shù)。
跨語言強(qiáng)制轉(zhuǎn)換
- 用戶現(xiàn)在可以將任何任意 Python 對(duì)象包裝/代理到 JavaScript 中。
- 更好的跨語言迭代器支持。
異常處理
- 實(shí)現(xiàn)完整的跨語言堆棧跟蹤。
- 改進(jìn)了跨語言嵌套異常處理和 Promise 拒絕處理。
- uncaughtExceptionHandler
JavaScript 引擎更新
- 將 SpiderMonkey 更新到最新版本,因此用戶可以享受與最新 Firefox 相同的新的 JS + WASM 語言功能,并且性能更好。
- 貢獻(xiàn)了一個(gè)補(bǔ)丁到 SpiderMonkey,修復(fù)了一個(gè)錯(cuò)誤。
開發(fā)人員體驗(yàn)改進(jìn)
- 更好的開發(fā)人員體驗(yàn),帶有嵌入式調(diào)試工具pmdb(受gdb啟發(fā))和 WTFPythonMonkey(受wtfnode啟發(fā))。
- 更好的 Python 類型提示和開發(fā)人員文檔。
模塊系統(tǒng)
PythonMonkey 的模塊系統(tǒng)允許輕松地將 JavaScript 庫移植到 Python,反之亦然。運(yùn)行時(shí)使開發(fā)人員“能夠輕松地將他們的 JavaScript 庫移植到 Python,而無需承擔(dān)用 Python 重寫庫并維護(hù)端口的昂貴負(fù)擔(dān),”Pringle 寫道。
此外,“JavaScript 也非常適合高度異步的工作負(fù)載,而 Python 則不然,”Pringle 在他的帖子中解釋道?!霸?Distributive,我們打算使用這個(gè)庫來執(zhí)行我們復(fù)雜的dcp-client庫,該庫是用 JS 編寫的,并支持 Web 堆棧上的分布式計(jì)算?!?/p>
同時(shí),Pringle 指出,PythonMonkey 旨在通過盡可能共享不可變的備份存儲(chǔ)來最大限度地減少內(nèi)存消耗和復(fù)制開銷。
PythonMonkey 路線圖
“PythonMonkey 的路線圖包括許多功能和改進(jìn),以擴(kuò)展其可用性,例如使用 esm 語法在 JavaScript 中導(dǎo)入 Python 模塊,XMLHttpRequest,實(shí)現(xiàn)獨(dú)立的事件循環(huán)而不依賴 Python 的事件循環(huán),以及對(duì) Node.js API 的支持,例如 fs、path、process,這將允許 Python 使用 NPM 包,例如express.js和socket.io,”Pringle 在去年發(fā)布時(shí)寫道。該公司已經(jīng)實(shí)現(xiàn)了其中大部分。
路線圖中另一個(gè)提出的目標(biāo)是將 PMJS 擴(kuò)展為一個(gè)完全集成的 Node.js 環(huán)境,它可以作為 Node.js 的直接替代品,并且還能夠從 JavaScript 中使用 Python 包。
通過這些計(jì)劃的增強(qiáng)功能,PythonMonkey 和 PMJS 旨在為開發(fā)人員提供一個(gè)完全集成的 Python-JS 環(huán)境。
比相關(guān)項(xiàng)目更優(yōu)越?
同時(shí),Pringle 將 PythonMonkey 與JS2PY、PyV8和Metacall等相關(guān)項(xiàng)目進(jìn)行了比較,強(qiáng)調(diào)了 PythonMonkey 在性能和功能方面的優(yōu)勢(shì)。
您可以在Google Colab上試用 PythonMonkey。