Skip to content

Commit c3de2f7

Browse files
committed
Getting ready for release 4.10.1
1 parent 071ff52 commit c3de2f7

4 files changed

Lines changed: 133 additions & 44 deletions

File tree

ANNOUNCE.rst

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,37 @@
1-
Announcing Python-Blosc2 4.10.0
2-
==============================
3-
4-
This is the string-support release: string expressions and DSL kernels now
5-
run on miniexpr, ``utf8()`` and ``dictionary()`` columns gain full indexing
6-
and comparisons, and NumPy's ``StringDType`` is understood by the array
7-
constructors. Alongside, slicing with plain keys is up to 1.7x faster and a
8-
new ``blosc2.random`` module brings chunk-parallel, NumPy-quality random
9-
constructors.
10-
11-
- **String expressions and DSL kernels over strings.** Concatenation,
12-
``lower``/``upper``/``strip``/``replace``/``substr``/``split_part`` and
13-
friends now run on miniexpr over fixed-width ``<Un`` and bytes ``S``
14-
arrays, producing string results sized by miniexpr itself. ``utf8()``
15-
columns can be queried in expression form
16-
(``t.where("name == 'x'")``), with scalar comparisons 5-6x faster via a
17-
raw-byte scan, and new ``blosc2.utf8_array()`` builds variable-length
18-
arrays directly.
19-
20-
- **Full indexing for string columns.** ``create_index()`` now works on
21-
``utf8()`` and ``dictionary()`` columns via alphabetical ranks —
22-
``sort_by`` drops from 424 ms to 7 ms at 1M rows — and scalar comparisons
23-
are served from the index.
24-
25-
- **New ``blosc2.random`` module**: 42 of NumPy's 43 ``Generator`` methods,
26-
each chunk generated in parallel with its own seeded ``PCG64`` stream
27-
(~3x faster than the NumPy path on 100M elements).
28-
29-
- **Slicing up to 1.7x faster.** Plain slice/int keys skip ndindex's general
30-
machinery (it was 43% of a scattered-read loop); strided steps, ellipsis
31-
and fancy indexing still use it.
32-
33-
- **String plumbing**: ``from_utf8()``/``to_utf8()`` conversions,
34-
``CTable.add_column(values=)``, ``Column.assign()`` on variable-length
35-
columns, ``StringDType`` dispatch in the array constructors, and DSL
36-
operands can be native NumPy arrays or pandas ``Series``.
37-
38-
- **Important fixes**: string column indexes returning zero rows at the
39-
default column width, ``SChunk`` slices for typesizes above 255 bytes
40-
(upstream, via C-Blosc2 3.3.1), scalar bools in tuple keys now matching
41-
NumPy, and a batch of miniexpr correctness fixes.
1+
Announcing Python-Blosc2 4.10.1
2+
===============================
3+
4+
A correctness release: lazy indexing and reductions now follow NumPy in a
5+
batch of cases where they quietly did not, the stores close several
6+
cross-process read races, and wheels finally ship usable C-Blosc2 development
7+
files. Bundled C-Blosc2 moves to 3.3.2.
8+
9+
- **Lazy indexing now matches NumPy.** Integer indexing no longer squeezes
10+
length-1 axes the index kept, a ``None`` in the key stops shifting operand
11+
axes for ``LazyUDF`` and broadcast operands, indexing a full reduction
12+
slices the operands instead of evaluating over everything, and
13+
``datetime64``/``timedelta64`` comparisons work in expressions rather than
14+
raising.
15+
16+
- **``NDArray.nbytes`` reports the logical size**, ``size * itemsize``, as
17+
NumPy does. ``.schunk.nbytes`` still gives the padded figure, which is what
18+
``cratio`` keeps measuring.
19+
20+
- **``CTable.where()`` applied a short boolean mask to the wrong rows.** A
21+
mask no longer than the live-row count is now logical — entry *i* selects
22+
the *i*-th live row — instead of being padded out to the physical length
23+
and picking up rows outside the view.
24+
25+
- **Cross-process store fixes.** ``EmbedStore`` and ``DictStore`` resolved a
26+
key under the store lock but read the data after releasing it; the resolve
27+
and the read now share one lock. Overwriting an external ``DictStore`` leaf
28+
is atomic too — the new leaf is built beside its final name and moved into
29+
place — so a concurrent reader can no longer open a half-rewritten file.
30+
31+
- **Wheels ship working C-Blosc2 development files** (``pkg-config`` and
32+
``find_package(Blosc2)`` both failed against an installed wheel before),
33+
and are ~1.5 MB smaller, carrying two copies of ``libblosc2`` rather than
34+
three.
4235

4336
Install it with::
4437

RELEASE_NOTES.md

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,103 @@
22

33
## Changes from 4.10.0 to 4.10.1
44

