[codex] Improve dynamic table validation and colnames sync#818
[codex] Improve dynamic table validation and colnames sync#818ehennestad merged 10 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR tightens DynamicTable invariants around colnames by adding normalization/validation, syncing colnames when schema-defined column properties are assigned, and re-checking table configuration during export to catch drift.
Changes:
- Added
validateColnames(uniqueness + normalization) andsyncNamedColumnutilities undertypes.util.dynamictable. - Updated
checkConfigto validatecolnamesand detect materialized columns missing fromcolnames. - Updated class generation to inject validator/setter hooks (and added/updated tests) so direct assignments keep
colnamesconsistent and export re-validates constraints.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| +types/+util/+dynamictable/validateColnames.m | New helper to normalize and enforce uniqueness of colnames. |
| +types/+util/+dynamictable/syncNamedColumn.m | New helper to ensure schema-defined columns are registered in colnames on assignment. |
| +types/+util/+dynamictable/checkConfig.m | Extends DynamicTable config validation (now checks for missing detected columns and normalizes colnames). |
| +types/+hdmf_common/DynamicTable.m | Uses validateColnames in validate_colnames and adds checkCustomConstraint to re-run checkConfig (e.g., on export). |
| +types/+core/Units.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/TimeIntervals.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/SweepTable.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/SimultaneousRecordingsTable.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/SequentialRecordingsTable.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/RepetitionsTable.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/PlaneSegmentation.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/IntracellularStimuliTable.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/IntracellularResponsesTable.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/IntracellularElectrodesTable.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/FrequencyBandsTable.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/ExperimentalConditionsTable.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +types/+core/ElectrodesTable.m | Adds post-set hooks to sync schema-defined columns into colnames on direct assignment. |
| +tests/+unit/dynamicTableTest.m | Updates/extends tests for duplicate colnames, direct-assignment syncing, and export-time drift detection. |
| +file/isDynamicTableDescendant.m | New generator helper to detect DynamicTable inheritance. |
| +file/getPropertyHooks.m | New generator hook registry for custom validator/setter behavior. |
| +file/fillValidators.m | Injects additional validator lines via generator hooks. |
| +file/fillSetters.m | Adds optional post-set hook generation via generator hooks. |
| +file/fillCustomConstraint.m | Adds custom constraint generation for DynamicTable to re-check config. |
| +file/fillConstructor.m | Uses new file.isDynamicTableDescendant helper when injecting DynamicTable validation into constructors. |
| +file/fillClass.m | Passes type/namespace into fillSetters for hook-aware setter generation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| colnames = cleanColumnNames(colnames); | ||
| if ischar(colnames) | ||
| colnames = {colnames}; | ||
| end | ||
|
|
||
| assert(iscellstr(colnames), ... | ||
| 'NWB:DynamicTable:InvalidColumnNames', ... | ||
| 'Column names must be a cell array of character vectors.'); | ||
|
|
There was a problem hiding this comment.
validate_colnames accepts MATLAB string arrays because types.util.checkDtype(...,'char',val) allows isstring(val), but validateColnames later asserts iscellstr(colnames)/ischar(colnames) and will therefore reject string input. Consider normalizing string (and cell arrays of strings) to cellstr early (e.g., colnames = cellstr(colnames) when isstring(colnames)), so the validator is consistent with checkDtype and common MATLAB calling patterns.
There was a problem hiding this comment.
@copilot apply changes based on this feedback
| if ischar(colnames) | ||
| colnames = {colnames}; | ||
| end | ||
|
|
||
| assert(iscellstr(colnames), ... | ||
| 'NWB:DynamicTable:InvalidColumnNames', ... | ||
| 'Column names must be a cell array of character vectors.'); |
There was a problem hiding this comment.
validateColnames will also fail for an N-by-M char array (multiple column names stored as padded rows), because it wraps any char input into a 1-element cell and then requires iscellstr. If colnames can be a char matrix (e.g., from some HDF5 string array reads or user input), consider converting multi-row char arrays via cellstr(colnames) before validating uniqueness.
There was a problem hiding this comment.
I don't think char matrix should be supported
| || isa(value, 'types.core.VectorData'); | ||
| isVectorIndex = isa(value, 'types.hdmf_common.VectorIndex') ... | ||
| || isa(value, 'types.core.VectorIndex'); | ||
| tf = ~isempty(value) && isVectorData && ~isVectorIndex; |
There was a problem hiding this comment.
isMaterializedColumn currently treats any non-empty VectorData object as “materialized”, regardless of whether its .data is empty / zero-length (the previous implementation in getDetectedColumnNames explicitly skipped empty Vector.data for DataStub, DataPipe, and in-memory arrays). If the intent is to validate only columns with actual row data, reintroduce the data-emptiness check (or rename the helper/message to reflect the new semantics).
| tf = ~isempty(value) && isVectorData && ~isVectorIndex; | |
| hasData = false; | |
| if ~isempty(value) && isVectorData && ~isVectorIndex | |
| hasData = ~isempty(value.data); | |
| end | |
| tf = ~isempty(value) && isVectorData && ~isVectorIndex && hasData; |
There was a problem hiding this comment.
I think the current implementation is correct. Presence of VectorData should be treated as a materialised column. If data is empty, an error should be thrown later in checkConfig if other columns in the same table has a non-zero length. If the property is dropped here because data is empty, the column height check would not flag this empty column as a problem later.
Agent-Logs-Url: https://github.com/NeurodataWithoutBorders/matnwb/sessions/f5f16c03-9489-4eee-8c7f-127005b690ce Co-authored-by: ehennestad <17237719+ehennestad@users.noreply.github.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #818 +/- ##
==========================================
- Coverage 95.35% 95.25% -0.10%
==========================================
Files 205 209 +4
Lines 7430 7508 +78
==========================================
+ Hits 7085 7152 +67
- Misses 345 356 +11 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Motivation
This PR fixes gaps in DynamicTable validation and synchronization around
colnames.Changes:
colnamesproperty contains unique elementscolnamescolnamesin sync.DynamicTableconfiguration duringnwbExportto ensure dynamic tables are still valid when writing.How to test the behavior?
Example 1: Adding duplicate colnames
Before:
mainaccepted duplicatecolnames.After:
The feature branch rejects duplicate
colnames.Example 2: Adding wrong colname
Before:
mainallowed a materialized column to be missing fromcolnames.After:
The feature branch detects the mismatch immediately.
Example 3: Adding column w/o colnames automatically updates colnames
Before:
On
main, direct assignment does not synccolnames.After:
On the feature branch, direct assignment updates
colnamesautomatically.Example 4: nwbExport fails for invalid table
Before:
mainallowed export to proceed even aftercolnamesdrifted out of sync.After:
The feature branch re-validates DynamicTable configuration during export.
Checklist
fix #XXwhereXXis the issue number?🤖 Generated with Codex