将 Python 编译为 WebAssembly

Compiling Python to WebAssembly

我了解到可以将 Python 2.7 代码转换为 Web Assembly,但我找不到关于如何转换的权威指南。

到目前为止,我已经使用 Emscripten 及其所有必要组件将 C 程序编译为 Web Assembly,所以我知道它正在运行(使用的指南:http://webassembly.org/getting-started/developers-guide/

要在 Ubuntu 机器上执行此操作,我必须执行哪些步骤?我是否必须将 python 代码转换为 LLVM 位码,然后使用 Emscripten 对其进行编译?如果是这样,我将如何实现?

在 web assembly 实现垃圾收集之前,这是不可能的。您可以在此处关注进度:https://github.com/WebAssembly/proposals/issues/16

WebAssembly 与 asm.js

首先我们来看看WebAssemblyasm.js在原理上有什么区别,有没有潜力重用现有知识和工具。以下给出了很好的概述:

让我们概括一下,WebAssembly(MVP,因为 its roadmap 上还有更多内容,大致):

  • 是具有静态类型的 AST 的二进制格式,可以由现有的 JavaScript 引擎执行(因此支持 JIT 或编译 AOT),
  • 它比 JavaScript、
  • 紧凑 10-20%(gzip 比较)并且解析速度快一个数量级
  • 它可以表达更多不适合JavaScript语法的低级操作,阅读asm.js(例如64位整数,特殊CPU指令,SIMD等)
  • 可转换(在某种程度上)to/from asm.js.

因此,目前 WebAssembly 是 asm.js 的迭代,并且仅针对 C/C++(和类似语言)。

Python 网络

看起来 GC 并不是唯一阻止 Python 代码以 WebAssembly/asm.js 为目标的东西。两者都代表低级静态类型代码,其中 Python 代码不能(实际上)被代表。由于目前WebAssembly/asm.js的工具链是基于LLVM的,所以可以将一种可以轻松编译为LLVM IR的语言转换为WebAssembly/asm.js。但遗憾的是,Python 过于动态,无法适应它,正如 PyPy 的 Unladen Swallow and several attempts 所证明的那样。

此 asm.js 演示文稿有 slides about the state of dynamic languages。这意味着目前只能将整个 VM(C/C++ 中的语言实现)编译为 WebAssembly/asm.js 并解释(尽可能使用 JIT)原始源代码。 Python 有几个现有项目:

  1. PyPy:PyPy.js (author's talk at PyCon). Here's release repo。主 JS 文件 pypyjs.vm.js 为 13 MB(gzip -6 后为 2MB)+ Python stdlib + 其他内容。

  2. CPython:pyodide, EmPython, CPython-Emscripten, EmCPython,等等。empython.js 是 5.8 MB(gzip -6 之后是 2.1 MB),没有标准库。

  3. 微蟒:this fork.

    那里没有内置的 JS 文件,所以我可以使用 trzeci/emscripten/ 一个现成的 Emscripten 工具链来构建它。类似于:

     git clone https://github.com/matthewelse/micropython.git
     cd micropython
     docker run --rm -it -v $(pwd):/src trzeci/emscripten bash
     apt-get update && apt-get install -y python3
     cd emscripten
     make -j
     # to run REPL: npm install && nodejs server.js 
    

    它产生 1.1 MB 的 micropython.js(在 gzip -d 之后为 225 KB)。如果您只需要非常合规的实现而不需要 stdlib,那么后者已经是需要考虑的问题了。

    要生成 WebAssembly 构建,您可以将 Makefile 的第 13 行更改为

     CC = emcc -s RESERVED_FUNCTION_POINTERS=20 -s WASM=1
    

    然后 make -j 产生:

     113 KB micropython.js
     240 KB micropython.wasm
    

    您可以查看 emcc hello.c -s WASM=1 -o hello.html 的 HTML 输出,了解如何使用这些文件。

    通过这种方式,您还可以在 WebAssembly 中构建 PyPy 和 CPython,以便在兼容的浏览器中解释您的 Python 应用程序。

这里另一个可能有趣的东西是 Nuitka,一个 Python 到 C++ 的编译器。有可能将您的 Python 应用程序构建为 C++,然后使用 Emscripten 将其与 CPython 一起编译。但实际上我不知道该怎么做。

解决方案

目前,如果您正在构建一个传统的网站或 Web 应用程序,而下载几兆字节的 JS 文件几乎是一种选择,请查看 Python-to-JavaScript 转译器(例如 Transcrypt) or JavaScript Python implementations (e.g. Brython). Or try your luck with others from list of languages that compile to JavaScript.

否则,如果下载大小不是问题,并且您已准备好解决很多问题,请在以上三个选项中进行选择。

2020 年第 3 季度更新

  1. JavaScript 端口 was integrated 进入 MicroPython。它住在 ports/javascript.

  2. 该端口可作为名为 MicroPython.js 的 npm 包使用。 您可以在 RunKit.

    中试用
  3. 在 Rust 中有一个积极开发的 Python 实现,称为 RustPython。因为 Rust 官方支持 WebAssembly 作为 编译目标,难怪demo link就在 自述文件的顶部。虽然,现在还早。他们的免责声明如下。

    RustPython is in a development phase and should not be used in production or a fault intolerant setting.

    Our current build supports only a subset of Python syntax.

简而言之:有转译器,但你不能自动将任意 Python 转换为 Web Assembly,我怀疑你在未来很长一段时间内是否能够做到这一点。尽管理论上这些语言同样强大,并且总是可以进行手动翻译,但 Python 允许某些数据结构和表达模式需要非常智能的跨语言编译器(或转译器)[见下文]。解决方法可能是 Python to C to Web Assembly,因为 python-to-C 技术已经相当成熟,但这通常也行不通,因为 Python-to-C 是也很脆弱(见下文)。

WebAssembly 专门针对类 C 语言,如您在 http://webassembly.org/docs/high-level-goals/

中所见

从 Python 到 C 的转换可以使用像 PyPy 这样的工具来完成,它已经开发了很长时间,但对于任意 Python 代码仍然不起作用。这有几个原因:

  1. Python 有一些非常方便、抽象和漂亮的数据结构,但它们很难转化为静态代码。
  2. Python 取决于动态垃圾回收。
  3. 大多数 Python 代码在很大程度上依赖于各种库,每个库都有自己的怪癖和问题(例如用 C 甚至汇编程序编写)。

如果你更仔细地研究为什么 Python-to-C(或 Python 到 C++)如此棘手,你可以看到这个简洁答案背后的详细原因,但我认为这是超出你的问题范围。

首先想到的是PyPy和rpython,然后我发现

https://github.com/soIu/rpython

我们无法将任意 python 代码转换为 rpython。但是,如果您需要替代语言而不是 rust 或 AssemblyScript,rpython 可能是一个方向。

因此,我可以将 python/js/rust 与 wasm 作为沙箱一起使用,并使用非常 pythonic 的语言(例如 rpython)进行编码。至少我不用担心整数溢出。

Python 对于 WASM 来说太动态了,结果你只能得到在 WASM 中实现的解释器,而不是你的代码被编译的。

另一种方式,你可以尝试使用Python作为工具语言,将你自己的模型编译器写入WASM。我的意思不是 source-to-WASM 编译,而是将应用程序模型转换为 WASM 或其他 low-level 语言的编译器,例如 Rust 或 C++。

这样的模型可以作为一个数据结构来实现,该数据结构由描述应用程序部分的对象组合而成:模块、类、API 界面、GUI 元素等。此类对象必须具有代码生成g “知道”如何将其状态和行为写入 low-level 代码片段的方法。

接下来,您可以通过将此类生成的代码片段组合到可以使用 WASM-enabled 编译器或仅 WAT 文件构建的项目中来手动构建您的应用程序。