Releases: Blosc/python-blosc2
Release list
Release 4.9.1
Changes from 4.9.0 to 4.9.1
A small hot-fix release for the Arrow interop work in 4.9.0: a real
performance regression in dictionary-column export, and a clearer error
message when opening a nonexistent CTable in append mode.
Improvements
CTable.iter_arrow_batches()(and thereforeto_arrow()and the Arrow
PyCapsule interchange,__arrow_c_stream__) no longer recomputes the
full live-row-position array from scratch on every batch, for every
dictionary column — anO(n_rows)scan that was repeated
O(n_rows / batch_size)times. The position array is now computed once
per export call instead. Measured 6-14x faster export for
dictionary-encoded string columns (e.g.company) on a 1M-row table.- Reminder for anyone consuming a
CTablethrough the Arrow PyCapsule
protocol (DuckDB, pyarrow, Polars, pandas): the raw Arrow C Stream
interface has no column-projection pushdown, so a consumer that only
needs a few columns still triggers export of every column in the table.
UseCTable.select([...])to project down to the columns you actually
need before handing the table to the consumer, particularly if any
column is an expensive nested/list type.
Bug fixes
- Opening a
CTablewithmode="a"at a path that doesn't exist yet now
raises a clearFileNotFoundError("mode='a' opens an existing table;
use mode='w' to create a new one") instead of silently falling through
and creating a new, empty table.
Release 4.9.0
Changes from 4.8.1 to 4.9.0
This release is about cooperation: CTable now speaks the tabular
ecosystem's own protocols instead of asking it to speak blosc2's. Arrow
tools (pyarrow, DuckDB, Polars, and pandas >= 3.0 via DataFrame.from_arrow())
can consume or produce a CTable directly through the Arrow PyCapsule
interface; a new utf8() string column stores text in Arrow's own
offsets+bytes layout and reads back as NumPy StringDType; and
engine=blosc2.jit now runs correctly inside pandas 3 itself. Alongside
that, CTable gained a proper missing-data story (fillna/dropna,
null-safe arithmetic and comparisons) and a pandas-3-style chaining API
(assign()/col(), UDF aggregations, CTable.apply()).
New features
Ecosystem interop
- Arrow PyCapsule interchange:
CTable.__arrow_c_stream__lets
pyarrow, DuckDB, and Polars consume aCTabledirectly as a stream of
record batches, with bounded memory — noto_arrow()/copy step
required. pandas >= 3.0 can do the same via the new
pandas.DataFrame.from_arrow()classmethod (the plainpd.DataFrame(t)
constructor does not use this protocol).CTable.from_arrow()now
accepts any object implementing the same protocol on ingest
(single-argument form), in addition to the existing(schema, batches)
form — including Arrow'sstring_viewlayout (Polars' default string
export type), which raisedTypeErrorbefore this release. blosc2.utf8(): a new column type for high-cardinality/free-text
strings, storing each column as two companion NDArrays — int64 row
offsets plus a UTF-8 byte blob — the same layout Arrow uses for
large_string. A row costs exactly its encoded byte length (7-13x
smaller uncompressed than fixed-widthstring()on high-cardinality
text), and reads materialize as NumPyStringDTypearrays (NumPy >=
2.0 required; older NumPy falls back tovlstringon Arrow/Parquet
import with a clear message). Full query surface: comparisons,
where(),sort_by,group_bykeys,fillna, and Arrow export
(large_string, sentinel-null mask) / import (Arrow/Parquet string
columns now default toutf8instead ofvlstring). Measured on the
1e7-row NYC-taxicompanycolumn: ingest 3622 ms -> 598.6 ms
(6.05x faster, only 1.22x slower thanstring()and 2.63x faster
thanvlstring()), full-column read 2472.6 ms -> 165.3 ms (14.96x
faster), equality filter ~1900 ms -> 162 ms (~11.7x faster), and
groupby-key factorization 3304 ms -> 558 ms (2.83x, down from a
16.9x-slower initial fallback, now faster than fixed-widthstring()
keys). See the new "Choosing a string column type" guide in the
CTablereference andexamples/ctable/utf8_strings.py. Known gaps:
string-expression filters (t.where("name == 'x'")) and
create_indexonutf8columns still raise a clear
NotImplementedError; use fixed-widthstring()if you need those.- pandas engine, pandas 3 compatible:
engine=blosc2.jitfor
DataFrame.applynow returns a properly indexedDataFrame/Series
under pandas 3.0.3's defaultraw=False(previously a raw NumPy
array, so results only matched by value, never by type).
Series.map(func, engine=blosc2.jit)is now implemented (it
previously always raisedNotImplementedError). Non-numeric columns
now raise a clearValueErrorinstead of a deepnumexprerror. See
the guide "Using Blosc2 as a pandas engine" and
bench/bench_pandas_engine.py.
pandas-style CTable API
CTable.assign(**named_exprs): return a view with additional computed
columns, without mutating the table or copying column data. Pairs with
the newblosc2.col(name)— an unbound column expression that defers
operator replay until it's bound to a table (assign(),t[...],
where()) — to write pandas-3-style chains:
t.assign(profit=col("revenue") - col("cost"))[col("profit") > 0].sort_by("profit", ascending=False).head(10).- Missing data:
Column.fillna(value)replaces sentinel/None
values for scalar, dictionary, and varlen-scalar columns;
CTable.dropna(subset=None)returns a view excluding rows where any
nullable column (or a chosen subset) is null. Column arithmetic
(+ - * / // % **) and comparisons (< <= > >= == !=) on nullable
int/timestamp/bool columns now propagate nulls instead of operating on
the raw sentinel: arithmetic promotes tofloat64/NaN, comparisons
follow SQLWHEREsemantics (a null operand never satisfies any
comparison) — fixing filters liket[t.x < 0]wrongly matching null
rows. Also fixes null detection for timestamp columns
(is_null()/null_count()/dropna()previously missed every
NaT). - UDF aggregations:
group_by().agg()accepts a custom callable as
the op via the named form (output_name=(column, callable[, dtype]));
it receives each group's live, non-null values as a 1-D NumPy array.
CTable.apply(func, columns=None, dtype=None, engine="auto")applies
a UDF across the table's live rows, sugar overblosc2.lazyudf().
group_by(engine=...)now accepts"auto"/"numpy"explicitly
alongside the existing default. CTableviews are now read-only for value writes:Column.__setitem__
andColumn.assign()raiseValueErroron a view, pointing at
take()/copy()as the escape hatch (structural mutations already
raised; this closes the one unguarded cell-write path).NDArray.iter_sorted()/argsort()on aFULL-indexed array now reads
the sidecar range directly instead of building the full permutation —
~52x faster and ~193x less memory on a 20M-element array for
iter_sorted(start=-k)-style tail queries. See the new optimization
tip.
Improvements
- String-key
group_by()(fixed-widthstring()keys) now factorizes
via an exact hash of each row's raw bytes instead of a NumPy
UTF-32 argsort, with a vectorized collision-checked verify pass
keeping the result bit-identical: 1157 ms -> 737 ms on a 1e7-row
benchmark. Every caller benefits automatically; noengine=switch
involved. - An example showing ctables and ndarrays bundled together in the same
TreeStore. - C-Blosc2 bumped to 3.2.3.
Bug fixes
- Fixed a
@blosc2.dsl_kernel-decorated function crashing unconditionally
when passed as a groupby UDF aggregation (g.agg(name=(col, dsl_kernel_fn))): it now runs like the equivalent undecorated callable. - Fixed
CTable.head()/tail()silently discarding row order when called
on a lazily-sorted view (e.g.t.sort_by("col", ascending=False)on a
view, or any.sort_by()result chained off a prior filter): they
ignored_cached_live_positionsand built a plain physical-order mask
instead, sot.where(...).sort_by("x", ascending=False).head(10)came
back in the wrong order. - Fixed a
NameErrorwhennan/infscalars appeared in lazy
expressions;ShapeInferencerno longer ignores user-provided shapes
fornan/inf. - Fixed
CTable.from_arrow()raisingTypeError: No blosc2 spec for Arrow type DataType(string_view)on any Arrowstring_view/binary_view
column — the layout Polars exports by default through the PyCapsule
protocol.string_view/binary_viewnow import exactly like
string/large_string/binary/large_binaryeverywhere a column's
Arrow type is inspected (schema inference, null-sentinel selection,
list/struct/dictionary value types).
Release 4.8.1
Changes from 4.8.0 to 4.8.1
Improvements
- Read-only memory mapping for
CTablestores:CTable.open()(and
FileTableStorage) gain anmmap_mode="r"parameter, mirroring
blosc2.open(). All members of a read-only store — scalar, list, varlen
and dictionary columns alike — are then read from mapped pages; for.b2z
archives, in place at their offsets inside the single mapped container
file. With several concurrent readers on one file this pays off quickly:
2.5x/4.4x/4.5x faster wall time for 1/4/8 readers in our benchmark
(bench/optim_tips/tip_10_mmap_many_readers.py). - Reduced memory consumption in
CTable.extend()when passed anNDArray.
Bug fixes
- Fixed lifetime/use-after-free hazards around zero-copy cframes and
vlmeta:schunk_from_cframe()/ndarray_from_cframe()withcopy=False
(the default) returned objects pointing into the caller's bytes buffer
without keeping it alive, so a temporary cframe (e.g.
ndarray_from_cframe(response.content)) could be reclaimed under the live
object, corrupting reads. The buffer is now pinned on the returned object.
Also,vlmetaread paths (__getitem__/__len__/__iter__) now raise
ReferenceErroron an orphaned owner instead of segfaulting, matching the
write paths. BatchArray.delete()/ObjectArray.delete(): negative-step slices
(e.g.del arr[3:0:-1]) deleted chunks in ascending order, shifting the
indices of chunks still to be deleted and removing the wrong ones (or
raisingRuntimeError).ListArray.extend_arrow(): Arrow chunks were appended to the backend
without flushing pending cells first, reordering unflushed rows after the
new ones.- Shape inference:
stack()with a negative axis inserted the new dimension
one position too early, so a lazyexpr's reported.shapedisagreed with
its computed result;vecdot()also normalized positive axes as if they
were negative. - Chunked
matmul(): broadcast (size-1) operand batch dims were sliced with
the result-chunk coordinates, producing empty slices when the broadcast
dim spans several result chunks. DictStore.__setitem__(): overwrite semantics depended on value size —
embedded keys refused overwrite ("already exists"), while an
embedded-to-external overwrite double-stored the key and resurrected the
stale embedded value after a delete. Assignments now behave uniformly
dict-like, dropping any previous value.- Explicitly passing
cparams=Noneordparams=Noneto NDArray
constructors crashed; both now mean "defaults". - Fixed a builtin-shadowing bug that made store detection in
blosc2.open()recurse ~250 times (silently swallowed) on every
.b2z/.b2dopen; opening a.b2zis now ~10x faster under allocation
tracing.
Documentation
- Restructured docs (#674), with a new
Optimization tips
section, including new tips on grouping related data into a single
memory-mapped.b2zfile and on usingmmap_mode="r"with many
concurrent readers.
Release 4.8.0
Changes from 4.7.0 to 4.8.0
Sharing containers across processes
- New
lockingstorage parameter (and theBLOSC_LOCKINGenvironment
variable to enable it fleet-wide) serializes accesses to an on-disk
SChunk/NDArray/EmbedStore/DictStoreagainst other handles and other
processes, via a small sidecar lock file (.b2lock). Advisory: every
handle touching the container must opt in. SChunk.holding_lock()/NDArray.holding_lock(): a context manager to
hold the exclusive lock across several operations, making a multi-step
mutation atomic to other locked handles.- New
SChunk.refresh(), mirroring the existingNDArray.refresh(). - Fixed a data-loss bug in
NDArray.append(): it read the cached,
unrefreshed shape before computing the resize target, so under
concurrent growth/shrink — even insideholding_lock()— another writer's
just-appended data could be silently deleted. EmbedStoreandDictStore(.b2d) now support cross-process writers
under locking: transactional writes plus key-map re-sync, so readers
follow keys added or removed by another process.DictStore.to_b2z()(andTreeStore, which inherits from it) now replaces
the target file atomically, so concurrent readers always see either the old
or the new archive, never a torn one.- Growth-SWMR (single writer, multiple readers): a reader
NDArrayhandle
opened before aresize()made through another handle follows the new
shape on its next data access, or via the new explicitNDArray.refresh(). - New user guide page,
Sharing containers across processes,
covering all of the above plus the caveats (NFS,mmap_mode, Windows
in-use-file rename).
Bug fixes
- Fixed
detect_aligned_chunks()(used internally to fast-path aligned
slice reads/writes): a floor-division undercounted the chunk grid for
arrays whose shape isn't a multiple of the chunk shape, which could
silently return the wrong chunk's data for an otherwise-aligned slice
with a nonzero start in an earlier dimension.
Others
- Raised the manylinux wheel baseline from
manylinux2014(CentOS 7, glibc
2.17, GCC 10.2) tomanylinux_2_28(AlmaLinux 8, glibc 2.28, GCC 12),
fixing a build failure with NumPy >=2.5 which requires GCC >=10.3.
Release 4.6.0
Changes from 4.5.1 to 4.6.0
CTable.sort_by(view=True): zero-copy sorted views
-
CTable.sort_by()now acceptsview=True, returning a lightweight
sorted view that shares the parent's column data and gathers rows on
demand in sorted order — no whole-table copy. This is ideal for reading a
sorted slice of a large (possibly on-disk) table::t.sort_by("col", view=True)[:10] # top-10 without materialisingSorting on a fully indexed column streams directly from the index, so the
table is never materialised. Multi-column sorts and dotted (nested) leaf
names are supported (e.g.t.sort_by(["trip.begin.lon", "payment.fare"], ascending=[True, False])).
where on dictionary (string) columns
whereexpressions now work over dictionary-encoded (string) columns,
including membership tests such as'"Acme" in company', so categorical
text columns can be filtered without decoding the whole column.
b2view is now an opt-in extra
- The
b2viewterminal browser and its TUI stack (textual,
textual-plotext) are no longer core dependencies: a plain
pip install blosc2no longer pulls them, keeping the compression library
lean (and dropping deps that are unusable under wasm32, which has no TTY).
Install the viewer withpip install "blosc2[tui]", or
pip install "blosc2[hires]"to also get the high-reshview. The
b2viewcommand prints this hint if the dependencies are missing.
group_by: flexible aggregation naming
-
CTable.group_by(...).agg()now accepts a list of(column, ops)pairs
and explicit output names (pandas-style keyword arguments), alongside the
existing auto-suffixed mapping; the forms can be combined::g.agg({"sales": ["sum", "mean"]}) # auto: sales_sum, sales_mean g.agg([(t.sales, ["sum", "mean"])]) # auto, but accepts Column objects g.agg(revenue=("sales", "sum")) # explicit: revenue g.agg({"sales": "sum"}, n=("*", "size")) # combined, with a named row countThe list-of-pairs and named forms accept
Columnobjects (t.sales), which
the mapping form cannot becauseColumnis unhashable and so cannot be a dict
key. -
Aggregation ops may also be given as the matching blosc2 reduction functions
(blosc2.sum,mean,min,max,argmin,argmax), matched by
identity -- e.g.g.agg([(t.sales, [blosc2.sum, "mean"])]). This is a
naming shorthand only; arbitrary/UDF callables (and look-alikes such as
np.sumor a user function namedsum) are rejected rather than silently
misinterpreted.
group_by / group_reduce: tri-state sort=
- Vectorized dictionary group ordering:
group_by()result building now
batch-decodes dictionary (string) keys in one pass (decode_batch) instead of
onedecode()per group, making high-cardinality string group-bys dramatically
faster (end-to-endgroup_by().size()dropped from seconds to milliseconds on
~100k-group workloads). sort=is now a tri-state (None/True/False) on both
CTable.group_by()andblosc2.group_reduce():True— always return groups sorted by key.False— never sort; deterministic but unspecified order.None(the new default) — auto: sort only when cheap. Integer and
dictionary keys are sorted (free / vectorized); float and multi-key results,
whose only ordering is an O(G log G) Python sort over every distinct group,
are left unsorted to avoid a cost that can rival the grouping itself on
high-cardinality data.
- Behavior changes (the two APIs had different prior defaults, so they move
in opposite directions):CTable.group_by()previously returned results always sorted. Under the
newNonedefault, float-key and multi-key group-bys are no longer
key-sorted by default — passsort=Trueto restore sorted output. This is
a deliberate divergence from pandas (which defaults tosort=True), suited
to blosc2's large / on-disk datasets.blosc2.group_reduce()previously defaulted tosort=False(unsorted).
Under the newNonedefault its cheap kernels now sort by default —
most visibly float keys, which previously came out in hash order. Integer
keys were already ascending; the generic Python fallback stays unsorted.
Passsort=Falseto opt out.
Accelerated reductions from index summaries
min/maxon indexedColumns, andargmin/argmaxinsidegroup_by, are
now accelerated using the index's per-block min/max summaries: when an
index is available these reductions run from the precomputed summaries instead
of decompressing the underlying data, which is dramatically faster on large
columns. A fast path also builds min/max envelope plots from any index.- The last
group_byoperation is memoized and reused when the same
grouping is requested again, avoiding recomputation in interactive / repeated
workflows (e.g.b2view).
b2view: group-by, sort, and richer plots
- Interactive group-by (
G): group aCTableby a column (integer, string,
or now float keys) directly in the viewer, with a three-list / two-column
menu; while grouped,S/Roperate on the grouped result and the data
panel's subtitle shows aG(roup)chip. The last grouping is memoized for
instant reuse. - Sort by column (
S): sort aCTableby a fully indexed column via a
dropdown (Rtoggles reverse) as a zero-copysort_by(view=True)that streams
from the index — the table is never materialised,Escrestores the original
order, and aSORTEDchip shows in the status bar. Non-indexed columns can
now be sorted too. Sort and filter are mutually exclusive; a row window
composes over a sort, and anfilter is preserved acrossSort /Group. - Better plots of grouped/sorted views: a grouped view plots bars for a
categorical key and lines for a numeric key; numeric-key group plots
render as stem/impulse charts rather than misleading connected lines. Bar
plots gain anhi-res counterpart mirroring the line/scatter plots, and+/-
zoom about the view's left edge. --maxmaximizes the current panel, andescapeis now the single,
consistent way to back out of every modal.
Other / bug fixes
- C-Blosc2 upgraded to 3.1.5.
- Open-file cache correctness: cached open handles are now validated against
the file's fingerprint (st_mtime_ns,st_size) and cached index handles are
released when a table closes, so a file changed underneath an open handle is no
longer served stale. - NumPy 2.5 compatibility: adjusted for deprecations in NumPy 2.5.
- Substantially reduced test-suite runtime, and emscripten builds no longer
attempt to spawn subprocesses (unsupported there).
Release 4.5.1
Changes from 4.5.0 to 4.5.1
This follow-up release builds the b2view terminal viewer into a richer
data-exploration tool — a scatter plot, a searchable column picker, a
one-shot demo download, refreshed chrome, and several interaction fixes — and
upgrades the bundled C-Blosc2 to 3.1.4. WASM/Pyodide is now a fully
supported platform, and CTable.info reports per-column compressed sizes.
b2view: richer exploration
- Scatter plots: from a column plot, press
sto scatter the current column
(X) against another column (Y) chosen from a list, over the current (zoomed)
row range;hthen opens a high-resolutionmatplotlibscatter. - High-res for 1-D series is now an envelope plot (matching the in-terminal
view), and a newrkey toggles between the min/max envelope and the raw
values (strided-sampled when the range is wide). - Searchable column picker: the
cgo-to-column key now opens a searchable,
selectable list (type to filter, ↑/↓, Enter) for CTables, instead of a text
field; N-D arrays still go by numeric index. - Show/hide columns:
/opens a searchable multi-select to pick which CTable
columns are displayed. - Demo download:
b2view --downloadfetches a demo bundle
(chicago-taxi-flat.b2zby default) into the current directory if it is not
already there, then opens it. - Refreshed chrome: a branded header, a left-docked filename label in the
title, and clearer status chips.
b2view: interaction fixes
- Go-to-row/column pre-fill is now pre-selected, so the first keystroke
replaces the current index instead of appending to it (typing a column name no
longer produced e.g.0payment.fare). - Escape keeps its layered exit while a panel is maximized: with the data
panel maximized, escape now unlocks a plot's locked row window (and clears
filters) as documented, instead of being hijacked into restoring the panel —
userto restore (ESCAPE_TO_MINIMIZE = False). - Test-suite robustness fixes (a timing flake and a Windows rendering glitch).
Other
- C-Blosc2 upgraded to 3.1.4.
- WASM/Pyodide is now a fully supported platform, with more frequent CI runs.
CTable.infoshows per-column compressed sizes (cbytesandcratio),
andprint_versions()uses clearerPython-Blosc2/C-Blosc2labels.
Release 4.5.0
Changes from 4.4.5 to 4.5.0
This release teaches the b2view terminal viewer to plot — peak-preserving
envelope line plots of any series, with zoom, a row-window lock, and an optional
high-resolution matplotlib view — and gives CTable a pandas-like display and
CSV experience. It also publishes WASM/Pyodide wheels to PyPI and adds
faster strided reads for NDArray and Column.
b2view: plotting and data inspection
- In-terminal plots: press
pon a numeric series (a CTable column or an
array row) to draw a braille line plot. Plots are peak-preserving min/max
envelopes by default, so no spike or trough is hidden however large the
series is; large local series stream their envelope exactly in bounded
spans (only remote c2arrays fall back to a labeled strided sample). - Zoom and row-window lock: zoom the plot into a row range and pan it; press
vto lock the data grid to the plotted range so paging stays inside it
(escape unlocks). The plot and high-res views honor the locked window. - High-resolution view:
hopens a high-resmatplotlibimage of the
plotted range (new optionalhiresextra:matplotlib+textual-image). - On-demand cell decode:
enterdecodes a single skipped/expensive CTable
cell, and SChunk nodes now preview as a paged hex dump. - Fixes and polish: row paging re-aligns to the page grid after dim-mode
single-row scrolls; the data panel now focuses correctly with
--path ... --panel data; status chips are branded yellow.
CTable display
CTable.to_string()now renders the whole table by default (every row and
every column), likepandas'DataFrame.to_string(). Newmax_rowsand
max_widthparameters truncate on demand. Behaviour change: previously
to_string()returned the truncated view; code that relied on that should
passmax_rows=/max_width=(or usestr()).- The
[N rows x M columns]dimensions footer now follows pandas: omitted by
to_string()(passshow_dimensions=Trueto force it), and shown by
str/repr/printonly when the view is actually truncated. Previously it
was always appended. repr(ctable)now shows the same truncated table asstr(ctable)
(pandas/polars convention), instead of the one-lineCTable<…>summary. The
compact summary remains available viactable.info.- New display options in
set_printoptions:display_widthcontrols the
column-fitting width budget (None= auto-detect terminal,-1= show all
columns, positive int = fixed budget), anddisplay_rowsnow accepts-1to
show all rows (0still shows none). - New
blosc2.printoptions(...)context manager temporarily sets the display
options and restores them on exit, e.g.
with blosc2.printoptions(display_rows=-1, display_width=-1): print(t).
CTable I/O
CTable.to_csv()now accepts no path, returning the CSV as a string like
pandas'DataFrame.to_csv(). Passing a path still writes the file (and
returnsNone); the returned string is byte-for-byte the same as the file.
Performance
- Faster strided reads:
NDArray.__getitem__gains a sparse-gather fast
path for large strides, andColumn.__getitem__short-circuits when the
logical positions equal the physical ones. - Fix: a negative
stepinColumngetitem could return[]; it now
returns the reversed selection.
Indexing
- Fix: a sidecar-handle cache collision could return the wrong SUMMARY
index for a compact-store column. - Cross-column index pruning is now enabled for compact CTable queries, so
more predicates prune blocks before any data is materialized. The docs also
note when summary indexes are not created automatically.
Packaging
- WASM/Pyodide wheels on PyPI: the main wheel build now also produces
pyemscriptenwheels for CPython 3.13 (2025 ABI) and 3.14 (2026 ABI) and
uploads them to PyPI, soblosc2ismicropip-installable in Pyodide, and
b2viewprints a clear message instead of crashing when run under WASM.
Known limitation: slicing an in-memorySChunkloaded from a frame fails on
the Pyodide 0.29.x Emscripten toolchain (cp313); it works on Pyodide 314
(cp314) and natively. See issue #664. - cibuildwheel updated to 4.1.
Release 4.4.5
Changes from 4.4.3 to 4.4.5
Note: 4.4.4 was skipped due to a failure during the release process.
This release promotes the b2view terminal viewer to a core feature —
installed by default, with new interactive row and column filtering — and
makes BatchArray block layouts (and hence compression ratios) reproducible
across CPUs.
b2view, the terminal data viewer
- Installed by default:
textualandrichare now regular
dependencies, so theb2viewCLI works out of the box (the[tui]extra
is gone). A getting-started walkthrough was added to the docs, and the
README now lists the CLI tools. - Row filtering: pressing
fon a CTable node opens a modal that takes
the same string expressions asCTable.where()(dotted nested names,
and/or) and pages through the matching view. Filters are remembered
per node for the session, the data header shows the active filter plus
the unfiltered total, and escape (or an empty expression) clears it. - Column filtering:
/narrows the visible columns by case-insensitive
substring; column paging and thecgoto-column modal then operate on
that subset. Combines freely with the row filter; escape clears one
layer per press (rows first, then columns). - Mouse handling: the terminal owns the mouse by default, so native
text selection/copy works like in any CLI program;--mouselets b2view
capture it instead (click-to-focus, wheel scrolling by half a page,
paging at the edges). - Navigation:
?opens a help screen listing all keys;cjumps to a
column by index, exact name or unique name prefix;s/ejump to the
first/last column window; row paging and jumps keep the cursor on its
column; dim-mode index/viewport movements clamp at the boundaries instead
of wrapping around. - Rendering: column windows are fitted from measured rendered widths
(and re-fitted on terminal resize and panel maximize/restore), and float
columns use a uniform number of decimals so decimal points align down
the column. - Test suite: first automated tests for the TUI — Pilot-driven keyboard
journeys against a deterministic generated store (markertui), plus
render unit tests. Skipped on wasm, where Textual apps cannot start
(no termios).
BatchArray
- Reproducible block layouts: automatic variable-length block sizing
now uses fixed byte budgets (1 MiB for clevel 1-3, 8 MiB for 4-6, 16 MiB
for 7-8) instead of the CPU cache sizes, so the layout — and hence the
compression ratio — no longer depends on the machine that created the
array.
Build and docs
- Installing test dependencies: the docs now use
pip install . --group test(a PEP 735 dependency group); the stale
[test]extra syntax was removed. - cibuildwheel updated to 4.0.
Release 4.4.3
Changes from 4.4.2 to 4.4.3
This is a maintenance release focused on faster CTable cold-start, printing
and groupby performance, a lighter import blosc2, new raw-storage access
for columns, and support for the new J2K/HTJ2K codec plugins.
CTable performance
- Lazy column opening in views:
select()(and other view-producing
operations) no longer open every projected column up front. A column is
only opened from storage when the view actually reads it, so selecting and
then touching a subset of columns — or aggregating a single one — skips
the cold-start cost of the rest. - Lazy index opening in queries: query planning no longer opens every
SUMMARY-indexed column on a wide persistent table; only indexes for
columns actually referenced by the predicate are loaded. - Faster table printing:
repr()/to_string()now memoise per-column
sparse gathers for the duration of a render and combine the head and tail
rows into a single sparse read per column. Each column is read from
storage once instead of ~6 times (precision detection, width sizing and
row rendering all hit the cache). - Groupby with integral float keys: float key columns whose values are
integral and fit a compact non-negative range (e.g. float32 id/second
columns) now take the dense single-key fast path instead of the markedly
slower generic float-hash path. Fractional or non-finite keys fall back
automatically. - No tempdir in read mode: opening a
.b2z/.b2dstore in'r'mode
no longer creates a temporary working directory, since nothing is ever
written.
Lighter imports and prefetcher rework
- asyncio dependency dropped: the on-disk chunk prefetcher used by the
UDF and numexpr fallback engines now uses plainconcurrent.futures
instead of an asyncio event loop.import blosc2no longer pulls in
~30 asyncio modules, saving ~3 MB of memory footprint at import time. - Prefetcher deadlock fixed: an exception during evaluation could leave
the generator finalizer blocked forever inthread.join()while the
reader thread was stuck on a full prefetch queue. A stop event now makes
the producer bail out when its consumer goes away.
New features
Column.rawaccessor: returns the underlying storage container of a
column (NDArray,ListArray,DictionaryColumn, …) directly. Unlike
Column.__getitem__, which always materializes NumPy arrays, this is the
column as a blosc2-native compressed object — usable as a lazy-expression
operand without decompressing, and exposing storage details likeschunk,
chunksorcparams. Note that this is a physical view: fixed-width
containers are over-allocated to chunk capacity, so slice tolen(table)
to get just the live rows, and no validity-mask or null-sentinel
processing is applied. RaisesAttributeErrorfor computed columns,
which have no backing storage.- J2K and HTJ2K codec IDs:
blosc2.Codec.J2Kandblosc2.Codec.HTJ2K
expose the IDs for the new JPEG 2000 codec plugins (installable with
pip install blosc2-j2kandpip install blosc2-htj2k).
Fixes
--float-trunc-precand nested columns: the precision-truncation
filter of theparquet_to_blosc2CLI now propagates to float fields
inside nested (struct/list) columns too.- Guard for unsupported computed-column expressions: expressions that
would serialize to an empty or non-round-trippable string are now rejected
with an early, actionableValueErroratadd_computed_column()time,
instead of silently breaking on reload.
Build
- C-Blosc2 updated to 3.1.3.
Release 4.4.2
Changes from 4.4.1 to 4.4.2
This is a feature and maintenance release that promotes DSL kernels to
first-class CTable computed columns, adds a new CTable.__setitem__
assignment idiom, optimises bulk NDArray writes, and fixes several
correctness issues.
DSL kernels as first-class CTable columns
add_computed_column()accepts DSL kernels:@blosc2.dsl_kernel-decorated
functions can now back virtual computed columns directly, in addition to
the existing string-expression form. The column survives save/open
round-trips via persisteddsl_source.add_generated_column()accepts DSL kernels: stored generated columns
(written duringappend/extendand onrefresh_generated_column())
now support DSL kernels as their transformer.CTable.where()accepts UDF/DSL kernels: filter predicates are no
longer limited to expression strings — any DSL kernel can be passed directly.dtypeinference for DSL kernels: whendtypeis omitted,
lazyudf()infers the output dtype via NumPy type promotion of the input
column dtypes. Passdtypeexplicitly for type-changing kernels
(comparisons, casts).kernel_from_source()utility: newdsl_kernel.kernel_from_source()
reconstructs aDSLKernelfrom its stored source text, shared by the
CTable DSL-column loaders and the persistedLazyUDFdecoder.- Security note:
.b2dfiles from untrusted sources that contain DSL
computed columns execute stored Python source on open. A warning is now
included in the documentation.
New CTable.__setitem__ column-assignment API
t["col"] = arr: new shorthand equivalent tot["col"][:] = arr.
Accepts any array-like includingblosc2.NDArray. RaisesKeyErrorfor
unknown columns andValueErrorfor views or read-only tables.
Chunked NDArray writes in extend() and Column.__setitem__
extend({"col": ndarray})decompresses chunk-by-chunk: when a
blosc2.NDArrayis passed as a column value toextend(), it is now
written in chunks instead of being fully decompressed upfront. Pass
validate=Falseto avoid a transient full decompression during constraint
checking.col[:] = blosc2_ndarrayfast path: a new no-holes fast path in
Column.__setitem__skips the O(n) validity-mask gather and writes the
NDArray one chunk at a time using contiguous slice writes. Works for both
scalar and fixed-shape ndarray columns. Falls back to a chunked fancy-index
path when deleted rows are present.
BLOSC_ME_JIT environment variable override
- Full CLI override:
BLOSC_ME_JITnow takes unconditional priority over
both thejit=andjit_backend=keyword arguments, making it easy to
switch JIT backends from the command line without modifying code.
Correctness fixes
- View corruption in
Column.__setitem__: aNone == Noneguard
evaluation on view-backed columns could fire the NDArray fast path,
bypassing physical-position remapping and silently corrupting rows. Fixed
by explicitly checkingbase is Nonebefore activating the fast path. CTable.__setitem__view guard: the newt["col"] = arrAPI now
raisesValueErroron views, matching the contract of all other mutating
CTable methods.- Fast path enabled for disk-opened tables: the fast path previously
remained dormant for tables opened from disk because_last_posstarts as
None. The guard now calls_resolve_last_pos()to lazily initialise it. - DSL column
jit_backendpreserved in_empty_copy: thejit_backend
setting was silently dropped during internal table copies; it is now
retained. lazyexprColumn unwrapping:convert_inputs()now automatically
unwrapsCTable.Columnobjects to their backing NDArray so that shape and
identity checks work correctly.
Documentation and examples
- Parquet-to-blosc2 walkthrough: new step-by-step tutorial added to the
getting-started section. Thanks to @SyedIshmumAhnaf. - CTable performance tips: new section in the overview covering when to
prefer computed vs. generated columns, chunk sizing, and query optimisation. - Simplified docstring examples: examples throughout
ndarray.pyand
ctable.pynow useblosc2.array(),blosc2.arange(), and
blosc2.linspace()directly instead of two-step numpy-then-asarray
patterns. udf-computed-col.pyexample: new end-to-end example demonstrating DSL
kernel computed and generated columns.