Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Python 3.11 in the web browser – A journey (PyC...

Python 3.11 in the web browser – A journey (PyCon DE 2022 Keynote)

https://2022.pycon.de/program/SBCNDY/

Python is ubiquitous, popular and runs almost everywhere – even on Mars. But there is one place that Python has not yet conquered: the browser. Python 3.11 may finally lay the foundation to make an old dream come true and have Python in the web browser.

In my talk I will explain how to cross-compile CPython 3.11 to Web Assembly and demonstrate how to run CPython in JavaScript engines. The talk will cover

- Why are some core developers and contributors working on Web Assembly port?
- What is WASM and how do builds for browser, node, and WASI differ? What are the features and limitations of different WASM targets?
- A short introduction to Python’s build system and how cross compiling works.
-What problems did we run into and how did we have to modify CPython’s sources for WASM?
-What is missing to make web browser support stable and usable in production?
- A comparison to existing solutions like Pyodide.
- What does WASM support mean for the community and PyPI packages?

Avatar for Christian Heimes

Christian Heimes

April 13, 2022
Tweet

More Decks by Christian Heimes

Other Decks in Programming

Transcript

  1. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    PyCon DE 2022 Keynote Python 3.11 in the web browser – A journey Christian Heimes Principal Software Engineer [email protected] [email protected] 1 @ChristianHeimes
  2. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    3 I have a confession to make… ▸ I have no experience with front-end development. ▸ I don't know how browsers work. ▸ I never learnt JavaScript. ▸ I started learning WASM a couple of months ago.
  3. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Python 3.11 in the web browser – A Journey 4 C WebAssembly bleeding edge
  4. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    5 How to run Python code in the browser? ▸ Execute Python code on the server ▸ Create Python interpreter in JavaScript ▸ Transpile Python code to JavaScript code Brython, source-to-source translation ▸ Browser plugin Netscape Plugin API (NPAPI), ActiveX, Google NaCl Pepper, … Adobe Flash ▸ Compile to WebAssembly
  5. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Katie Bell: How to run Python in the browser 6 Linux Conf AU 2022 ▸ 10 min: introduction ▸ 2 min: (compile and) run Python in the browser ▸ 28 min: making stdin work (keyboard input)
  6. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    8 How it's going 2022-04-05 / 2022-03-21 $ node --experimental-wasm-threads \ --experimental-wasm-bulk-memory \ python.js -m test -w == CPython 3.11.0a7+ (heads/bpo-40280-smaller-build-dirty:eb79f02293d, Apr 8 2022, 10:58:59) [Clang 15.0.0 (https://github.com/llvm/llvm-project 80ec0ebfdc5692a58e0832125f2c == Emscripten-1.0-wasm32-32bit little-endian == cwd: /python-wasm/cpython/builddir/emscripten-node/build/test_pyt hon_42æ == CPU count: 8 == encodings: locale=UTF-8, FS=utf-8 0:00:00 load avg: 0.10 Run tests sequentially 0:00:00 load avg: 0.10 [ 1/435] test_grammar 0:00:00 load avg: 0.10 [ 2/435] test_opcodes ... == Tests result: SUCCESS == 342 tests OK. 92 tests skipped. Total duration: 12 min 22 sec Tests result: SUCCESS
  7. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    9 Contributors ▸ Ethan Smith, Brett Cannon (python-wasm) ▸ Erlend Aasland (build system) ▸ Katie Bell (browser UI) ▸ Hood Chatham, Roman Yurchak (Pyodide) ▸ Sam Clegg, Thomas Balling (Emscripten) ▸ Paul m. p. P., Trey Hunner (early adopters) ▸ Lin Clark, Dan Callahan, David Beazley (talks) ▸ … and many more
  8. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    WebAssembly compilers operating systems 10
  9. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    caniuse.com/wasm ? 11 Modern browsers support two programming languages Wasmer wasmtime Node
  10. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    12 Compilers human code to native machine code C C++ Go Rust frontend frontend frontend frontend intermediate representation backend backend backend backend x86_64 i686 aarch64 armv7
  11. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    13 def line(a, x, b): return a * x + b double line(double a, double x, double b) { return a * x + b; } # elf64-x86-64 0: f2 0f 59 c1 mulsd xmm0,xmm1 4: f2 0f 58 c2 addsd xmm0,xmm2 8: c3 ret # elf32-littlearm 0: ee002b01 vmla.f64 d2, d0, d1 4: eeb00b42 vmov.f64 d0, d2 8: e12fff1e bx lr # elf64-s390 0: b3 1c 00 02 mdbr %f0,%f2 4: b3 1a 00 04 adbr %f0,%f4 8: 07 fe br %r14 a: 07 07 nopr %r7 c: 07 07 nopr %r7 e: 07 07 nopr %r7 Instruction Set Architecture (ISA) gcc -O2, objdump -d
  12. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    14 # line(a=3, x=5, b=2) local_vars = (3, 5, 2) def get_local(idx): push(local_vars[idx]) stack = [] push = stack.append pop = stack.pop def add(): right = pop() left = pop() result = left + right push(result) code = [ (get_local, 0), # load a=3 (get_local, 1), # load x=5 (mul,), # store 3*5 (get_local, 2), # load b=2 (add,), # store 15+2 ] Stack machine def mul(): right = pop() left = pop() result = left * right push(result) for instr, *args in code: instr(*args) result = pop() # 17
  13. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    15 # WASM text (WAT) (module (export "line" (func $line)) (func $line (param $a f64) (param $x f64) (param $b f64) (result f64) get_local $a get_local $x f64.mul get_local $b f64.add ) ) WebAssembly Text (WAT) >>> import dis >>> def line(a, x, b): ... return a * x + b ... >>> dis.dis(line) 2 0 LOAD_FAST 0 (a) 2 LOAD_FAST 1 (x) 4 BINARY_MULTIPLY 6 LOAD_FAST 2 (b) 8 BINARY_ADD 10 RETURN_VALUE BINARY_OP_MULTIPLY_FLOAT BINARY_OP_ADD_FLOAT
  14. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    16 Compilers human code to native machine code C C++ Go Rust frontend frontend frontend frontend intermediate representation backend backend backend backend x86_64 i686 aarch64 armv7 backend wasm32 Emscripten SDK
  15. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Opening a file on disk 17 open("file.txt") _io.TextIOWrapper _io.BufferedReader _io.FileIO // _io/fileio.c #include <fcntl.h> int fd = open( path, flags, ... ); glibc libc.so.6 open(path, ...); openat( dirfd, path, ... ); syscall( SYS_openat, dirfd, path, ... ); open Kernel sys_call_table ... VFS ext4fs block device SATA ... syscall
  16. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    18 Problems ▸ Linux, macOS, Windows, … have different APIs ▸ Linux syscall numbers are arch-specific x86_64: 257, i686: 295, ARM: 332, aarch64: 56, ... ▸ Browsers are sandboxed How to open a file safely on all platforms? (import "env" "__syscall_openat" (func $fimport$1 (param i32 i32 i32 i32) (result i32))) (import "wasi_snapshot_preview1" "fd_read" (func $fimport$2 (param i32 i32 i32 i32) (result i32))) (import "wasi_snapshot_preview1" "fd_close" (func $fimport$0 (param i32) (result i32)))
  17. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Opening a file on Emscripten / WASI 19 open("file.txt") _io.TextIOWrapper _io.BufferedReader _io.FileIO // _io/fileio.c #include <fcntl.h> int fd = open( path, flags, ... ); musl static libc.a open(path, ...); openat( dirfd, path, ... ); syscall( SYS_openat, dirfd, path, ... ); open Browser NodeJS WASM import / export WASI runtime MEMFS NODE raw FS
  18. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    20 JavaScript "Kernel code" Emscripten's glue layer var asmLibraryArg = { "__syscall_openat": ___syscall_openat, "fd_close": _fd_close, "fd_read": _fd_read }; var info = { 'env': asmLibraryArg, 'wasi_snapshot_preview1': asmLibraryArg, }; WebAssembly.instantiate(binary, info); function ___syscall_openat(dirfd, path, flags, varargs) { SYSCALLS.varargs = varargs; try { path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path); var mode = varargs ? SYSCALLS.get() : 0; return FS.open(path, flags, mode).fd; } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e; return -e.errno; }
  19. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    21 ▸ x86_64-unknown-linux-gnu ▸ x86_64-unknown-linux-musl ▸ armv7l-unknown-linux-gnueabihf ▸ aarch64-unknown-linux-android ▸ aarch64-apple-darwin ▸ i686-pc-windows-msvc ▸ sparcv9-sun-solaris ▸ wasm32-emscripten ▸ wasm32-wasi Target Triple(t) machine vendor os (abi) "CPU" "OS"
  20. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    22 What is WebAssembly ▸ "machine-independent machine code" ▸ binary (.wasm), text (.wat) with metadata ▸ designed to be portable and fast ▸ 4 value types (integer i32, i64; float f32, f64) ▸ ~200 instructions ▸ optional extension (128 bits vector ops, threading) ▸ wasm modules have ・ imports ・ exports ・ memory
  21. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    24 ./configure && make Autotools and Makefile $ ./configure checking for git... found checking build system type... x86_64-pc-linux-gnu checking host system type... x86_64-pc-linux-gnu checking for Python interpreter freezing... ./_bootstrap_python checking for python3.11... python3.11 checking Python for regen version... Python 3.11.0a6 checking for pkg-config... /usr/bin/pkg-config checking pkg-config is at least version 0.9.0... yes checking for --enable-universalsdk... no checking for --with-universal-archs... no checking MACHDEP... "linux" checking for gcc... gcc ... $ make gcc -c -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -std=c99 -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Werror=implicit-function-declaration -fvisibility=hidden -I./Include/internal -I. -I./Include -DPy_BUILD_CORE -o Programs/python.o ./Programs/python.c ... CC='gcc' LDSHARED='gcc -shared' OPT='-DNDEBUG -g -fwrapv -O3 -Wall' ./python -E ./setup.py build running build running build_ext ...
  22. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    configure 25 autoconf, m4 ▸ feature detection ・ compiler settings ・ platform settings ・ header files ・ libraries, packages ▸ create pyconfig.h, Makefile.pre ▸ makesetup Modules/Setup* ▸ create Makefile AC_CHECK_HEADERS([sys/random.h]) AC_CHECK_FUNCS([pthread_sigmask]) AC_CHECK_LIB([rt], [clock_nanosleep], [ AC_DEFINE([HAVE_CLOCK_NANOSLEEP], [1]) ]) AS_CASE([$ac_sys_system], [Emscripten], [EXEEXT=.js], [WASI], [EXEEXT=.wasm], ) // pyconfig.h #define HAVE_SYS_RANDOM_H 1 #define HAVE_PTHREAD_SIGMASK 1 #define HAVE_CLOCK_NANOSLEEP 1
  23. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Makefile 26 GNU make ▸ compile minimal core ▸ build "Programs/_freeze_module" ・ freeze "importlib_bootstrap.h" ▸ build "_bootstrap_python" ・ deep freeze stdlib core ▸ compile built-in modules ▸ build "python" executable ▸ compile shared extensions (distutils) ./python setup.py build BUILDPYTHON=python$(EXE) LINKCC=gcc -shared .PHONY: build_all build_all: $(BUILDPYTHON) $(BUILDPYTHON): Programs/python.o \ $(LINK_PYTHON_DEPS) $(LINKCC) $(PY_CORE_LDFLAGS) \ $(LINKFORSHARED) \ -o $@ Programs/python.o \ $(LINK_PYTHON_OBJS) \ $(LIBS) $(MODLIBS) $(SYSLIBS)
  24. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Cross-compile with Emscripten 27 EMSDK ▸ build system: x86_64-pc-linux-gnu ▸ target host: wasm32-unknown-emscripten ▸ cross compiler: emcc (Python script, Clang, LLVM, Binaryen, NodeJS) ▸ sysroot (headers, libraries) ▸ configure wrapper: emconfigure ▸ make wrapper: emmake Problem: _freeze_module, ./python setup.py
  25. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Cross compiling improvements 28 ▸ new wasm32 targets ▸ new --with-build-python ▸ re-implement _freeze_module in pure Python ▸ detect module deps in configure ▸ Modules/Setup.stdlib ▸ improved config.cache ▸ VPATH / out-of-tree build fixes ▸ new --with-emscripten-target # Makefile MODULE_ZLIB=yes MODULE_ZLIB_CFLAGS= MODULE_ZLIB_LDFLAGS=-lz MODULE__SCPROXY=n/a # Setup.stdlib.in @MODULE__SCPROXY_TRUE@_scproxy _scproxy.c @MODULE_ZLIB_TRUE@zlib zlibmodule.c # Setup.stdlib #_scproxy _scproxy.c zlib zlibmodule.c
  26. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    29 Build Python for browsers $ git clone https://github.com/python/cpython.git $ cd cpython $ mkdir -p builddir/build $ mkdir -p builddir/emscripten-browser $ cd builddir/emscripten-browser $ CONFIG_SITE=config.site-wasm32-emscripten \ emconfigure ../../configure \ --host=wasm32-unknown-emscripten \ --build=x86_64-pc-linux-gnu \ --with-emscripten-target=browser \ --with-build-python=$(pwd)/../build/python $ emmake make $ cd builddir/build $ ../../configure $ make $ ls python python $ cd ../.. $ ls python* python.data python.html python.js python.wasm python.worker.js # config site ac_cv_func_eventfd=no ac_cv_func_memfd_create=no
  27. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    30 Build container $ git clone .../cpython.git $ cd cpython $ podman run --rm -ti \ -v $(pwd):/python-wasm/cpython:Z \ quay.io/tiran/cpythonbuild:emsdk3 $ ./build-python-build.sh $ ./build-python-emscripten-browser.sh $ ./build-python-emscripten-node.sh $ ./Tools/wasm/wasm_webserver.py github.com/ethanhs/python-wasm/actions
  28. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    repl.ethanhs.me cheimes.fedorapeople.org/python-wasm 31
  29. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    > 60 CPython pull requests 32 https://bugs.python.org/issue40280 $ ./run-python-node.sh -c 'import _zoneinfo' builddir/emscripten-node/python.js:147 throw ex; ^ Error [RuntimeError]: function signature mismatch at module_dealloc (wasm-function[1908]) at _Py_Dealloc (wasm-function[1986]) at insertdict (wasm-function[1652]) at _PyDict_SetItem_Take2 (wasm-function[1641]) at dict_ass_sub (wasm-function[1733]) at PyObject_SetItem (wasm-function[495]) at finalize_modules (wasm-function[3677]) at Py_FinalizeEx (wasm-function[3674]) at Py_RunMain (wasm-function[4057]) at pymain_main (wasm-function[4060]) Emitted 'error' event on process instance at: at emitUnhandledRejectionOrErr at MessagePort.[nodejs.internal.kHybridDispatch] at MessagePort.exports.emitMessage --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -2612,7 +2612,7 @@ static PyTypeObject PyZoneInfo_ZoneInfoType = { // Specify the _zoneinfo module static PyMethodDef module_methods[] = {{NULL, NULL}}; static void -module_free(void) +module_free(void *m) {
  30. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Emscripten bug fixes 33 https://github.com/ethanhs/python-wasm/issues/43 ▸ Segfault in freeaddrinfo, memory leak in getaddrinfo ▸ clock_nanosleep() sleeps forever ▸ strftime() mishandles quoted percent, e.g. %%z ▸ week number of strftime("%U") and %W ▸ Fix edge case in strftime %V (ISO 8601 week number) ▸ opendir() leak file descriptor on error ▸ msync() on anon mapping fails with JS TypeError ▸ utimensat() sets wrong atime and mtime ▸ Blocking socket accept() fails with JS TypeError
  31. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    34 Browser ❌ subprocess (fork, exec) ❌ threads ❌ file system (only MEMFS) 🚧 shared extension modules ❌ PyPI packages ❓ sockets ❌ urllib, asyncio ❌ signals Node ❌ subprocess (fork, exec) ✅ threads ✅ file system (Node raw FS) 🚧 shared extension modules ❌ PyPI packages ❓ sockets ❌ urllib, asyncio 🚧 signals Supported features CPython 3.11 alpha 7 Pyodide ❌ subprocess (fork, exec) 🚧 threads ✅ file system (IDB, Node, …) ✅ shared extension modules ✅ PyPI packages ✅ WebAPI fetch / WebSocket ✅ signals
  32. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Frequently asked questions 35 ▸ Can I replace JavaScript with Python? No ▸ How large is the WASM binary? ~ 4.5 MB compressed with static, reduced stdlib 4.6 MB wasm (compressed 1.5 MB), 2.7 MB stdlib bytecode, some JS ▸ How fast is CPython in WebAssembly? 1.5 to 3 times slower than native (without optimizations) ▸ Will Python core officially support WASM? Not yet, maybe in the future ▸ What about Pyodide?
  33. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    38 Executable docs and examples Trey Hunner: www.pythonmorsels.com/paste/
  34. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    JupyterLite Jupyter ❤ WebAssembly ❤ Python jupyterlite.github.io/demo/lab/ 40
  35. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    41 Electron Visual Studio Code vscode.dev github.dev
  36. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    43 ▸ cocos2d ▸ freetype ▸ libGL ▸ libGL-emu ▸ libGL-webgl2 ▸ libjpeg ▸ libpng ▸ libwasmfs ▸ libwebgpu_cpp ▸ mpg123 ▸ ogg ▸ SDL2 ▸ SDL2_gfx ▸ SDL2_image ▸ SDL2_mixer ▸ SDL2_ttf ▸ vorbis ▸ zlib Emscripten ports Emscripten ported libraries ❓
  37. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Paul m. p. P. https://pmp-p.github.io/pygame-wasm/ 44
  38. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    WASI 45 WebAssembly System Interface ▸ no browser or JavaScript ▸ small runtimes (wasmtime 18 MB Rust binary) ▸ sandboxed ▸ capability-based security properties ▸ JIT or AOT (ahead-of-time) compilation to native code ▸ Wizer (WebAssembly Pre-Initializer) Wasmer wasmtime
  39. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    high-performance, secure, sandboxed Python edge computing 46
  40. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    47 ▸ numpy-1.22.3-cp310-cp310-win_amd64.whl ▸ numpy-1.22.3-cp310-cp310-win32.whl ▸ numpy-1.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl ▸ numpy-1.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl ▸ numpy-1.22.3-cp310-cp310-macosx_11_0_arm64.whl ▸ numpy-1.22.3-cp310-cp310-macosx_10_14_x86_64.whl ▸ numpy-1.22.3-cp39-cp39-win_amd64.whl ▸ numpy-1.22.3-cp39-cp39-win32.whl ▸ numpy-1.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl ▸ numpy-1.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl ▸ numpy-1.22.3-cp39-cp39-macosx_11_0_arm64.whl ▸ numpy-1.22.3-cp39-cp39-macosx_10_14_x86_64.whl ▸ numpy-1.22.3-cp38-cp38-win_amd64.whl ▸ numpy-1.22.3-cp38-cp38-win32.whl ▸ numpy-1.22.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl ▸ numpy-1.22.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl ▸ numpy-1.22.3-cp38-cp38-macosx_11_0_arm64.whl ▸ numpy-1.22.3-cp38-cp38-macosx_10_14_x86_64.whl numpy-1.22.3-cp311-abi3-wasm32.whl ▸ Python >= 3.11, <4 ▸ Any OS ▸ Any CPU architecture Python binary extension hell 3 Python versions, 3 CPU archs, 3 OSes: 18 wheels
  41. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    48 wasmtime for Python github.com/bytecodealliance/wasmtime-py from functools import partial from wasmtime import Store, Module, Instance store = Store() module = Module(store.engine, """ (module (export "line" (func $line)) (func $line (param $a f64) (param $x f64) (param $b f64) (result f64) get_local $a get_local $x f64.mul get_local $b f64.add ) )""") instance = Instance(store, module, []) line = instance.exports(store)["line"] line = partial(line, store) print(line(3., 5., 2.)) # 17.0
  42. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    Python on WebAssembly … a lot of opportunities! … a lot of work left to do! 49 ❤
  43. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    50 ▸ https://bugs.python.org/issue40280 ▸ https://github.com/ethanhs/python-wasm ▸ https://pyodide.org/ ▸ https://pmp-p.github.io/pygame-wasm ▸ https://jupyterlite.github.io/demo/lab/ ▸ https://assets.metacade.com/emulators/wi n311vr.html ▸ https://pypi.org/project/wasmtime/ ▸ https://mbebenita.github.io/WasmExplorer/ ▸ https://wasdk.github.io/WasmFiddle/ ▸ David Beazley Keynote PyCon India 2019 https://youtu.be/VUT386_GKI8 ▸ Lin Clark: A Cartoon Intro to WebAssembly https://youtu.be/HktWin_LPf4 Links & Resources https://repl.ethanhs.me https://cheimes.fedorapeople.org/python-wasm https://speakerdeck.com/tiran/
  44. PyConDE 2022: Python in the Browser, @ChristianHeimes, CC BY-SA 4.0

    linkedin.com/company/red-hat youtube.com/user/RedHatVideos facebook.com/redhatinc twitter.com/RedHat 51 Red Hat is the world’s leading provider of enterprise open source software solutions. Award-winning support, training, and consulting services make Red Hat a trusted adviser to the Fortune 500. Thank you