5-
XXX version-specific blurb XXX
5+
A correctness release: lazy indexing and reductions now follow NumPy in a
6+
batch of cases where they quietly did not, the stores close several
7+
cross-process read races, and wheels finally ship usable C-Blosc2 development
8+
files. Bundled C-Blosc2 moves to 3.3.2.
9+
10+
### Bug fixes
11+
12+
#### Lazy expressions and indexing
13+
14+
- **Indexing a lazy expression with an integer squeezed too much.**
15+
`expr[0]` dropped *every* length-1 axis of the result, including ones the
16+
index kept, so a `(1, 4)` expression indexed at `[0]` came back as `(4,)`
17+
where NumPy gives `(4,)` only for the consumed axis and keeps the rest.
18+
Only the dimensions the integer indices actually consumed are dropped now.
19+
`where()` results, whose length is data-dependent, are left alone.
20+
Closes #319.
21+
- **`LazyUDF` and broadcast operands mis-indexed on `None`.** A `None` in the
22+
key inserts an axis in the result but consumes none in the operand;
23+
aligning it as if it did shifted every axis to its left by one, so
24+
`expr[None, 2]` read the wrong operand region. Closes #403, #688.
25+
- **Indexing a full reduction materialized the whole operand.** `(a + b).sum()
26+
[key]` evaluated the reduction over everything and then indexed; the
27+
operands are sliced first now. Closes #457.
28+
- **Datetime comparisons in lazy expressions raised.** numexpr has no datetime
29+
type, so `t1 < t2` on `datetime64`/`timedelta64` died with `unknown type
30+
datetime64[s]` and the error was re-raised rather than letting the NumPy
31+
fallback try. They are compared as their underlying int64 counts, which is
32+
exact, keeping the fast path. Mixed units and `NaT` decline that route and
33+
fall back to NumPy, since raw counts would silently lie about both.
34+
Closes #409.
35+
- **`NDArray.nbytes` reported the padded size.** It now returns the logical
36+
`size * itemsize`, matching NumPy, whenever the shape does not fill the
37+
chunk grid exactly. `cratio` still measures the stored (padded) data, so
38+
`nbytes / cbytes` need not equal `cratio`; use `.schunk.nbytes` for the
39+
padded figure. Closes #544.
40+
41+
#### Tables and stores
42+
43+
- **`CTable.where()` applied a short boolean mask to the wrong rows.** A mask
44+
shorter than the live-row count was padded out to the *physical* length,
45+
which aligned it with the underlying column and selected rows outside the
46+
view. A mask no longer than the live-row count is now treated as logical —
47+
entry *i* selects the *i*-th live row — with a short one simply leaving the
48+
trailing rows unselected. Closes #607.
49+
- **Cross-process read races in `EmbedStore` and `DictStore`.** Both resolved
50+
a key under the store lock but read the data after releasing it, so a
51+
concurrent writer could be caught mid-mutation: `EmbedStore.__getitem__`
52+
returned bytes from a stale offset, and `DictStore.__getitem__` opened an
53+
external leaf that a concurrent overwrite had just removed or half-rewritten
54+
(`KeyError`, or `RuntimeError: Error while getting the buffer`). The
55+
resolve, the existence check and the open now share one lock; non-shared
56+
stores skip it entirely. Fixes #691, #692.
57+
- **Overwriting an external `DictStore` leaf is now atomic.** `__setitem__`
58+
removed the old leaf and rebuilt it at the same path, and the handle
59+
`__getitem__` returns holds no file descriptor — the C layer re-opens the
60+
leaf by path for every chunk it decompresses — so a read already in flight
61+
could open a truncated file. The new leaf is built beside its final name and
62+
moved in with a single `os.replace()`, so every such re-open sees one
63+
complete cframe or the other. A crash mid-write now leaves a stray `.tmp`
64+
staging file rather than a partial leaf.
65+
- **`TreeStore.close()` swallowed inline handle failures.** A `CTable` that
66+
failed to flush left an archive whose row count disagreed with a varlen
67+
column, reported only on read, long after `close()` said it succeeded. Every
68+
handle still gets a chance to close and the store is still packed; the
69+
failure is then re-raised.
70+
71+
#### Other
72+
73+
- **`unpack_tensor()` turned padding into a phantom column.** `np.dtype()`
74+
renames the empty-named padding fields of a structured descr (`''` -> `f2`),
75+
so a packed tensor with padding came back with an extra field. Closes #287.
76+
77+
### Packaging
78+
79+
- **Wheels ship usable C-Blosc2 development files.** Install paths are now
80+
relative to `CMAKE_INSTALL_PREFIX`; the absolute ones made C-Blosc2 generate
81+
a `blosc2.pc` and exported targets pointing into the build tempdir, so
82+
`pkg-config` and `find_package(Blosc2)` both failed against an installed
83+
wheel. Verified by building and linking a C program against a wheel three
84+
ways: `pkg-config`, `Blosc2::blosc2_shared` and `Blosc2::blosc2_static`.
85+
miniexpr's license texts are mirrored into `.dist-info/licenses`, where PEP
86+
639 tooling looks. Closes #627.
87+
- **Wheels carry two copies of `libblosc2` instead of three**, ~1.5 MB
88+
smaller. C-Blosc2 set both `VERSION` and `SOVERSION`, and scikit-build-core
89+
follows symlinks, so the fully versioned file — referenced by nothing but
90+
the symlinks pointing at it — was shipped as a third full copy.
91+
92+
### Development
93+
94+
- The test suite runs in parallel by default (`-n auto --dist loadfile` in
95+
`pytest.ini`), 130s -> 35s locally, falling back to a serial run when
96+
pytest-xdist is absent. The `heavy` tests, 58% of everything collected and
97+
excluded from every push-time job, now run in a nightly workflow. The
98+
network tests run once per push on a single Linux job instead of five times.
99+
- The ruff rule set is spelled out with `select` rather than `extend-select`,
100+
so a ruff release widening its defaults no longer redefines what CI
101+
enforces.
6102

7103
## Changes from 4.9.1 to 4.10.0
8104

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ dependencies = [
4141
"rich",
4242
"threadpoolctl; platform_machine != 'wasm32'",
4343
]
44-
version = "4.10.1.dev0"
44+
version = "4.10.1"
4545
[project.entry-points."array_api"]
4646
blosc2 = "blosc2"
4747

src/blosc2/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = "4.10.1.dev0"
1+
__version__ = "4.10.1"
22
__array_api_version__ = "2024.12"

0 commit comments

Comments
 (0)