-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change: Only embed specs/namespaces for types that are included in NWB file on export #615
Open
ehennestad
wants to merge
46
commits into
master
Choose a base branch
from
607-fix-namespace-embedding-in-file
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
a458d62
Only include namespaces for types that are included in NWB file on ex…
ehennestad 13822a8
Merge branch 'master' into 607-fix-namespace-embedding-in-file
ehennestad 35150e9
Add functionality for installing extensions
ehennestad bd40894
Minor fixes
ehennestad 132aa67
Update comment
ehennestad 781f303
Add comment + print message when extension has been installed
ehennestad eab998e
Update installExtension.m
ehennestad be4be3d
Fix changed variable name
ehennestad 650ceec
Update matnwb_createNwbInstallExtension.m
ehennestad 8e18448
Create listNwbTypeHierarchy.m
ehennestad 7c41ca6
Add private method for embedding specifications to file on export
ehennestad a467c46
Fix variable name
ehennestad ff95e98
Add workflow for updating nwbInstallExtension
ehennestad bb3514b
Add option to save extension in custom location
ehennestad 184fa81
Create InstallExtensionTest.m
ehennestad ddbe9dc
Update docstring
ehennestad 44a6a20
Merge branch 'master' into add-nwb-install-extension
ehennestad 24c3899
Merge branch 'add-nwb-install-extension' of https://github.com/Neurod…
ehennestad da00cea
Change dispExtensionInfo to return info instead of displaying + add test
ehennestad 40b7703
Reorganize code into separate functions and add tests
ehennestad eeb2006
Merge branch 'master' into 607-fix-namespace-embedding-in-file
ehennestad e3b4906
Merge branch 'master' into add-nwb-install-extension
ehennestad 6faba21
Minor changes to improve test coverage
ehennestad a74a2d2
add nwbInstallExtension to docs
ehennestad 16877f9
Update update_extension_list.yml
ehennestad 0bc735f
Update downloadExtensionRepository.m
ehennestad 67680c2
Update docstring for nwbInstallExtension
ehennestad b2e679a
Fix docstring indentation in nwbInstallExtension
ehennestad 69f07d9
Add doc pages describing how to use (ndx) extensions
ehennestad 07d5162
Fix typo
ehennestad 13b0d1b
Update +tests/+unit/InstallExtensionTest.m
ehennestad 32342ed
Update docs/source/pages/getting_started/using_extensions/generating_…
ehennestad 81a2259
Merge branch 'master' into add-nwb-install-extension
bendichter b9f8f2c
Add docstrings for functions to retrieve and list extension info
ehennestad 2b0e820
Fix docstring formatting/whitespace
ehennestad bcb2584
Update listExtensions.m
ehennestad 691ef81
Move static test methods into io.internal.h5 namespace
ehennestad 5958136
Update writeEmbeddedSpecifications.m
ehennestad f5af434
Add validateEmbeddedSpecifications
ehennestad 9de778b
Update NwbFile.m
ehennestad 258b8dc
Create listEmbeddedSpecNamespaces.m
ehennestad c694d10
Update nwbExportTest.m
ehennestad 8f0ec28
Update test for spec/namespace embedding
ehennestad 7e9aac6
Merge branch 'add-nwb-install-extension' into 607-fix-namespace-embed…
ehennestad 09ec654
Merge branch 'master' into 607-fix-namespace-embedding-in-file
ehennestad ab79e41
Update read_indexed_column.m
ehennestad File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
function deleteAttribute(fileReference, objectLocation, attributeName) | ||
% deleteAttribute - Delete the specified attribute from an NWB file | ||
|
||
arguments | ||
fileReference {io.internal.h5.mustBeH5FileReference} | ||
objectLocation (1,1) string | ||
attributeName (1,1) string | ||
end | ||
|
||
objectLocation = io.internal.h5.validateLocation(objectLocation); | ||
|
||
% Open the HDF5 file in read-write mode | ||
[fileId, fileCleanupObj] = io.internal.h5.resolveFileReference(fileReference, "w"); %#ok<ASGLU> | ||
|
||
% Open the object (dataset or group) | ||
[objectId, objectCleanupObj] = io.internal.h5.openObject(fileId, objectLocation); %#ok<ASGLU> | ||
|
||
% Delete the attribute | ||
H5A.delete(objectId, attributeName); | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
function deleteGroup(fileReference, groupLocation) | ||
% deleteGroup - Delete the specified group from an NWB file | ||
|
||
arguments | ||
fileReference {io.internal.h5.mustBeH5FileReference} | ||
groupLocation (1,1) string | ||
end | ||
|
||
groupLocation = io.internal.h5.validateLocation(groupLocation); | ||
|
||
% Open the HDF5 file in read-write mode | ||
[fileId, fileCleanupObj] = io.internal.h5.resolveFileReference(fileReference, "w"); %#ok<ASGLU> | ||
|
||
% Delete the group | ||
H5L.delete(fileId, groupLocation, 'H5P_DEFAULT'); | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
function groupNames = listGroupNames(fileReference, h5Location) | ||
|
||
arguments | ||
fileReference {io.internal.h5.mustBeH5FileReference} | ||
h5Location (1,1) string | ||
end | ||
|
||
[fileId, fileCleanupObj] = io.internal.h5.resolveFileReference(fileReference); %#ok<ASGLU> | ||
|
||
% Open the specified location (group) | ||
[groupId, groupCleanupObj] = io.internal.h5.openGroup(fileId, h5Location); %#ok<ASGLU> | ||
|
||
% Use H5L.iterate to iterate over the links | ||
[~, ~, groupNames] = H5L.iterate(... | ||
groupId, "H5_INDEX_NAME", "H5_ITER_INC", 0, @collectGroupNames, {}); | ||
|
||
% Define iteration function | ||
function [status, groupNames] = collectGroupNames(groupId, name, groupNames) | ||
% Only retrieve name of groups | ||
objId = H5O.open(groupId, name, 'H5P_DEFAULT'); | ||
objInfo = H5O.get_info(objId); | ||
if objInfo.type == H5ML.get_constant_value('H5O_TYPE_GROUP') | ||
groupNames{end+1} = name; | ||
end | ||
H5O.close(objId); | ||
status = 0; % Continue iteration | ||
end | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
function mustBeH5File(value) | ||
arguments | ||
value {mustBeFile} | ||
end | ||
|
||
VALID_FILE_ENDING = ["h5", "nwb"]; | ||
validExtensions = "." + VALID_FILE_ENDING; | ||
|
||
hasH5Extension = endsWith(value, validExtensions, 'IgnoreCase', true); | ||
|
||
if ~hasH5Extension | ||
exception = MException(... | ||
'MatNWB:validators:mustBeH5File', ... | ||
'Expected file "%s" to have .h5 or .nwb file extension', value); | ||
throwAsCaller(exception) | ||
end | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
function mustBeH5FileReference(value) | ||
arguments | ||
value {mustBeA(value, ["char", "string", "H5ML.id"])} | ||
end | ||
|
||
if isa(value, "char") || isa(value, "string") | ||
try | ||
io.internal.h5.mustBeH5File(value) | ||
catch ME | ||
throwAsCaller(ME) | ||
end | ||
else | ||
% value is a H5ML.id, ok! | ||
end | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
function [fileId, fileCleanupObj] = openFile(fileName, permission) | ||
% openFile Opens an HDF5 file with the specified permissions and ensures cleanup. | ||
% | ||
% [fileId, fileCleanupObj] = io.internal.h5.openFile(fileName) opens the HDF5 | ||
% file specified by fileName in read-only mode ('r') by default. | ||
% | ||
% [fileId, fileCleanupObj] = io.internal.h5.openFile(fileName, permission) | ||
% opens the HDF5 file specified by fileName with the access mode defined by | ||
% permission. | ||
% | ||
% Input Arguments: | ||
% fileName - A string or character vector specifying the path to the | ||
% HDF5 file. This must be a .h5 or .nwb file. | ||
% | ||
% permission - (Optional) A scalar string specifying the file access mode. | ||
% Valid values are "r" for read-only (default) and "w" for | ||
% read-write. | ||
% | ||
% Output Arguments: | ||
% fileId - The file identifier returned by H5F.open, used to | ||
% reference the open file. | ||
% | ||
% fileCleanupObj - A cleanup object (onCleanup) that ensures the file is | ||
% closed automatically when fileCleanupObj goes out of | ||
% scope. | ||
% | ||
% Example: | ||
% [fid, cleanupObj] = io.internal.h5.openFile("data.h5", "w"); | ||
% % Use fid for file operations. | ||
% % When cleanupObj is cleared or goes out of scope, the file is | ||
% % automatically closed. | ||
|
||
arguments | ||
fileName {io.internal.h5.mustBeH5File} | ||
permission (1,1) string {mustBeMember(permission, ["r", "w"])} = "r" | ||
end | ||
|
||
switch permission | ||
case "r" | ||
accessFlag = 'H5F_ACC_RDONLY'; | ||
case "w" | ||
accessFlag = 'H5F_ACC_RDWR'; | ||
end | ||
fileId = H5F.open(fileName, accessFlag, 'H5P_DEFAULT'); | ||
fileCleanupObj = onCleanup(@(fid) H5F.close(fileId)); | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
function [groupId, groupCleanupObj] = openGroup(fileId, h5Location) | ||
% openGroup Opens an HDF5 group at given location and ensures cleanup. | ||
|
||
arguments | ||
fileId {mustBeA(fileId, "H5ML.id")} | ||
h5Location (1,1) string | ||
end | ||
|
||
% Open the specified location (group) | ||
groupLocation = io.internal.h5.validateLocation(h5Location); | ||
groupId = H5G.open(fileId, groupLocation); | ||
groupCleanupObj = onCleanup(@(gid) H5G.close(groupId)); | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
function [objectId, objectCleanupObj] = openObject(fileId, objectLocation) | ||
% openObject Opens an HDF5 object at given location and ensures cleanup. | ||
|
||
arguments | ||
fileId {mustBeA(fileId, "H5ML.id")} | ||
objectLocation (1,1) string | ||
end | ||
|
||
% Open the object (dataset or group) | ||
objectLocation = io.internal.h5.validateLocation(objectLocation); | ||
objectId = H5O.open(fileId, objectLocation, 'H5P_DEFAULT'); | ||
objectCleanupObj = onCleanup(@(oid) H5O.close(objectId)); | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
function [h5FileId, fileCleanupObj] = resolveFileReference(fileReference, permission) | ||
% resolveFileReference - Resolve a file reference to a H5 File ID. | ||
% | ||
% Utility method to resolve a file reference, which can be either a | ||
% filepath or a file id for a h5 file. | ||
% | ||
% The returned value will always be a file ID. This allows functions that | ||
% does operations on h5 files to receive either a file path or a file id | ||
% | ||
% Note: If the file reference is a file ID for an open file, the permission | ||
% might be different than the provided/requested permission. | ||
|
||
arguments | ||
fileReference {io.internal.h5.mustBeH5FileReference} | ||
permission (1,1) string {mustBeMember(permission, ["r", "w"])} = "r" | ||
end | ||
|
||
if isa(fileReference, "char") || isa(fileReference, "string") | ||
% Need to open the file | ||
if isfile(fileReference) | ||
[h5FileId, fileCleanupObj] = io.internal.h5.openFile(fileReference, permission); | ||
else | ||
error('File "%s" does not exist', fileReference) | ||
end | ||
else | ||
h5FileId = fileReference; | ||
% If the file is already open, we are not responsible for closing it | ||
fileCleanupObj = []; | ||
end | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
function locationName = validateLocation(locationName) | ||
arguments | ||
locationName (1,1) string | ||
end | ||
|
||
if ~startsWith(locationName, "/") | ||
locationName = "/" + locationName; | ||
end | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
function namespaceNames = listEmbeddedSpecNamespaces(fileReference) | ||
|
||
arguments | ||
fileReference {io.internal.h5.mustBeH5FileReference} | ||
end | ||
|
||
[fileId, fileCleanupObj] = io.internal.h5.resolveFileReference(fileReference); %#ok<ASGLU> | ||
|
||
specLocation = io.spec.internal.readEmbeddedSpecLocation(fileId); | ||
namespaceNames = io.internal.h5.listGroupNames(fileId, specLocation); | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
function validateEmbeddedSpecifications(h5_file_id, expectedNamespaceNames) | ||
% validateEmbeddedSpecifications - Validate the embedded specifications | ||
% | ||
% This function does two things: | ||
% 1) Displays a warning if specifications of expected namespaces | ||
% are not embedded in the file. | ||
% E.g if cached namespaces were cleared prior to export. | ||
% | ||
% 2) Deletes specifications for unused namespaces that are embedded. | ||
% - E.g. If neurodata type from an embedded namespace was removed and the | ||
% file was re-exported | ||
|
||
% NB: Input h5_file_id must point to a file opened with write access | ||
|
||
specLocation = io.spec.internal.readEmbeddedSpecLocation(h5_file_id); | ||
embeddedNamespaceNames = io.internal.h5.listGroupNames(h5_file_id, specLocation); | ||
|
||
checkMissingNamespaces(expectedNamespaceNames, embeddedNamespaceNames) | ||
|
||
unusedNamespaces = checkUnusedNamespaces(... | ||
expectedNamespaceNames, embeddedNamespaceNames); | ||
|
||
if ~isempty(unusedNamespaces) | ||
deleteUnusedNamespaces(h5_file_id, unusedNamespaces, specLocation) | ||
end | ||
end | ||
|
||
function checkMissingNamespaces(expectedNamespaceNames, embeddedNamespaceNames) | ||
% checkMissingNamespaces - Check if any namespace specs are missing from the file | ||
missingNamespaces = setdiff(expectedNamespaceNames, embeddedNamespaceNames); | ||
if ~isempty(missingNamespaces) | ||
missingNamespacesStr = strjoin(" " + string(missingNamespaces), newline); | ||
warning('NWB:validators:MissingEmbeddedNamespace', 'Namespace is missing:\n%s', missingNamespacesStr) | ||
end | ||
end | ||
|
||
function unusedNamespaces = checkUnusedNamespaces(expectedNamespaceNames, embeddedNamespaceNames) | ||
% checkUnusedNamespaces - Check if any namespace specs in the file are unused | ||
unusedNamespaces = setdiff(embeddedNamespaceNames, expectedNamespaceNames); | ||
end | ||
|
||
function deleteUnusedNamespaces(fileId, unusedNamespaces, specRootLocation) | ||
for i = 1:numel(unusedNamespaces) | ||
thisName = unusedNamespaces{i}; | ||
namespaceSpecLocation = strjoin( {specRootLocation, thisName}, '/'); | ||
io.internal.h5.deleteGroup(fileId, namespaceSpecLocation) | ||
end | ||
end |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
function parentTypeNames = listNwbTypeHierarchy(nwbTypeName) | ||
% listNwbTypeHierarchy - List the NWB type hierarchy for an NWB type | ||
arguments | ||
nwbTypeName (1,1) string | ||
end | ||
|
||
parentTypeNames = string.empty; % Initialize an empty cell array | ||
currentType = nwbTypeName; % Start with the specific type | ||
|
||
while ~strcmp(currentType, 'types.untyped.MetaClass') | ||
parentTypeNames(end+1) = currentType; %#ok<AGROW> | ||
|
||
% Use MetaClass information to get the parent type | ||
metaClass = meta.class.fromName(currentType); | ||
if isempty(metaClass.SuperclassList) | ||
break; % Reached the base type | ||
end | ||
% NWB parent type should always be the first superclass in the list | ||
currentType = metaClass.SuperclassList(1).Name; | ||
end | ||
end |
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's a big deal that way this is used here, but note that deleting groups and datasets from an HDF5 file does not free up the space