Skip to main content

Inspect notebook data

A displayed view exposes data, files, and graph namespaces through anywidget. This route uses the base package and does not require Deno.

Display a notebook

In Jupyter, display the view first:

import observablejs as obs

notebook = obs.Notebook(
obs.ojs("rows = [{amount: 2}, {amount: 4}, {amount: 6}]", key="rows"),
obs.ojs("total = rows.reduce((sum, row) => sum + row.amount, 0)", key="total"),
)
view = notebook.view("total", capture_state=False)
view

The view displays 12. It evaluates rows as a hidden dependency. Preview capture is optional for namespace reads and dataset discovery.

Inspect source and dependencies

Cell handles and source are available immediately:

print(notebook.cells["rows"].source)
print(notebook.cells.keys())

After display, ask the originating browser for its analysis:

names = await view.data.names()
graph = await view.graph.snapshot()
upstream = await view.graph.upstream("total")

The graph describes the full definition. Reading it does not execute additional cells or fetch imports. view.inspection is the latest readonly analysis snapshot for traitlets observers and starts as None before publication.

Read a hidden dependency

Run this in a later Jupyter cell:

rows = await view.data["rows"].to_python()
print(rows)
[{'amount': 2}, {'amount': 4}, {'amount': 6}]

The method waits for the requested value and returns detached Python data. It reads this view's current browser values, including browser interactions.

Use a target library directly when you need a dataframe:

frame = await view.data["rows"].to_polars(columns=["amount"], limit=2)
table = await view.data["rows"].to_arrow()

Install Polars or PyArrow for the corresponding conversion. Projection and row ranges reduce transfer volume before conversion.

Discover datasets

catalog = await view.data.discover()
for dataset in catalog.datasets:
print(dataset.name, await dataset.describe())
frame = await catalog.datasets["rows"].to_polars()

catalog.errors reports failures while retaining independent datasets. catalog.pending marks incomplete evaluation. Discovered references pin the observed value revision. Get a new catalog when a value changes.

For passive reactive observation, view.datasets publishes the latest readonly metadata tuple. Inspect that trait directly or observe it with traitlets. In marimo, view.value subscribes a cell to frontend updates.

Run full reads in marimo

An executing marimo cell blocks browser response processing. Launch the read in a background coroutine so the cell can finish and the browser can reply:

import asyncio
import marimo as mo
import observablejs as obs

notebook = obs.Notebook(
obs.ojs("rows = [{amount: 2}, {amount: 4}, {amount: 6}]", key="rows"),
)
view = notebook.view(capture_state=False)
get_rows, set_rows = mo.state(None)

async def collect_rows():
rows = await view.data["rows"].to_python()
set_rows(rows)

read_task = asyncio.create_task(collect_rows())
view

Display the result in another cell:

rows = get_rows()
mo.ui.table(rows) if rows is not None else mo.md("Loading data")

Keep read_task to cancel outstanding work. view.close() also ends pending requests. Exceptions should be handled in the coroutine when the UI needs to show an error state.

Read original files

raw = await view.files["sales.csv"].read_bytes()
frame = await view.files["sales.csv"].to_polars()

These methods use the view's browser URL resolution and credentials. Reading view.data[name] evaluates the producing code and its transformations instead.

For synchronous Python scripts, use headless notebook data. See the namespace reference for descriptions, provenance, conversion options, and async headless access.