boro
author and compose mosaic clients as anywidgets
install
uv add boro
what
mosaic is an architecture for interactive linked views over millions of rows: clients publish queries, a coordinator manages and cross-filters them against a data source (e.g., duckdb).
vgplot is how you usually author custom mosaic-based visualizations, a bundle with its own grammar and layout. boro is the same client/coordinator architecture, but each client is a standalone anywidget.
primitives
three anywidgets:
Coordinator— owns aDataSourceand the js-side mosaic engine. brokers queries over arrow ipc. headless.Selection— wraps a mosaicSelection.valuesyncs the array of clauses (sql predicates) to python.Client— base class. Requiredcoord; conventionalfilter_by(read side) andtarget(publish side) selection slots, bothboro.SelectionTrait. Subclasses can declare further selection slots withboro.SelectionTrait().
The base accepts a unified selection= shortcut covering all four shapes:
Histogram(c, ..., selection=sel) # crossfilter (both slots = sel)
Histogram(c, ..., selection=(scope, detail)) # asymmetric pair
Histogram(c, ..., selection=(sel, None)) # read-only (filter only)
Histogram(c, ..., selection=(None, sel)) # publish-only
Explicit filter_by= / target= kwargs override the shortcut.
each client is its own _esm and DOM. the mosaic engine and js deps live once,
in the coordinator; clients reach it via host.getWidget(...).
hello world
import boro
import duckdb
con = duckdb.connect()
con.execute("CREATE TABLE flights AS SELECT * FROM read_parquet('flights.parquet')")
c = boro.Coordinator.connect(con)
sel = boro.Selection.crossfilter(c)
Histogram(c, table="flights", column="delay", selection=sel)
Scatter(c, table="flights", x="distance", y="delay", selection=sel)
>>> sel.value
[{'value': [5, 30], 'sql': '"delay" BETWEEN 5 AND 30', 'meta': {...}, 'source': '...'}]
writing a client
import boro
import traitlets
class RowCounter(boro.Client):
_esm = """
export default {
async render({ model, host, signal, el }) {
el.style.cssText = "font: 14px ui-sans-serif; padding: 6px 8px;";
el.textContent = "…";
const coord = await host.getWidget(model.get("coord"));
const { Query, msql, createClient } = coord.exports;
const ctx = await createClient({ model, host, signal });
ctx.liveQuery({
filterBy: ctx.filterBy,
query: (filter) =>
Query.from(model.get("table"))
.select({ n: msql.count() })
.where(filter ?? []),
onResult: (result) => {
if (!result.isSuccess) {
return;
}
const n = Number(result.data.toColumns().n[0]);
el.textContent = `${n.toLocaleString()} rows`;
},
});
},
};
"""
table = traitlets.Unicode().tag(sync=True)
def __init__(self, coord: boro.Coordinator, table: str, **kwargs):
super().__init__(coord=coord, table=table, **kwargs)
RowCounter(c, table="flights", selection=sel)
coord.exports provides Query / msql (mosaic-sql), mc (mosaic-core), and
createClient. createClient({ model, host, signal }) returns a ctx that
exposes:
ctx.selections.<name>— resolved mosaicSelections, one perboro.SelectionTraitfield on the Python class (filter_by/targetcome from the base; subclasses can declare more).ctx.state(name, opts?)— a{get, set, subscribe}handle on a sync trait. Optional{target, clause}opts auto-derive a clause from the trait and publish it to a Selection on every change.ctx.liveQuery({ filterBy }, queryFn, onState)— registers a filter-driven query.onStatereceives{status: 'idle'|'pending'|'success'|'error', data, error}.ctx.coordinator— the mosaicCoordinatorfor one-shot ad-hoc queries (await ctx.coordinator.query(sql, { type: 'arrow' })).ctx.client,ctx.clients,ctx.signal— the synthetic primaryMosaicClient(clause source identity), theSetof all sibling clients used for cross-filter exclusion instate-derived clauses, and the abort signal scoped to this render.
example
uv run jupyterlab examples/
Metadata
Release files for boro 0.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| boro-0.0.0.tar.gz | 12.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| boro-0.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.6 kB
Release files / boro-0.0.0.tar.gz
| Download URL | boro-0.0.0.tar.gz |
|---|---|
| Size | 12.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
79b0621b125c93580de9bbf30784204b38e70a9fd883fe757f1a1560dd8df0bf
|
|
BLAKE2b-256 checksum How to use checksums |
7b502a9cf15d0e46b3f1768b27e32e90006912f8fed6df7cef68b27cf8f6a7dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","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}
|
Release files / boro-0.0.0-py3-none-any.whl
| Download URL | boro-0.0.0-py3-none-any.whl |
|---|---|
| Size | 15.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4657a987fc461998dcb52271fe374bbd7d87e15db67135a22d7e4e92d623fb8c
|
|
BLAKE2b-256 checksum How to use checksums |
b0d035df76979ae731ece4751b0cee8e3ccccc087d2b59dc3b3b7574a3b2969a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","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}
|