Skip to content

GuideDay-to-day use. Assumes you've run a first notebook.

Comparing packages ​

A notebook that runs One package's numbers for four web frameworks through Ganymede's host API and compares the results; its first cell says what it needs. The copy below ran with ganymede execute, which grants on the command line and needs a Ganymede release after 0.9.0:

sh
ganymede execute compare-packages.ipynb --grant run=package-stats.ipynb

See Other notebooks: jupyter.host().

A notebook run on the cdp-node kernel, shown with its saved output.Download compare-packages.node.ipynb · View on GitHub

This notebook runs another notebook, One package's numbers (package-stats.ipynb), once for each of four web frameworks, and compares the results. Each run starts a fresh kernel with the package name as its parameter and comes back with the named results.

It needs a Ganymede server, which answers jupyter.host(), and a grant that lets this notebook run the other:

sh
ganymede grant compare-packages.ipynb run=package-stats.ipynb

Without a server, jupyter.host() resolves to null and the notebook stops at the first cell, as it would under plain Jupyter.

The host ​

ts
const host = await jupyter.host();
if (!host) throw new Error("No Ganymede server: open this notebook under ganymede lab or ganymede execute.");
host.has("notebooks.run")
true

Run the notebook for each package ​

host.notebooks.run runs the notebook to the end and answers with its named results. The runs here go one after another; Promise.all would run them at once, each in its own kernel.

ts
const packages = ["express", "fastify", "hono", "koa"];

type Stats = { package: string; latest: string; stable_releases: number; first_release: string; weekly_downloads: number };
const rows: Stats[] = [];
for (const pkg of packages) {
  const run = await host.notebooks.run({ path: "package-stats.ipynb", params: { pkg } });
  rows.push(run.results as Stats);
}
rows.map((r) => `${r.package}: ${r.weekly_downloads}`)
[
  'express: 195384165',
  'fastify: 22307443',
  'hono: 104726612',
  'koa: 9568603'
]

The results are data the other notebook wrote, so this one treats them as values, never as code to run.

Compare ​

ts
jupyter.md`| Package | Latest | Stable releases | First release | Downloads last week |
| --- | --- | ---: | --- | ---: |
${rows.map((r) => `| ${r.package} | ${r.latest} | ${r.stable_releases} | ${r.first_release} | ${r.weekly_downloads.toLocaleString("en")} |`).join("\n")}`
PackageLatestStable releasesFirst releaseDownloads last week
express5.3.02472010-12-29195,384,165
fastify5.12.83012016-10-0722,307,443
hono4.13.184152021-12-14104,726,612
koa3.3.01122013-11-069,568,603
ts
import * as vl from "npm:vega-lite-api@5";

vl.markBar()
  .data(rows)
  .encode(
    vl.y().fieldN("package").sort("-x").title(null),
    vl.x().fieldQ("weekly_downloads").title("downloads last week"),
    vl.tooltip(["package", "latest", "weekly_downloads"]),
  )
  .width(460)
  .height(160)

Publish ​

This notebook is a program too: its own named results are there for whoever runs it.

ts
const top = rows.toSorted((a, b) => b.weekly_downloads - a.weekly_downloads)[0];
jupyter.result("most_downloaded", top.package);
jupyter.result("downloads", Object.fromEntries(rows.map((r) => [r.package, r.weekly_downloads])));
most_downloaded = "express"
downloads = {"express":195384165,"fastify":22307443,"hono":104726612,"koa":9568603}

Next ​

MIT license. Parts are ports of Deno's Jupyter kernel, © the Deno authors.