Observable JavaScript notebooks as reusable Python widgets
Project description
pyobservablejs
pyobservablejs brings interactive Observable notebooks into Python workflows.
Author a notebook in Python or load an existing one, render the views you need,
and keep its controls and results connected to your Python code.
Use it in JupyterLab, marimo, VS Code notebooks, Google Colab, and other environments that support anywidget.
Quick start
Python 3.11 or newer is required.
uv add pyobservablejs marimo
uv run marimo edit notebook.py
Existing Observable notebook
import marimo as mo
import observablejs as obs
notebook = obs.Notebook.from_observablehq("@d3/world-tour")
mo.ui.anywidget(notebook.view())
@d3/world-tour resolves to
observablehq.com/@d3/world-tour.
Author an interactive notebook
import marimo as mo
import observablejs as obs
notebook = obs.Notebook(
obs.html(
"""
<h2>Palmer penguins</h2>
<p>Choose a species to filter the chart.</p>
"""
),
obs.ojs(
"""
viewof species = Inputs.select(
["All", ...new Set(penguins.map((d) => d.species))],
{label: "Species", value: "All"}
)
""",
key="species_control",
),
obs.js(
"""
Plot.dot(
species === "All"
? penguins
: penguins.filter((d) => d.species === species),
{
x: "culmen_length_mm",
y: "culmen_depth_mm",
fill: "species",
tip: true
}
).plot({
height: 320,
color: {legend: true},
x: {grid: true, label: "Bill length (mm)"},
y: {grid: true, label: "Bill depth (mm)"}
})
""",
key="penguins_plot",
),
)
mo.ui.anywidget(notebook.view())
penguins, Inputs, and Plot come from Notebook Kit. Changing the viewof
selection recomputes the plot in the browser.
Cells, views, and inputs
Notebook accepts four kinds of cells:
| Helper | Cell source |
|---|---|
obs.js(...) |
Standard Notebook Kit JavaScript |
obs.ojs(...) |
Observable JavaScript |
obs.md(...) |
Markdown |
obs.html(...) |
HTML |
Top-level declarations in JavaScript cells form a reactive graph: when a value changes, every cell that references it runs again.
View means two things. In Python, .view() creates a renderable
NotebookView widget. In a JavaScript cell, view(input) displays a browser
input and defines its reactive value.
| Call | Result |
|---|---|
notebook.view() |
A NotebookView that renders every cell. |
notebook.view(cells=[...]) |
A composite view of selected cells and their hidden dependencies. |
notebook.cell(0).view() |
A focused view of one cell and its hidden dependencies. |
view(input) inside obs.js |
A browser input whose value other cells can reference. |
Create one view per place a notebook appears in the host. Views from the same
notebook stay connected: Python variable updates reach all of them, and named
viewof inputs share supported values across views.
Python variables
Pass Python values to the browser through variables:
import observablejs as obs
notebook = obs.Notebook(
obs.js('html`<p>Threshold: <strong>${threshold}</strong></p>`'),
variables={"threshold": 0.75},
)
full_view = notebook.view()
full_view
While the view stays mounted, call notebook.update_variables(threshold=0.9)
to update the value.
After the view renders, read browser values through values or cell_values
and inspect dependency metadata through graph.
Load Notebook Kit HTML
Load a Notebook Kit HTML document:
from pathlib import Path
import observablejs as obs
path = Path("chart.html")
notebook = obs.Notebook.from_html(
path.read_text(encoding="utf-8"),
base_path=path.parent,
embed_file_attachments=True,
rewrite_imports=True,
)
Documentation
Acknowledgements
Thanks to the Observable team for Notebook
Kit, which provides the notebook
APIs and runtime used throughout this project.
pyobsplot informed the Python variable
API, and Trevor Manz's anywidget composition demo
shaped the widget design.
License
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 pyobservablejs-0.0.2.tar.gz.
File metadata
- Download URL: pyobservablejs-0.0.2.tar.gz
- Upload date:
- Size: 1.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b544ce5ae15d32259c85140b396f89d78681d3035c1d2790b311f611229b117c
|
|
| MD5 |
03d51f5ad9bc883fc7aebb19436b169a
|
|
| BLAKE2b-256 |
f08050405c58e1283391c9315a53f0b30ee3b2df9000c9d4bdbe3cc6b921eed8
|
File details
Details for the file pyobservablejs-0.0.2-py3-none-any.whl.
File metadata
- Download URL: pyobservablejs-0.0.2-py3-none-any.whl
- Upload date:
- Size: 1.5 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6770720bb9019942e6a994174ab50a39731cc4ef8707749490ea51272597cc14
|
|
| MD5 |
b66aa8d3230a1fb271df9cec209afe8c
|
|
| BLAKE2b-256 |
07ac2e64f436481c697f00ed4d4d112f112e4be87143c0cae158266cd41a24c4
|