LWN: Pyodide - 浏览器中的Python!

Linux News搬运工

共 3782字,需浏览 8分钟

 ·

2021-06-06 22:40

关注了就能看到更多这么棒的文章哦~

Pyodide: Python for the browser

By Jake Edge
May 11, 2021
DeepL assisted translation
https://lwn.net/Articles/855875/

Python 社区中许多人一直以来都希望能够在浏览器中运行 Python。不过目前来看 JavaScript 已经很好地巩固了作为 web 和浏览器的内嵌语言的地位。Pyodide 项目则是通过将现有的 CPython 解释器编译为 WebAssembly 并在浏览器的 JavaScript 环境中运行这个编译出来的二进制文件,这提供了一种在浏览器中运行 Python 的方法。Pyodide 是 Mozilla 的 Iodide 项目的一部分(此项目已被淘汰),但 Pyodide 现在被剥离出来作为一个社区驱动的项目在继续。

History

Iodide 是于 2019 年推出的,旨在创建一个用于科学探索(scientific exploration)和可视化(visualizaiton)的浏览器内嵌的 notebook,类似于 Jupyter 和 JupyterLab。在当时宣布此项目的文章中,强调了 JavaScript 和科学计算之间的互不匹配(毕竟大多数现有的科学计算环境都是基于 Python 的),因此引出了 Pyodide :

当我们开始考虑如何让网络更好地服务于科学家时,我们关注的是如何让 Javascript 工作得更好,比如将现有的科学相关库函数都编译成 WebAssembly,并将它们封装成易于使用的 JS API。当我们向 Mozilla 的 WebAssembly wizards 提出这个建议时,他们提出了一个更雄心勃勃的想法:如果许多科学家喜欢 Python,那就把整个 Python science stack 都编译出来在 WebAssembly 中运行,就能满足它们的需求了。

这听起来似乎是一个大项目,但 Mike Droettboom 只花了两周时间就让 Python 在 Iodide notebook 中运行起来了。在接下来的几个月里,他和其他人增加了对那些最流行的 Python 科学软件包的支持(其中许多是用 C 语言实现的,如 CPython 解释器),比如 NumPy、Matplotlib、pandas、SciPy 和 scikit-learn。有人担心 Pyodide 的性能,但实际证明性能并不会阻碍这个使用场景:

在 Javascript 虚拟机中运行 Python 解释器会带来一些性能损失,但这种损失出乎意料地小。在我们的性能测试中,在 Firefox 上运行比本地运行慢 1-12 倍,在 Chrome 上则慢 1-16 倍。这个体验表明,对于互动式的研究来说已经足够可用了。

在 Iodide 宣布一个月后,Droettboom 发布了更多关于 Pyodide 的细节。它是用 Emscripten 构建的,Emscripten 提供了一种将 C 和 C++编译成 WebAssembly 的方法,同时还有 "一个兼容层用来使浏览器感觉像一个本地计算环境"。这个兼容层(compatibility layer)对于像 Python 这样的工具来说是非常必要的:

如果你仅仅把这个 WebAssembly 加载到浏览器中是不够的,对 Python 解释器来说,它的工作环境会和直接在本地操作系统上运行时有很大差异。例如,网络浏览器中没有文件系统(用来加载和保存文件)。幸运的是,emscripten 提供了一个用 JavaScript 编写的虚拟文件系统,Python 解释器可以使用这个。默认情况下,这些虚拟 "文件" 会存放在浏览器标签页相关的 DDR 内存中,当你离开页面时它们就会消失。(emscripten 也提供了一种方法让文件系统在浏览器的本地持久存储中存储内容,但 Pyodide 没有使用)。

通过模拟文件系统以及标准计算环境的其他功能,emscripten 使得将现有的项目转移到网络浏览器上成为可能,而且改动少得惊人。

