Repository navigation
fix(types): allow one object in several unnamed groups - #918
Open
ehennestad wants to merge 2 commits into
Open
ehennestad wants to merge 2 commits into
ehennestad wants to merge 2 commits into
Conversation
2 of 3 tasks
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #918 +/- ##
=======================================
Coverage 95.46% 95.47%
=======================================
Files 240 240
Lines 8869 8901 +32
=======================================
+ Hits 8467 8498 +31
- Misses 402 403 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
|
Let's take a look into this extension. It appears they wanted to specialize OpticalChannel into OpticalChannelPlus. Did they intend to keep OpticalChannel? |
HasUnnamedGroups exposes each entry of a type's unnamed groups as a property with the entry's name, and raised DuplicateEntry whenever a name appeared in more than one group. An object whose type matches more than one group is placed in each of them: an OpticalChannelPlus in an ndx-multichannel-volume ImagingVolume fills both the inherited optical channel group and the extension's own group, as it does in PyNWB. Creating such an object, and so reading any file with this layout, failed. A name may now appear in several groups when every occurrence is the same object. It has one location in the file and is exported once, so it gets one property. Different objects with the same name are still rejected, because both would be written to the same location. The property reads from any group that holds the name, assigning to it updates every group that accepts the value, remove() removes the name from all groups, and the property is deleted only when no group holds the name. Before, rejecting a duplicate also deleted the property of the entry that was already there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- numEntries counts the unique entry names across all unnamed groups instead of summing each Set's Count, so an object held by two groups under one name is one entry. The count appears only in the display header of container-like types. ImagingPlaneWithSubtypeChannels is not container-like, and no public method reaches numEntries, so no test covers the shared case. HasUnnamedGroupsTest/testObjectDisplay still goes through the new code for a ProcessingModule. - The class help says that each group exports a shared object to the same location, so the file holds one copy. It no longer says that the object is written once. - The DuplicateEntry message ends with "Use a different name." add() rejects any existing name, so "add the same object" was not reachable through add(). - Add a test that assigning a value no group accepts raises NWB:Set:FailedValidation and leaves every group unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ehennestad
force-pushed
the
allow-shared-entry-in-unnamed-groups
branch
from
October 6, 2026 18:14
20431af to
67628bc
Compare
This branch has not been deployed
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.
Motivation
Fixes a bug where
nwbReadfails on a file in which one object belongs to two groups of its parent, as in the DANDI:000541 files, which could not be read since v2.11.0.Background — Since v2.11.0 (#705), every entry in a type's unnamed groups is also available as a property with the entry's name, for example
module.MyDatafor an entry of aProcessingModule. A name must therefore identify one object.Problem — Reading a file in which one object belongs to two groups fails. This happens with the ndx-multichannel-volume extension, for example in DANDI:000541. Its
ImagingVolumeextendsImagingPlane: it inherits the requiredopticalchannelgroup (typeOpticalChannel) and adds anopticalchannelplusgroup (typeOpticalChannelPlus, a subtype ofOpticalChannel). The file's one channel,GFP-GCaMP, is anOpticalChannelPlus, so it belongs in both groups; PyNWB also places it in both. MatNWB rejects any name that appears in more than one group, so creating theImagingVolumefails andnwbReadstops.Solution — A name may appear in several groups when every occurrence is the same object. That object has one location in the file and is written once. Different objects with the same name are still rejected, because both would be written to the same location in the file.
Related: #567 reports slow reads of the DANDI:000541 files. Since v2.11.0 they could not be read at all.
What changed
matnwb.mixin.HasUnnamedGroups:removeremoves a name from every group that holds it.Implementation notes
createDynamicPropertyaccepts a name found in several groups whenisSameObjectInAllGroupsis true (handle identity, not equality), and creates the property only once.onSetEntryRemoveddeletes the property only when no group holds the name. Before, removing the rejected entry from the new group also deleted the property created for the existing entry.sharedEntrySchemareproduces the extension's layout: anImagingPlanesubtype that adds a group for anOpticalChannelsubtype.Examples
Reading a DANDI:000541 file
This reads one file from DANDI:000541 (
sub-20190924-01/sub-20190924-01_ses-20190924_ophys.nwb, 1.46 GB) and prints which groups of theImagingVolumehold the optical channel.Before — The read stops while creating the
ImagingVolume, becauseGFP-GCaMPis in two groups.After — The file is read. Both groups hold the channel, and both hold the same object.
How to test
Run the example above with the file from DANDI:000541. Without the download, the same layout is covered by a test schema, including an export and read that writes the shared object once:
runtests("tests.unit.schema.SharedEntryTest")Checklist
fix #XXwhereXXis the issue number?🤖 Generated with Claude Code