Repository navigation
Fix LoadSeuratRds losing on-disk layers after an object is moved or shared - #297
BenjaminDEMAILLE wants to merge 2 commits into
Conversation
SaveSeuratRds() records where each on-disk layer lives and LoadSeuratRds() reopens it from that record, but the record was only ever interpreted from the current working directory. Both ways of saving therefore broke as soon as the object was shared or reorganized: - relative = TRUE writes paths relative to the rds, so loading from any other working directory found nothing; - relative = FALSE writes absolute paths, so moving or renaming the folder holding the rds and its stores found nothing. In both cases LoadSeuratRds() warned "Cannot find any of the layer files specified" and returned an object with no layers at all. Resolve each cached path against the directory holding the rds file before checking it: try the recorded path as-is, then relative to that directory, then by name within it. Objects that have not moved keep loading through the first branch, so existing behaviour is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
cc @anashen Small one, and it is a genuine bug rather than a feature: One helper plus one call site, no new dependency. The new test file fails 6 assertions against Related Seurat-side PR: satijalab/seurat#10452. Outside contribution: I am not a member of the Seurat dev team, and this comment is not an official answer from the maintainers. |
The resolver handles comma-separated path entries, since some on-disk classes record more than one file per layer, but nothing exercised that branch. Add unit tests for it, plus the cases that must not be disturbed: a component that already resolves is returned byte-identical, an unresolvable one is returned unchanged rather than rewritten, and NA, empty and dir = NULL inputs pass straight through. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Superseded by #320, which groups the on-disk layer PRs into one, since they are the same round trip: join, save, reload. The commit is carried over unchanged; closing here to keep the queue down rather than because anything was wrong with it. |
Summary
SaveSeuratRds()records where each on-disk layer (BPCellsIterableMatrix,DelayedMatrix, ...) lives, andLoadSeuratRds()reopens it from that record. But the record was only ever interpreted from the current working directory, so both ways of saving break as soon as the object is shared or reorganized:relative = TRUEwrites paths relative to the.Rds, so loading from any other working directory finds nothing;relative = FALSE(the default) writes absolute paths, so moving or renaming the folder holding the.Rdsand its stores finds nothing.In both cases the load warns
Cannot find any of the layer files specifiedand returns an object with no layers at all, which is easy to miss.Reproducer
The same happens with
relative = FALSEafter the folder is moved.Fix
Resolve each cached path against the directory holding the
.Rdsbefore checking whether it exists..ResolveLayerPaths()tries, in order:.Rdsdirectory,.Rdsdirectory.Objects that have not moved keep loading through the first branch, so existing behaviour is unchanged. Step 2 fixes
relative = TRUE; step 3 fixes an absolute path whose store travelled alongside the.Rds, which is exactly the layoutSaveSeuratRds(move = TRUE)creates.Tests
New
tests/testthat/test_save_rds.Rcovers all three cases (relative after a move from another working directory, absolute after a move, and an object that has not moved). Againstmainit fails 6 assertions; with this change all 11 pass. Guarded withskip_if_not_installed("BPCells").Notes
5.4.0.9002. Happy to add one in whichever order suits you.SaveSeurat()/LoadSeurat()single-file bundles for the same problem (Enable Seurat to Handle Ultra-Large Matrices (>2^31 values) seurat#9798).🤖 Generated with Claude Code