Compilando Python en WebAssembly

90

He leído que es posible convertir el código Python 2.7 a Web Assembly, pero no puedo encontrar una guía definitiva sobre cómo hacerlo.

Hasta ahora he compilado un programa en C para Web Assembly usando Emscripten y todos sus componentes necesarios, así que sé que está funcionando (guía utilizada: http://webassembly.org/getting-started/developers-guide/ )

¿Cuáles son los pasos que debo seguir para hacer esto en una máquina Ubuntu? ¿Tengo que convertir el código Python a código de bits LLVM y luego compilarlo usando Emscripten? Si es así, ¿cómo puedo lograrlo?

Robbie
fuente
1
@guettli github.com/pypyjs/pypyjs/issues/145
denfromufa
1
Echa un vistazo a pyodide: hacks.mozilla.org/2019/04/…
Alex
1
Pyodide lleva el tiempo de ejecución de Python al navegador a través de WebAssembly: github.com/iodide-project/pyodide
guettli

Respuestas:

146

WebAssembly vs asm.js

Primero, veamos cómo, en principio, WebAssembly es diferente de asm.js , y si existe la posibilidad de reutilizar el conocimiento y las herramientas existentes. A continuación, se ofrece una descripción general bastante buena:

Recapitulemos, WebAssembly (MVP, ya que hay más en su hoja de ruta , aproximadamente):

  • es un formato binario de AST con escritura estática, que puede ser ejecutado por motores JavaScript existentes (y por lo tanto compatible con JIT o AOT compilado),
  • es un 10-20% más compacto (comparación con gzip) y un orden de magnitud más rápido de analizar que JavaScript,
  • puede expresar más operaciones de bajo nivel que no encajan en la sintaxis de JavaScript, leer asm.js (por ejemplo, enteros de 64 bits, instrucciones especiales de CPU, SIMD, etc.)
  • es convertible (hasta cierto punto) a / desde asm.js.

Por lo tanto, actualmente WebAssembly es una iteración de asm.js y se dirige solo a C / C ++ (y lenguajes similares).

Python en la Web

No parece que GC sea lo único que impide que el código Python se dirija a WebAssembly / asm.js. Ambos representan código escrito estáticamente de bajo nivel, en el que el código Python no se puede representar (de manera realista). Como la cadena de herramientas actual de WebAssembly / asm.js se basa en LLVM, un lenguaje que se puede compilar fácilmente a LLVM IR se puede convertir a WebAssembly / asm.js. Pero, lamentablemente, Python también es demasiado dinámico para encajar en él, como lo demuestran Unladen Swallow y varios intentos de PyPy.

Esta presentación de asm.js tiene diapositivas sobre el estado de los lenguajes dinámicos . Lo que significa es que actualmente solo es posible compilar VM completa (implementación del lenguaje en C / C ++) en WebAssembly / asm.js e interpretar (con JIT cuando sea posible) las fuentes originales. Para Python hay varios proyectos existentes:

  1. PyPy: PyPy.js ( charla del autor en PyCon ). Aquí está el repositorio de lanzamiento . El archivo JS principal ,, pypyjs.vm.jstiene 13 MB (2 MB después gzip -6) + Python stdlib + otras cosas.

  2. CPython: pyodide , EmPython , CPython-Emscripten , EmCPython , etc. empython.jses de 5,8 MB (2,1 MB después gzip -6), sin stdlib.

  3. Micropython: esta bifurcación .

    No había ningún archivo JS construido allí, así que pude construirlo con trzeci/emscripten/una cadena de herramientas Emscripten lista para usar. Algo como:

     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 
    

    Produce micropython.jsde 1,1 MB (225 KB después gzip -d). Esto último ya es algo a considerar, si solo necesita una implementación muy compatible sin stdlib.

    Para producir WebAssembly construir puede cambiar la línea 13 de la Makefilea

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

    Luego make -jproduce:

     113 KB micropython.js
     240 KB micropython.wasm
    

    Puede mirar la salida HTML de emcc hello.c -s WASM=1 -o hello.html, para ver cómo usar estos archivos.

    De esta manera, también puede potencialmente construir PyPy y CPython en WebAssembly para interpretar su aplicación Python en un navegador compatible.

Otra cosa potencialmente interesante aquí es Nuitka , un compilador de Python a C ++. Potencialmente, puede ser posible construir su aplicación Python en C ++ y luego compilarla junto con CPython con Emscripten. Pero prácticamente no tengo idea de cómo hacerlo.

Soluciones

Por el momento, si está creando un sitio web convencional o una aplicación web donde descargar un archivo JS de varios megabytes es apenas una opción, eche un vistazo a los transpilers de Python a JavaScript (por ejemplo, Transcrypt ) o implementaciones de Python de JavaScript (por ejemplo, Brython ). O pruebe suerte con otros de la lista de lenguajes que se compilan en JavaScript .

De lo contrario, si el tamaño de la descarga no es un problema y está listo para abordar muchos aspectos ásperos, elija entre los tres anteriores.

Actualización del tercer trimestre de 2020

  1. El puerto de JavaScript se integró en MicroPython. Vive en ports / javascript .

  2. El puerto está disponible como un paquete npm llamado MicroPython.js . Puedes probarlo en RunKit .

  3. Hay una implementación de Python desarrollada activamente en Rust, llamada RustPython . Debido a que Rust admite oficialmente WebAssembly como destino de compilación , no es de extrañar que haya un enlace de demostración justo en la parte superior del archivo Léame. Aunque es temprano. Su descargo de responsabilidad sigue.

    RustPython se encuentra en una fase de desarrollo y no debe usarse en producción o en una configuración intolerante a fallas.

    Nuestra compilación actual solo admite un subconjunto de la sintaxis de Python.

saaj
fuente
1
Esos tamaños .js y .wasm no son realmente justos. La compresión de transmisión está bien soportada y podría usarse para reducir el tamaño de ambos. ¿Qué tamaño tienen los mismos archivos comprimidos con gzip? Aparte de eso, buena respuesta.
enigmaticFysicist
Así que quería agregar que en 2020, parece que el pyodide es lo más cercano que OP está buscando. Es el tiempo de ejecución de Python en el ensamblaje web (asumiría que pusieron C y luego Python en wasm). También es compatible con varias bibliotecas. Además, parece bastante fácil de usar.
David Frick
3

Esto no será posible hasta que el ensamblaje web implemente la recolección de basura. Puede seguir el progreso aquí: https://github.com/WebAssembly/proposals/issues/16

Malcolm White
fuente
17
No necesariamente. Puede implementar GC, y especialmente el recuento de referencias, como lo usa Python IIRC, además de Wasm. En principio, debería poder tomar CPython y compilarlo en Wasm usando Emscripten.
Andreas Rossberg
1
Mi opinión de OP fue que querían usar las herramientas existentes: implementar cpython GC sobre wasm suena como un proyecto en sí mismo
Malcolm White
3
No debería tener que hacer nada más, solo haga que CPython compile. Ya contiene la implementación RC, AFAICT.
Andreas Rossberg
3

En resumen: hay transpiladores, pero no se puede convertir automáticamente ningún Python arbitrario en Web Assembly, y dudo que pueda hacerlo durante mucho tiempo. Aunque teóricamente los lenguajes son igualmente poderosos, y la traducción manual siempre es posible, Python permite algunas estructuras de datos y modos expresivos que requieren un compilador (o transpilador) entre lenguajes muy inteligente [ver más abajo]. Una solución podría ser Python to C to Web Assembly ya que la tecnología python-to-C es moderadamente madura, pero eso tampoco va a funcionar en general, ya que Python-to-C también es frágil (ver más abajo).

WebAssembly está dirigido específicamente a lenguajes similares a C, como puede ver en http://webassembly.org/docs/high-level-goals/

La traducción de Python a C se puede hacer con herramientas como PyPy, que ha estado en desarrollo durante mucho tiempo, pero que aún no funciona para código Python arbitrario. Hay varias razones para esto:

  1. Python tiene algunas estructuras de datos muy útiles, abstractas y agradables, pero son difíciles de traducir a código estático.
  2. Python depende de la recolección de basura dinámica.
  3. La mayoría del código de Python depende en gran medida de varias bibliotecas, cada una de las cuales tiene sus propias peculiaridades y problemas (como estar escrito en C o incluso en ensamblador).

Si observa más detenidamente por qué Python-to-C (o Python to C ++) ha sido tan complicado, puede ver las razones detalladas detrás de esta respuesta concisa, pero creo que eso está fuera del alcance de su pregunta.

GregD
fuente