Repository navigation
Make SafeDisk.with_numpy() reads as fast as pickle and store numpy data in keys and values - #47
Merged
Merged
Conversation
Documentation build overview
|
…umpy arrays, scalars, time scalars, object, record and masked arrays and memory maps in its keys and values.
avgvi
force-pushed
the
feat/with-numpy-fast-arrays
branch
from
November 6, 2026 06:24
8491e1e to
17b827e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes
SafeDisk.with_numpy()reads as fast as the default pickleDisk, and extends what it stores so that numpy data works in keys as well as values. Existing entries are unchanged: the stored bytes of every type 5.8.3 already handled are identical, and 5.8.3 and this branch read each other's plain array entries.Speed
with_numpy()now keeps the.npyheaders it has parsed or written, in two bounded in-memory tables, instead of parsing each header with numpy's literal parser on every read. The size of the data is still checked against the header on every read.Key cost on a
with_numpy()disk, compared with 5.8.3:str,int,floatandbyteskeys are unchanged, and so are keys ofwith_typesclasses (21.1 → 21.2 µs). A tuple of plain values costs about 0.3 µs more (4.82 → 5.13 µs), for the check that converts numpy scalars in it. PlainSafeDiskis unchanged.New in keys
item()gives, also inside tuples, lists, dicts, sets and frozensets, soint64(1)and1name the same entry andmemoizehits for either.memoizearguments. Every array in a key is written in C order, so an equal array of the same dtype and shape names the same entry. Another dtype, shape or time unit names another entry.SafeDiskpickles it, at any protocol.New in values (and keys)
datetime64/timedelta64scalar('scalar', dtype name, int64 count)NaTincluded; a unit multiple over 9 digits raisesTypeErrorat write('objects', shape, items), each item checked like any valuenumpy.recarray('records', array)recarraynumpy.ma.MaskedArray('masked', data, mask, fill_value, hard_mask)MaskedArraywith its mask, fill value and hard masknumpy.memmapndarrayAll of these are persistent IDs, as
'npy'and'scalar'already were: rows name no module or function. A value holding an array of objects is read back once before it is stored, so a self-referencing array raisesTypeErrorinstead of being written unreadable.numpy.matrix, otherndarraysubclasses, arrays with object fields,longdoubleand structured scalars are still refused, with messages that say what to store instead. Entries and keys using the new kinds read as unreadable on 5.8.3 and earlier.Fix
Values whose pickling handed out short-lived views of one array (for example an object whose
__reduce__yields slices) could be stored with one view's data in place of later ones on 5.8.2 and 5.8.3. The pickler's memo now holds each array object it has seen, so a freed view's id is never reused for another.Review and tests
An independent design and logic review ran on an earlier version of this branch; this PR includes its changes: persistent IDs in place of module-level rebuild functions, the key fast paths, the write-time time-unit check, and its test gaps (pinned key bytes, the key pickle scan, two time units in one value, recursion, the header size limit, protocols 2, 4 and 5).
pytest: 985 passed on numpy 2.5 (arrays.py100% covered, 99.22% overall) and 983 on numpy 1.26, Python 3.12.cached(), unchanged, now caches 2-D rows, numpy numbers and image arrays on this branch.CHANGELOG.mdhas the entries under Unreleased; they don't name this PR's number yet.