为了在浏览器环境中做实际有用的工作,程序需要访问浏览器的文档对象模型(DOM,Document Object Model),这是由现成的 JavaScript API 提供的。这意味着 Python 和 JavaScript 代码需要有多种交互合作。大多数的基本类型(如数字、数组、字符串)可以相对容易地在两种语言之间进行映射和转换,但是 Python 将 object 和 dict 类型视为两种不同的类型,而 JavaScript 在一定程度上来说并不区分,也就是所有对象都可以被视为 dict。为了处理这种差异,两种语言中的 object 类型和 Python 中的 dict 类型都会用另一种语言中的代理(proxy)来表示,这样就允许每种语言完全访问另一种语言的数据类型了。

这篇文章在谈到从 Python 中访问 DOM 对象的方法时也举了一个关于这种代理方式的例子:

from js import document
x = document.getElementById("myElement")

document 对象是一个 JsProxy 类型,它会负责分发给正确的 API 调用,而 Pyodide 本身不需要直接参与进来:

所有这些都会经过代理,代理会即时查找这个 document 对象能做些什么。Pyodide 不需要包括一个完整列表来记录浏览器中拥有的所有 Web API。

考虑到 NumPy 等经常需要处理大数据集合,有必要提供一种高效的多维数组(multi-dimensional array)类型,让它在 JavaScript 和 Python 之间直接共享。毕竟在两种语言之间复制海量数据数组会对性能产生很大的影响,而且还可能会过度消耗浏览器的可用内存数量。所以我们创造了一个存储在两者共享的 heap 区域上的数组类型,在两种语言之间传递的时候仅仅需要复制一小段用来描述数组的 metadata 就好。

Present

两年过去了,Pyodide 已经取得了很大的进展。今年 4 月 22 日的时候发布了 0.17 版本。同时,它宣布该项目正在变得更加独立,拥有一个自己的 GitHub 仓库、一套借鉴自 CPython 的管理方式,以及一组来自 Rust 并参考了其他内容的行为准则。同时,Iodide 项目已经停止,但 Pyodide 目前来看并不仅仅是 Iodide 的一部分:"Pyodide 吸引了社区的大量兴趣,仍然在积极开发中,并在 Mozilla 之外的许多项目中得到了应用。"

0.17 版本中有许多有趣功能。支持了 Python 的 asyncio,因此 Python coroutine(协程)可以在浏览器 event loop(事件循环)中运行;Python 中可以等待 JavaScript 中的 Promise,反之亦然。错误处理也得到了改进,因此 Python 产生的异常可以在 JavaScript 中被捕获,同样,反过来也是可以做到的。

并且采用了新版本的 Emscripten,这有助于缩小所需生成二进制文件的大小("SciPy 包从 92MB 极其显著地降低到 15MB")。在性能方面也有帮助。使用最新的工具链(包括 LLVM 后端)会使运行时提速 25-30%。总的来说,自 2019 年宣布以来,它的性能已经有了很大改善。"性能取决于测试程序类型,大概比起本地直接执行最多慢 3 到 5 倍。"

与大多数(也许是所有?)项目一样,Pyodide 正在寻找有兴趣以各种方式作出贡献的人。"贡献的方式有很多,包括贡献代码、改进文档、添加新的 package 进来,也包括在你的应用程序中使用 Pyodide 并提供反馈。" 项目也提供了一个 roadmap 列出一些未来的计划。

正如 Mozilla 的 Dan Callahan 在 PyCon 2018 的主题演讲中所说,Python 可能会开始减少应用场景,因为人们使用的平台正在发生改变。虽然笔记本电脑、台式机和服务器都能很好地支持 Python,但许多人只使用手机和平板电脑,而在这些平台上 Python 可以算是缺席了。但是在所有这些设备上都有一个统一的平台(也有可能未来会有其他的统一平台出现在这些设备上),那就是 "web" 这个平台。如果 Python 想在里面继续掺和进来,就需要一个合理的支持浏览器的方式。Pyodide 可能是实现这个目标过程中的重要一环。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~



浏览 55
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报