Repository navigation
Enumerate a document's binaries the way NDI-matlab does - #308
Merged
Merged
Conversation
file_uploads_for_document was reading files.file_info[].locations[]. location off the doc dict -- the authoring record, which DID's ingest invalidates. add_file_series writes the manifest to a tempfile and add_file with the default delete_original=True removes that tempfile once the bytes are stored under <FileDir>/<manifest_uid>. The upload then had nothing to send for the manifest, and every series' MEMBERS (which have no file_info entry at all) were silently missed -- run 227 on TEST_USER_1 caught the manifest half (#306); the member half was undetected until this reshape. NDI-matlab's +ndi/+database/+internal/list_binary_files.m has none of this trouble because it asks ndi.dataset.database_existbinarydoc for each name, which is DID's exist_doc -- the storage-side source of truth -- and iterates series_info(k).count from 1..count to generate member names (NAME_1, NAME_2, ...). Port the same shape: file_uploads_for_document takes an optional dataset kwarg; when given it walks file_info[i].name (yielding the manifest name for a series) plus series_info[k].count for members, and asks the dataset for each name's storage-side path. uid is the basename of that path -- DID keys files by uid at <FileDir>/<uid>, so fileparts is enough. Thread the dataset through uploadFilesForDatasetDocuments, through sync.operations._upload_binaries (all three callers: uploadNew, mirrorToRemote, twoWaySync), and through orchestration.uploadDataset. A caller that hands only a raw dict falls back to the recorded-location shape -- kept for isolated tests and for callers with no dataset. Retires PR #307's additional_roots patch, which was at the wrong layer and only recovered the manifest. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdGZ8fRPeY9JSpUHgXm9ao
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.
Closes #306. Replaces the closed #307, which patched around the symptom at the wrong layer and never covered series MEMBERS.
The bug, in two parts
Manifest.
did.document.Document.add_file_serieswrites the series manifest to atempfile.NamedTemporaryFile(prefix="did_manifest_"), then callsadd_file(name, manifest_path).add_file's default isdelete_original=True, so DID copies the manifest into<FileDir>/<manifest_uid>and removes the source. The doc's authoring record (files.file_info[].locations[].location) still names the tempfile, which is gone.file_uploads_for_documentread only that record, so nothing was uploaded for the manifest -- the failure mode run 227 on TEST_USER_1 hit ("Manifest for file series (uid ...) is not a file of this dataset").Members. A series' MEMBERS have no
file_infoentry at all. They're namedNAME_ifori in 1..series_info[k].countand their bytes live at<FileDir>/<member_uid>. A caller that only walksfile_infonever sees them, so an upload sent zero member files regardless of what happened to the manifest. This half was undetected until this reshape.The fix
NDI-matlab's
+ndi/+database/+internal/list_binary_files.mhas never had this trouble because it goes through the storage-side source of truth:database_existbinarydocis DID'sexist_doc, which returns the storage-side path (<FileDir>/<uid>) whether the caller ingested seconds ago or the doc was loaded from disk. It also resolves series members through the manifest without needing them infile_info.Ported the same shape:
file_uploads_for_document(doc, *, dataset=None)takes an optional dataset. When given, it walksfile_info[i].name(which yields the manifest name for a series document) and iteratesseries_info[k].countfor members, askingdataset.database_existbinarydoc(doc_id, name)for each. The uid is the basename of the returned path -- DID keys files by uid, sofileparts(full_file_path)is enough (same as MATLAB).uploadFilesForDatasetDocuments, throughsync.operations._upload_binaries(all three callers --uploadNew,mirrorToRemote,twoWaySync), and throughorchestration.uploadDataset.files.file_info[].locations[].location, skip anything that isn't on disk. Kept for isolated tests and legacy callers.Tests
tests/test_cloud_upload_enumerates_via_dataset.pycovers:database_existbinarydoc(not by reading the doc's stalelocation).series_info[k].count, one-based, in slot order.uid = basename(path), matching MATLAB.file_infoandseries_infostill work.file_uid/file_pathshape still returned, and an object that isn't a real dataset (nodatabase_existbinarydoc) falls back rather than raising.database_addit, prove the recorded manifest location is gone (that IS the shape the test targets), and check thatfile_uploads_for_document(doc, dataset=local)returns manifest + all 4 members withuid == basename(path)and each path pointing at a real file under DID's FileDir.Live-cloud round-trip coverage (
tests/test_cloud_file_series_round_trip.py) is untouched -- it's what will pass on TEST_USER_1 with this fix applied.Why not fix DID?
Considered. The recorded
locationinfile_info[].locations[]is the doc's authoring record, and DID keeps its own storage-side record separately (file_locationstable'scached_locationcolumn) precisely so both remain readable. Rewriting the authoring record on ingest would break other consumers that read the doc as originally-authored.add_file_serieswriting to a persistent path instead of a tempfile is possible, but it just moves the same "recorded path can go stale" problem elsewhere -- any user callingadd_file(delete_original=True)on their own file hits it too. The right answer is the one NDI-matlab already has: on the upload side, ask the storage-side API where the bytes actually live.Files
src/ndi/cloud/upload.py-- reshape.src/ndi/cloud/sync/operations.py-- threaddatasetthrough_upload_binaries+ three callers.src/ndi/cloud/orchestration.py-- passdatasetthroughuploadDataset.src/ndi/cloud/ndi_matlab_python_bridge.yaml-- decision log for the new kwarg.tests/test_cloud_upload_enumerates_via_dataset.py-- new offline test.Bridge check passes (128 tests). Sync/upload/cloud tests pass (570 non-live). The 7 pre-existing failures in
test_cloud_profile_secret_field_names.pyreproduce onmaintoo -- unrelated sandbox keyring issue.🤖 Generated with Claude Code
https://claude.ai/code/session_01AdGZ8fRPeY9JSpUHgXm9ao
Generated by Claude Code