Funcnodes in pyodide
Project description
funcnodes-pyodide
Run FuncNodes completely in the browser by executing the backend worker inside Pyodide (Python compiled to WebAssembly) and driving it from the standard React Flow editor UI.
This package exists so FuncNodes workflows can be:
- Embedded as live, interactive examples in static sites / documentation (no server-side worker needed).
- Shipped as a “try it in your browser” demo.
- Used in environments where running a Python worker process is inconvenient, but browser-only execution is acceptable.
In the “normal” FuncNodes architecture, the React UI talks to a Python
funcnodes-workerprocess via WebSockets. Here, the “worker process” is replaced by a browser Web Worker that boots Pyodide, installs the needed Python packages, and then runs aRemoteWorker-compatible worker inside that Pyodide interpreter.
What’s in this folder
This repo subtree contains two deliverables plus a prebuilt demo:
1) Python package: funcnodes-pyodide (AGPL-3.0)
Located in src/funcnodes_pyodide/.
Key pieces:
funcnodes_pyodide.worker.PyodideWorker- A minimal
funcnodes_worker.RemoteWorkertransport that doesn’t use sockets. - Instead it forwards JSON + binary messages to a JavaScript “receiver” (the Web Worker global scope) via
receivepy(...)/receivepy_bytes(...).
- A minimal
funcnodes_pyodide.patch.patch()- Disables file-based logging handlers (Pyodide environments typically don’t have a normal writable filesystem).
- This patch auto-runs on import when
sys.platform == "emscripten".
python -m funcnodes_pyodide- Serves the prebuilt static demo UI from
src/funcnodes_pyodide/static/on a random free local port.
- Serves the prebuilt static demo UI from
2) JavaScript/TypeScript package: @linkdlab/funcnodes_pyodide_react_flow (MIT)
Located in src/react/.
Key pieces:
FuncnodesPyodideWorker(src/react/src/pyodineworker.ts)- A
@linkdlab/funcnodes_react_flow-compatible worker implementation. - Talks to a (Shared)WebWorker that runs the Pyodide runtime + Python worker.
- A
- Web Worker runtime (
src/react/src/pyodideWorkerLogic.mts,src/react/src/pyodideWorkerLayout.mts)- Loads Pyodide via dynamic
import(...). - Uses
micropipto install Python packages at runtime. - Imports
funcnodes_pyodide, creates Python worker instances viafuncnodes_pyodide.new_worker(...), and bridges messages between JS ↔ Python worker.
- Loads Pyodide via dynamic
3) Prebuilt static demo bundle
Located in src/funcnodes_pyodide/static/ and includes:
index.htmlfuncnodes_pyodide_react_flow.es.js(+.iife.js)funcnodes_pyodide_react_flow.css
How it works (high level)
- The page loads the FuncNodes React Flow UI bundle.
- The UI creates a
FuncnodesPyodideWorkerinstance (JS). - That JS worker spins up a (Shared)WebWorker.
- The WebWorker:
- Dynamically imports Pyodide (
pyodide.mjs), - Installs Python dependencies via
micropip, - Imports
funcnodes_pyodide, - Creates a
PyodideWorker(Python) and attaches the WebWorker as its “receiver”.
- Dynamically imports Pyodide (
- From then on, the UI uses the regular FuncNodes worker protocol:
- UI → WebWorker → Python
RemoteWorker.receive_message(...) - Python events/results → WebWorker → UI
- UI → WebWorker → Python
Quickstart: run the local demo page
Recommended environment variables (keep caches/config local):
UV_CACHE_DIR=.cache/uvFUNCNODES_CONFIG_DIR=.funcnodes
From this repo (this folder contains its own uv.lock):
cd funcnodes_pyodide
uv sync
FUNCNODES_CONFIG_DIR=.funcnodes uv run python -m funcnodes_pyodide
Open the printed http://localhost:<port> URL in a browser.
The first load is expected to be slow because the WebWorker will download Pyodide and install Python packages.
You can preload an example workflow export via URL, e.g.
?load=examples/cat.fnw.
Using it from JavaScript (embedding)
The build registers a few helpers on window.FuncNodes (which is a function exported by @linkdlab/funcnodes_react_flow and can also carry properties):
window.FuncNodes.FuncnodesPyodideWorker— the JS worker classwindow.FuncNodes.FuncnodesPyodide(...)— helper to mount the UI with a Pyodide worker
Recommended: FuncnodesPyodide(...)
This is the recommended API when using the prebuilt browser bundle:
<div id="root" style="height: 100vh"></div>
<script type="module" src="./funcnodes_pyodide_react_flow.es.js"></script>
<link rel="stylesheet" href="./funcnodes_pyodide_react_flow.css" />
<script type="module">
window.FuncNodes.FuncnodesPyodide("root", {
useWorkerManager: false,
debug: true,
// pyodide_url: "https://cdn.jsdelivr.net/pyodide/v0.29.0/full/pyodide.mjs",
// packages: ["https://example.com/your.whl"],
// restore_worker_state_on_load: true,
});
</script>
Advanced: construct a FuncnodesPyodideWorker yourself
This gives you direct access to the worker instance (for example to call
save_worker_state()).
<div id="root" style="height: 100vh"></div>
<script type="module" src="./funcnodes_pyodide_react_flow.es.js"></script>
<link rel="stylesheet" href="./funcnodes_pyodide_react_flow.css" />
<script type="module">
const worker = new window.FuncNodes.FuncnodesPyodideWorker({
uuid: "root",
shared_worker: false,
});
window.FuncNodes("root", {
useWorkerManager: false,
worker,
});
</script>
Configuration knobs
FuncnodesPyodideWorker accepts (among others):
uuid: logical worker id (used to route messages)shared_worker:trueto use aSharedWorker,falsefor a dedicatedWorkerpyodide_url: URL topyodide.mjs(defaults to the jsDelivr CDN)packages: additional Python packages to install viamicropipbefore starting the workerworker_url/worker: provide your own Worker/SharedWorker instance or URL instead of using the inline worker bundlesdebug: enable more verbose console logs during boot
Where this is used in the FuncNodes ecosystem
FuncNodes’ documentation site can embed live graphs that run fully in-browser using this package.
Limitations / gotchas
- Network required by default: the default setup fetches Pyodide and Python wheels at runtime (PyPI/CDNs).
- Package compatibility:
micropipcan only install packages that are compatible with Pyodide (pure Python wheels or Pyodide-provided packages). Many native extensions won’t work. - Concurrency constraints: Pyodide does not provide CPython-style multiprocessing, and browser threading constraints apply. Heavy CPU work can freeze the worker.
- File system semantics: Pyodide’s filesystem is virtual/in-memory unless you explicitly mount persistent storage; file-based logging is disabled by
funcnodes_pyodide.patch. - Interrupt support is best-effort: the WebWorker tries to use
SharedArrayBufferfor interrupts, but this requires cross-origin isolation headers (COOP/COEP) and isn’t available everywhere.
Development: using local Python changes in the browser
By default, the Pyodide worker installs funcnodes-pyodide from PyPI via
micropip. For local development you typically want to install a wheel
built from your working tree instead.
- Build a wheel:
cd funcnodes_pyodide
uv build --wheel
- Copy the wheel into the React dev server
public/folder (keep the original filename, don’t rename it):
mkdir -p src/react/public/pywheels
cp dist/*.whl src/react/public/pywheels/
- Point
packagesat the served wheel URL:
const wheelUrl = `${
window.location.origin
}/pywheels/funcnodes_pyodide-<version>-py3-none-any.whl?t=${Date.now()}`;
window.FuncNodes.FuncnodesPyodide("root", { packages: [wheelUrl] });
Notes:
micropipdownloads viahttp(s); a plain/pywheels/...path may be interpreted as a non-remote URL depending on where it’s constructed.- This repo’s JS worker code normalizes common relative/absolute paths to absolute URLs before sending them to the worker.
Development (building + tests)
Python (package + tests)
git clone https://github.com/Linkdlab/funcnodes_pyodide
cd funcnodes_pyodide
UV_CACHE_DIR=.cache/uv uv sync --group dev
FUNCNODES_CONFIG_DIR=.funcnodes uv run pytest
JS/TS (worker + UI bundle)
cd funcnodes_pyodide/src/react
yarn install
yarn watch
yarn test
yarn build
yarn build writes a production browser bundle into src/funcnodes_pyodide/static/
(see vite.browser.config.js), which is what python -m funcnodes_pyodide serves.
License
- Python package
funcnodes-pyodide: AGPL-3.0 - JS package
@linkdlab/funcnodes_pyodide_react_flow: MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file funcnodes_pyodide-2.0.0.tar.gz.
File metadata
- Download URL: funcnodes_pyodide-2.0.0.tar.gz
- Upload date:
- Size: 7.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3276d22442fa2dca612191e1678cbaa7be5bbdb87a1d17c70a3ccb09f87f6385
|
|
| MD5 |
eef78a2eef4ea9c6b3c0c1a0a82f446d
|
|
| BLAKE2b-256 |
a7130888e050ff87addf158c16975ed297fe73eafc15aa96d33a0aaeec59682f
|
Provenance
The following attestation bundles were made for funcnodes_pyodide-2.0.0.tar.gz:
Publisher:
version_publish_main.yml on Linkdlab/funcnodes_pyodide
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
funcnodes_pyodide-2.0.0.tar.gz -
Subject digest:
3276d22442fa2dca612191e1678cbaa7be5bbdb87a1d17c70a3ccb09f87f6385 - Sigstore transparency entry: 801301549
- Sigstore integration time:
-
Permalink:
Linkdlab/funcnodes_pyodide@643a18b16511e51462addb574f550e52491222a4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Linkdlab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
version_publish_main.yml@643a18b16511e51462addb574f550e52491222a4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file funcnodes_pyodide-2.0.0-py3-none-any.whl.
File metadata
- Download URL: funcnodes_pyodide-2.0.0-py3-none-any.whl
- Upload date:
- Size: 6.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
632b811f7a7f5fb8dd35d7e032698b7e6e5a2065035528d306792352df94ed2d
|
|
| MD5 |
a883dc122ccb1910d5238c1548a9ebfb
|
|
| BLAKE2b-256 |
eab481373bbbb76c2ab7bd9cbc0b1bd21838355f2ee8dde8fbaedbb5555b2611
|
Provenance
The following attestation bundles were made for funcnodes_pyodide-2.0.0-py3-none-any.whl:
Publisher:
version_publish_main.yml on Linkdlab/funcnodes_pyodide
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
funcnodes_pyodide-2.0.0-py3-none-any.whl -
Subject digest:
632b811f7a7f5fb8dd35d7e032698b7e6e5a2065035528d306792352df94ed2d - Sigstore transparency entry: 801301592
- Sigstore integration time:
-
Permalink:
Linkdlab/funcnodes_pyodide@643a18b16511e51462addb574f550e52491222a4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Linkdlab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
version_publish_main.yml@643a18b16511e51462addb574f550e52491222a4 -
Trigger Event:
push
-
Statement type: