Skip to main content

Observable JavaScript notebooks as reusable Python widgets

Project description

pyobservablejs

CI PyPI Python versions License

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyobservablejs-0.0.2.tar.gz (1.5 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyobservablejs-0.0.2-py3-none-any.whl (1.5 MB view details)

Uploaded Python 3

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

Hashes for pyobservablejs-0.0.2.tar.gz
Algorithm Hash digest
SHA256 b544ce5ae15d32259c85140b396f89d78681d3035c1d2790b311f611229b117c
MD5 03d51f5ad9bc883fc7aebb19436b169a
BLAKE2b-256 f08050405c58e1283391c9315a53f0b30ee3b2df9000c9d4bdbe3cc6b921eed8

See more details on using hashes here.

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

Hashes for pyobservablejs-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 6770720bb9019942e6a994174ab50a39731cc4ef8707749490ea51272597cc14
MD5 b66aa8d3230a1fb271df9cec209afe8c
BLAKE2b-256 07ac2e64f436481c697f00ed4d4d112f112e4be87143c0cae158266cd41a24c4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page