b2view: Browse TreeStore Bundles in the Terminal¶
The b2view CLI opens an interactive terminal browser (TUI) for Blosc2
TreeStore bundles, either sparse directories (.b2d) or compact
zip-backed files (.b2z). It shows the tree of groups and nodes, the
metadata and attrs of the selected node, and a paged view of the data
itself — NDArrays of any dimensionality as well as CTables.
b2view is opt-in: install it with the tui extra —
pip install "blosc2[tui]" — which also covers the in-terminal braille
plot (the p key). The high-resolution image view (the h key) needs
the hires extra instead — pip install "blosc2[hires]" (it includes
tui). See Installation for the full list of extras.
Step 1 — Create a sample store¶
Run the snippet below once to produce sample.b2z with a couple of
arrays and some metadata:
import blosc2
with blosc2.TreeStore("sample.b2z", mode="w") as tstore:
tstore.attrs["author"] = "me"
a = blosc2.linspace(0, 1, num=1_000_000, shape=(1000, 1000))
a.attrs["description"] = "a 2-D linspace"
tstore["/dense/a"] = a
tstore["/dense/b"] = blosc2.arange(10_000, shape=(10, 100, 10))
Any existing TreeStore bundle works too — for instance the output of the
parquet-to-blosc2 converter (see Parquet to Blosc2 Walkthrough).
Step 2 — Open it¶
b2view sample.b2z
The screen is split into four panels: the tree of the bundle on the
left, and meta, attrs and data panels for the node selected
in the tree. Move between panels with tab / shift+tab, maximize
the focused one with m (r restores it), and quit with q.
For standalone objects such as an NDArray or CTable, the tree panel is hidden, the remaining panels use the full width, and focus starts in the data panel by default. The metadata omits the internal root path; the header shows the source path.
By default the mouse is left to the terminal, so selecting and copying text
works as in any other command line program. Pass --mouse to let b2view
capture it instead: panels become clickable and the wheel scrolls the data
grid (paging at the boundaries), at the cost of native text selection.
You can also jump straight to a node and panel:
b2view sample.b2z /dense/a --panel data
Remote containers and arrays¶
Ordinary Caterva2 files¶
Published ordinary files (compressed SChunk byte streams) appear as file nodes.
Metadata inspection does not fetch their payload. Text and Markdown preview a
UTF-8 prefix, bounded to 64 KiB and 1,000 lines; truncated/invalid text is labelled.
Markdown links/images are passive and never fetch other resources. T toggles
raw text and Markdown; terminal controls are stripped from file content.
Install blosc2[images] for JPEG/PNG previews using Pillow and textual-image,
without requiring matplotlib. Terminal protocol support may fall back to colored
half-cells. Missing dependencies/display support show a download/open notice.
Automatic image input is limited to 16 MiB; original pixel count is limited to
16,777,216 pixels (64 MiB RGBA). Orientation is corrected, the first frame is used,
and the displayed image is reduced to at most 1600 × 1200 pixels. These limits
bound individual buffers, not total process RAM including decoder copies.
D prompts for a destination and streams the original file, not its Blosc
carrier. O downloads and opens the completed local file in the platform’s
external viewer after you submit the destination dialog, without an extra trust
checkbox. Both actions work for
PDFs, which deliberately need no terminal PDF renderer. External opening is
restricted to PDF, JPEG/PNG, Markdown, and text; other binary files remain
downloadable. No shell or credential-bearing URL is passed to the launcher.
Downloads are user-owned: they remain after closing the dialog or b2view, including
when external opening fails. Existing destinations are never overwritten by the
viewer; choose another name. Escape cancels an active transfer.
File reads/downloads have per-chunk limits of 32 MiB compressed and 256 MiB decoded.
Animated loading dots in the data-pane border indicate pending background
loads without hiding the current page or changing the layout. The indicator
disappears when the load completes or fails; it is activity, not byte progress.
Even a small preview can decode one complete chunk; temporary buffers can bring
peak memory above the decoded-chunk limit. Preview and per-call read limits
remain independent of these chunk limits.
A small preview may require a much larger chunk. Oversized or irregular sources
require server-side rechunking; the viewer never silently fetches a whole file
to work around these limits. File reference archives are not supported by this
feature. Incompatible files do not hide healthy
siblings.
Local and direct-fsspec ordinary files¶
Open a regular file directly with the same previews, fallback panels and actions:
b2view README.md
b2view photo.png
b2view brochure.pdf
b2view https://example.org/README.md
b2view --profile blosc2 s3://my-bucket/notes/README.md
Local file:// URLs and fsspec protocol chains are supported too. Install
blosc2[fsspec] and the relevant filesystem dependency for remote files.
Authentication/storage options use the same CLI flags as direct dataset URLs.
For an HTTP file without a filename extension, or a file URL containing an
@ path component, pass --remote-service fsspec to bypass service discovery.
Direct files open without a tree panel. Reads and image decoding run in the
background; preview text/image limits are unchanged. D copies original bytes
to a chosen local destination; O copies and opens the supported document
after explicit destination submission. Local sources are never modified, and
existing destinations are not overwritten. Choose a different destination when
the dialog defaults to the source’s own name. Transfers stream in 1 MiB spans
and support cancellation and atomic publication, rather than loading the whole
file into memory. Ordinary files must have a known size; a short read or a size
change during copying fails without publishing a partial destination.
These viewer-only handles do not extend blosc2.open or Caterva2’s caches.
--cache-dir/--max-cache-bytes do not configure direct ordinary-file caching;
filesystem backends may have their own buffering (notably chained archives).
HTTP operations have a default 10-second timeout. Existing Blosc2/Zarr/HDF5
hierarchies keep their dataset semantics.
Recognized dataset extensions retain their normal opener and corruption errors;
unrecognized filenames receive a small native-frame signature check, not object
deserialization or automatic content execution.
Ordinary directories¶
Local directories (including file:// URLs) and directories on fsspec
filesystems with directory-listing support open as lazy tree roots:
b2view ./documents/
b2view ../cat2lite/tests/fixtures/data-cat2-demo/root-example/
b2view --profile blosc2 s3://my-bucket/documents/
Only immediate children are listed; expanding a directory discovers the next
level. Selecting a file uses the same previews and copy/open actions as a direct
file input. Dataset containers such as B2Z, HDF5 and Zarr can be expanded as
mounted subtrees without first downloading the whole container. A corrupt leaf
does not hide its siblings. Refresh reopens the directory and its selected node.
Local symlinks are shown but not followed, and parent-path navigation cannot
escape the opened root. Opening a dataset directory such as .b2d or .zarr
directly continues to use the dataset opener, not the ordinary-directory browser.
Client cache directories are separated per mounted dataset; cache-byte budgets
apply per mounted source, not as a global limit for the whole directory tree.
Compressed local and fsspec documents¶
Document carriers such as README.md.b2, photo.png.b2 and
brochure.pdf.b2 use the same file previews/actions as their originals:
b2view README.md.b2
b2view ./documents/ /README.md.b2
b2view s3://bucket/documents/README.md.b2
b2view https://example.org/documents/analysis.ipynb.b2
Supported text, JPEG/PNG and PDF suffixes followed by .b2 identify this
viewer-only convention. A carrier must be a plain fixed-chunk SChunk byte stream,
not an NDArray, serialized object or remote reference. Local carriers are mapped
read-only; fsspec carriers use byte ranges on contiguous frames. Metadata
inspection does not decompress document chunks. Plain data.b2 and
.b2frame datasets keep their normal native behavior.
Remote reads fetch the frame header, then a bounded chunk index when values are
first requested, and each needed chunk’s header before its payload. HTTP servers
must return valid 206 byte-range responses; ignored ranges, incorrect
lengths and encoded responses are refused without reading the complete body.
Frame headers are capped at 1 MiB and chunk indexes at 8 MiB compressed/decoded.
Sparse frames and nonzero special-value chunks are not supported remotely.
Filesystem storage options (including authentication) apply to previews and
independent transfer handles. Other fsspec backends may have their own buffering.
Text previews decode only chunks covering the first 64 KiB, displaying at most 1,000 lines. Images decode complete original bytes only within the existing 16 MiB input and pixel limits. PDF selection needs no decompression. Chunk headers are checked before copying/decompressing payload: each chunk must fit 32 MiB compressed and 256 MiB decoded. Oversized/irregular carriers are refused; rechunk them rather than increasing automatic preview memory usage.
D streams decoded original bytes to the destination, keeping at most one
decoded chunk between reads. O uses the same explicit copy/external-open
workflow. Both suggest the original name (README.md, not README.md.b2);
carrier metadata retains the on-disk filename. No entire document is assembled
for downloading, and no original-file payload is deserialized or executed.
Sources are assumed immutable while open; refresh after replacing a carrier.
Passive Jupyter notebook previews¶
.ipynb files open as passive nbformat-4 documents, not executable notebooks:
b2view analysis.ipynb
b2view analysis.ipynb.b2
b2view https://cat2.cloud/demo/@public/examples/slice-time.ipynb
Markdown cells render without active links/images; code cells are syntax
highlighted using the declared supported language (otherwise plain text).
Raw cells, saved stdout/stderr, plain-text result representations and tracebacks
are shown as text with terminal controls removed. No kernel starts, cells never
execute. Saved PNG/JPEG outputs are decoded from the notebook and displayed
beneath their cells using the existing terminal-image support
(blosc2[images]). HTML/JavaScript, SVG, widget outputs and other media remain
omitted with a notice. There is no requirement for Jupyter or nbformat to be
installed. Markdown image links/attachments are not fetched.
At most eight saved images are displayed. They share a 16 MiB encoded-image-byte budget and a 16,777,216 decoded-pixel budget (64 MiB at RGBA), in addition to the 2 MiB notebook input cap below. Images are validated before pixel decoding and reduced to at most 1600 by 1200 pixels for display. Invalid or oversized images are skipped with a notice, keeping cell text available. Only saved static images are shown; interactive plots/widgets are not executed, and animated images show only their first frame.
Parsing requires the complete JSON document, so input is capped at 2 MiB.
Displayed source/output text shares a 64 KiB / 1,000-line budget, with at most
100 cells. Truncation is labelled; oversized or unsupported notebooks show the
normal fallback panel rather than being fetched without bounds. T switches
between the cell view and a bounded raw-JSON prefix; D copies the original
notebook. Notebook external opening is not enabled. These previews work with
local, fsspec and Caterva2 files, and local/fsspec .ipynb.b2 document carriers, under
their existing transport/chunk limits.
Find nodes in local or remote trees¶
Press f with the tree focused, or ctrl+f from any panel, to open tree
search. The tree frame includes a compact key hint. Typing filters already discovered paths
by case-insensitive substring, without network requests. Matching nodes retain
their ancestor context. An empty filter shows discovered nodes again; Escape
closes the dialog without changing the main tree’s selection or expansion state.
The dialog displays at most 500 matches at once; refine the filter for more.
Choose Search recursively to discover unopened directories/groups in the background. This works with local trees, Caterva2 repositories and fsspec hierarchies using their existing listing APIs. It requests listings and necessary metadata, not file previews, array values or table rows. For example, Caterva2 table classification can require an empty frame containing its schema.
Progress, listing errors and incomplete/cancelled status are shown explicitly. Search stops after 200 directories, 10,000 discovered nodes or depth 12; the final indivisible backend listing can exceed the node budget. Cancel search retains completed results; Escape cancels and closes. An in-flight backend operation may finish before cancellation takes effect, but the dialog can close immediately. Discovered listings are reused in subsequent searches; refresh the tree to invalidate them. No recursive discovery happens merely by typing a filter.
Press Enter in the input to focus the result tree, then select a matching node with Enter (or the mouse) to reveal its ancestors in the main tree and open its normal preview. Row/column filtering shortcuts remain unchanged. Standalone files and arrays have no tree to search.
Direct source URLs¶
Browse remote B2Z, Zarr, and HDF5 containers directly from their root:
b2view --profile blosc2 --endpoint-url https://s3.us-west-001.backblazeb2.com s3://blosc2/hierarchy.b2z
b2view --profile blosc2 --endpoint-url https://s3.us-west-001.backblazeb2.com s3://blosc2/hierarchy.zarr
b2view --profile blosc2 --endpoint-url https://s3.us-west-001.backblazeb2.com s3://blosc2/hierarchy.h5
Append a group path to browse a subtree, or an array path to open a standalone
array without a tree panel. Both / and :: dataset addressing work:
b2view s3://blosc2/hierarchy.b2z/d0/d1 --profile blosc2 --endpoint-url https://s3.us-west-001.backblazeb2.com
b2view s3://blosc2/hierarchy.zarr::d0/d1/a2 --profile blosc2 --endpoint-url https://s3.us-west-001.backblazeb2.com --panel data
The supplied URL stays in the header. Within a subtree, / refers to the
requested group. Group attributes belong to the selected group. Opening,
expansion, metadata reads, and array pages run in background workers; navigation
and quit remain available during a slow request. Refresh opens a new discovery
session, discards cached pages, and restores the selected path when it still
exists. Failed listings can be retried by selecting or expanding the group again.
Browsing is read-only. Remote roots and groups use RemoteStore with one
64 MiB MEMORY allowance shared across arrays. Selected leaves are RemoteArray
handles. Switching arrays or selecting a group releases the selected handle but
keeps its warm payload in the store until eviction or browser close. Revisited
chunks within the allowance need no further payload download. Discovery reads
metadata, not every array’s data. Metadata cost can grow with the number of
objects and chunks. Small objects may fit entirely within a bounded opening read.
--profile and --endpoint-url are optional; when omitted, the S3 backend
uses its normal credential and endpoint configuration. Install blosc2[tui]
and blosc2[fsspec] plus s3fs for S3 access. Zarr requires
blosc2[zarr]; HDF5 requires blosc2[hdf5] (h5py and optional filter plugins).
B2Z browsing does not require Zarr or HDF5 dependencies.
Format details and limits:
B2Z: discovery shares the ZIP directory with the native array reader. External arrays must be unencrypted, ZIP_STORED plain NDArrays. The embedded index identifies embedded leaves and CTable boundaries; their payload previews remain unavailable. Group attributes are read from external frame trailers or the bounded native chunks containing embedded attribute frames. Embedded attribute layouts with chunks larger than 1 MiB show a partial-metadata notice instead of fetching large payloads. TreeStore has no separate empty-group marker: an empty group is visible when its attribute frame records it.
Zarr: v2 and v3 groups use consolidated metadata when available and normal discovery otherwise. Unconsolidated groups need backend directory-listing support and LIST permission. Direct arrays do not require listing their parent. Empty groups and attributes are preserved. Unknown codecs and unsupported dtypes remain visible; preview support follows the existing Zarr array reader.
HDF5: h5py builds a native metadata and chunk-range index once per session, and all selected leaves reuse it. Indexing can enumerate many allocated chunks; it avoids full-file localization, but is not a constant-cost operation. Empty groups and attributes are preserved. Unsupported datasets remain unavailable nodes without hiding supported siblings. Hard-link aliases may be omitted, and soft/external links and group cycles are not followed.
These internal browser adapters do not change the array-only contract of
blosc2.open(..., lazy=True) or add a persisted RemoteArray hierarchy descriptor.
Opening an entire remote B2Z through blosc2.open requires an explicit
cache_dir for localization; use b2view for range-based hierarchy browsing.
Standalone remote .b2nd arrays retain their existing lazy viewer behavior.
Step 4 — Filter CTable rows¶
On a CTable node, press f and type a filter expression to page through
only the matching rows — the same expressions CTable.where() accepts,
including dotted nested column names and and / or:
payment.tips > 100 and trip.km > 0 and trip.sec > 0
The data header shows the active filter and the matching row count; all
navigation (paging, g, t / b) then operates on the filtered
rows. Press escape (or submit an empty expression) to go back to the
unfiltered table; each node remembers its filter for the session.
Columns can be filtered too: press / and type a case-insensitive
substring (e.g. payment) to show only the matching columns — column
paging and the c goto-column modal then operate on that subset. Row
and column filters combine freely; escape clears them one layer at a
time (row filter first, then columns).
Step 5 — Sort and group CTable rows¶
Press S on a CTable node to sort the rows by a column: a picker lists
every column, marking FULL-indexed ones with ◆ — those reuse their
pre-sorted positions and apply instantly, while the rest are scanned on
demand (slower on a big table, but no whole-table copy). R flips
between ascending and descending; escape restores the original order.
Press G to group by a dictionary or numeric column, choosing an
aggregation (count, sum, mean, …) and, where the aggregation needs one, a
value column. The data panel then shows the small grouped result — one row
per group — with the cursor parked on the aggregate column, and p plots
it as a bar chart. While grouped, S sorts the grouped result by any of
its columns (key or aggregate), R reverses it, and enter on an
argmin/argmax cell jumps to the matching row of the base table.
escape leaves the grouped view.
CLI options¶
--preview-rows N and --preview-cols N bound the size of each data
page (20 rows by 10 columns by default), and --panel chooses the panel
focused on startup (tree, meta, attrs or data).