Skip to content

Commit 1ad72a7

Browse files
committed
Merge branch 'dev' into release/5.0.0
2 parents ad69900 + d3df1fc commit 1ad72a7

12 files changed

Lines changed: 143 additions & 33 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,22 @@
1212
- Deprecated `BaseStorageSpec.add_attribute`, `GroupSpec.add_group`, `GroupSpec.add_dataset`, and `GroupSpec.add_link`. Use `set_attribute`, `set_group`, `set_dataset`, and `set_link` instead. @rly [#1333](https://github.com/hdmf-dev/hdmf/pull/1333)
1313
- Deprecated unused `BaseStorageSpec.get_data_type_spec` and `BaseStorageSpec.get_namespace_spec`. @rly [#1333](https://github.com/hdmf-dev/hdmf/pull/1333)
1414

15-
### Added
16-
- Added support for HDMF Common Schema 1.9.0.
17-
- Introduced a new data type `MeaningsTable` and changes to `DynamicTable` to support included `MeaningsTable` objects. @rly [#1376](https://github.com/hdmf-dev/hdmf/pull/1376)
18-
- Warning when `data_type_def` and `data_type_inc` are the same in a spec. @rly [#1312](https://github.com/hdmf-dev/hdmf/pull/1312)
19-
- Added abstract methods `HDMFIO.load_namespaces` and `HDMFIO.load_namespaces_io`. @rly [#1299](https://github.com/hdmf-dev/hdmf/pull/1299)
20-
2115
### Removed
2216
- Removed unused and undocumented `hdmf.monitor` module. @rly [#1327](https://github.com/hdmf-dev/hdmf/pull/1327)
2317
- Removed deprecated `Data.set_data_io` usage and `HERDManager` methods. @rly [#1328](https://github.com/hdmf-dev/hdmf/pull/1328)
2418
- Removed deprecated `HDF5IO.copy_file` method. Use the `HDF5IO.export` or the `h5py.File.copy` method instead. @stephprince [#1332](https://github.com/hdmf-dev/hdmf/pull/1332)
2519
- Removed deprecated `extensions` kwarg for `get_type_map` function. @stephprince [#1332](https://github.com/hdmf-dev/hdmf/pull/1332)
2620

21+
### Added
22+
- Added support for HDMF Common Schema 1.9.0.
23+
- Introduced a new data type `MeaningsTable` and changes to `DynamicTable` to support included `MeaningsTable` objects. @rly [#1376](https://github.com/hdmf-dev/hdmf/pull/1376)
24+
- Promoted `HERD` from the hdmf-experimental namespace to the HDMF Common namespace. @rly [#1387](https://github.com/hdmf-dev/hdmf/pull/1387)
25+
- Added warning when `data_type_def` and `data_type_inc` are the same in a spec. @rly [#1312](https://github.com/hdmf-dev/hdmf/pull/1312)
26+
- Added abstract methods `HDMFIO.load_namespaces` and `HDMFIO.load_namespaces_io`. @rly [#1299](https://github.com/hdmf-dev/hdmf/pull/1299)
27+
28+
### Fixed
29+
- Fixed a broken test and refactored `VectorIndex.get`. @rly, @mavaylon1 [#1293](https://github.com/hdmf-dev/hdmf/pull/1293)
30+
2731

2832
## HDMF 4.3.1 (January 28, 2026)
2933

docs/gallery/plot_external_resources.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
==============================================
44
55
This is a user guide to interacting with the
6-
:py:class:`~hdmf.common.resources.HERD` class. The HERD type
7-
is experimental and is subject to change in future releases. If you use this type,
8-
please provide feedback to the HDMF team so that we can improve the structure and
9-
access of data stored with this type for your use cases.
6+
:py:class:`~hdmf.common.resources.HERD` class.
107
118
Introduction
129
-------------
@@ -96,9 +93,6 @@
9693
from hdmf import Data
9794
import numpy as np
9895
import os
99-
# Ignore experimental feature warnings in the tutorial to improve rendering
100-
import warnings
101-
warnings.filterwarnings("ignore", category=UserWarning, message="HERD is experimental*")
10296

10397
try:
10498
import linkml_runtime # noqa: F401

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,9 @@ line-length = 120
174174
[tool.ruff.lint.per-file-ignores]
175175
"docs/gallery/*" = ["E402", "T201"]
176176
"src/*/__init__.py" = ["F401"]
177+
"src/hdmf/testing/make_test_files.py" = ["T201"]
177178
"test_gallery.py" = ["T201"]
178179

180+
179181
[tool.ruff.lint.mccabe]
180182
max-complexity = 17

src/hdmf/common/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,6 @@ def get_hdf5io(**kwargs):
272272
MeaningsTable = get_class('MeaningsTable', CORE_NAMESPACE)
273273
EnumData = get_class('EnumData', EXP_NAMESPACE)
274274
CSRMatrix = get_class('CSRMatrix', CORE_NAMESPACE)
275-
HERD = get_class('HERD', EXP_NAMESPACE)
275+
HERD = get_class('HERD', CORE_NAMESPACE)
276276
SimpleMultiContainer = get_class('SimpleMultiContainer', CORE_NAMESPACE)
277277
AlignedDynamicTable = get_class('AlignedDynamicTable', CORE_NAMESPACE)

src/hdmf/common/resources.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pandas as pd
22
import numpy as np
3-
from . import register_class, EXP_NAMESPACE
3+
from . import register_class
44
from . import get_type_map
55
from ..container import Table, Row, Container, Data, AbstractContainer, HERDManager
66
from ..term_set import TermSet
@@ -157,7 +157,7 @@ class ObjectKey(Row):
157157
__table__ = ObjectKeyTable
158158

159159

160-
@register_class('HERD', EXP_NAMESPACE)
160+
@register_class('HERD')
161161
class HERD(Container):
162162
"""
163163
HDMF External Resources Data Structure.

src/hdmf/common/table.py

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -233,21 +233,8 @@ def get(self, arg, **kwargs):
233233
indices = arg
234234
ret = list()
235235
if len(indices) > 0:
236-
# Note: len(indices) == 0 for test_to_hierarchical_dataframe_empty_tables.
237-
# This is an edge case test for to_hierarchical_dataframe() on empty tables.
238-
# When len(indices) == 0, ret is expected to be an empty list, defined above.
239-
try:
240-
data = self.target.get(slice(None), **kwargs)
241-
except IndexError:
242-
"""
243-
Note: TODO: test_to_hierarchical_dataframe_indexed_dtr_on_last_level.
244-
This is the old way to get the data and not an untested feature.
245-
"""
246-
for i in indices:
247-
ret.append(self.__getitem_helper(i, **kwargs))
248-
249-
return ret
250-
236+
# Load the entire target table at once to avoid multiple I/O calls
237+
data = self.target.get(slice(None), **kwargs)
251238
slices = [self.__get_slice(i) for i in indices]
252239
if isinstance(data, pd.DataFrame):
253240
ret = [data.iloc[s] for s in slices]
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Script to generate test files for backward compatibility testing."""
2+
import os
3+
from pathlib import Path
4+
5+
from hdmf import Data, HERDManager, __version__
6+
from hdmf.common import SimpleMultiContainer, get_hdf5io
7+
from hdmf.common.resources import HERD
8+
9+
10+
class _HERDManagerContainer(SimpleMultiContainer, HERDManager):
11+
"""A SimpleMultiContainer that also implements HERDManager, for testing."""
12+
13+
pass
14+
15+
16+
def make_herd_1_8_0_file(outdir):
17+
"""Generate an HDF5 file using hdmf-common-schema 1.8.0.
18+
19+
In schema 1.8.0, HERD was defined in the hdmf-experimental namespace.
20+
This creates test files to verify backward compatibility when reading
21+
files created with the old namespace.
22+
23+
Before running this, pip install hdmf==4.3.1 which includes hdmf-common-schema 1.8.0.
24+
25+
Parameters
26+
----------
27+
outdir : pathlib.Path
28+
Directory where the test files will be written.
29+
"""
30+
species = Data(name="species", data=["Homo sapiens", "Mus musculus"])
31+
container = _HERDManagerContainer(name="root", containers=[species])
32+
33+
er = HERD()
34+
er.add_ref(
35+
file=container,
36+
container=species,
37+
key="Homo sapiens",
38+
entity_id="NCBI_TAXON:9606",
39+
entity_uri="https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?mode=Info&id=9606",
40+
)
41+
42+
h5_path = outdir / "herd_1_8_0.h5"
43+
with get_hdf5io(path=str(h5_path), mode="w") as io:
44+
io.write(container)
45+
46+
old_cwd = os.getcwd()
47+
os.chdir(str(outdir))
48+
er.to_zip(path="herd_1_8_0.zip")
49+
os.chdir(old_cwd)
50+
51+
print(f"Created {h5_path}")
52+
53+
54+
if __name__ == "__main__":
55+
# Install these versions of HDMF and run this script to generate new files:
56+
# python src/hdmf/testing/make_test_files.py
57+
# Files will be made in tests/back_compat/
58+
59+
if __version__ == '4.3.1':
60+
outdir = Path(__file__).resolve().parent.parent.parent.parent / "tests" / "unit" / "back_compat_tests"
61+
make_herd_1_8_0_file(outdir)
25.4 KB
Binary file not shown.
948 Bytes
Binary file not shown.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Test reading HERD from files created with hdmf-common-schema 1.8.0.
2+
3+
In schema 1.8.0, HERD was defined in the hdmf-experimental namespace.
4+
These tests verify that HERD is read correctly and is resolved to the
5+
hdmf-common namespace when using the current schema.
6+
"""
7+
8+
from hdmf.common import get_hdf5io, get_type_map, CORE_NAMESPACE, SimpleMultiContainer
9+
from hdmf.common.resources import HERD
10+
from hdmf.testing import TestCase
11+
12+
13+
class TestHERD_1_8_0(TestCase):
14+
"""Test reading HERD from files written with hdmf-common-schema 1.8.0.
15+
16+
The test files were generated by make_test_files.py using hdmf 4.3.1
17+
(hdmf-common-schema 1.8.0) where HERD was in the hdmf-experimental
18+
namespace. The HDF5 file contains a SimpleMultiContainer with a Data
19+
object, and the ZIP file contains the HERD tables.
20+
"""
21+
22+
def setUp(self):
23+
self.path_h5 = "tests/unit/back_compat_tests/herd_1_8_0.h5"
24+
self.path_zip = "tests/unit/back_compat_tests/herd_1_8_0.zip"
25+
26+
def test_read_container(self):
27+
"""Test that the SimpleMultiContainer with data is read correctly."""
28+
with get_hdf5io(path=self.path_h5, mode="r") as io:
29+
container = io.read()
30+
self.assertIsInstance(container, SimpleMultiContainer)
31+
self.assertIn("species", container.containers)
32+
33+
def test_read_herd_from_zip(self):
34+
"""Test that HERD written under hdmf-experimental is read correctly from ZIP."""
35+
herd = HERD.from_zip(path=self.path_zip)
36+
self.assertIsInstance(herd, HERD)
37+
self.assertEqual(len(herd.keys.data), 1)
38+
self.assertEqual(herd.keys.data[0][0], "Homo sapiens")
39+
self.assertEqual(len(herd.entities.data), 1)
40+
self.assertEqual(herd.entities.data[0][0], "NCBI_TAXON:9606")
41+
42+
def test_read_herd_with_hdf5io(self):
43+
"""Test that HERD is loaded when reading HDF5 with herd_path."""
44+
with get_hdf5io(path=self.path_h5, mode="r", herd_path=self.path_zip) as io:
45+
io.read()
46+
self.assertIsInstance(io.herd, HERD)
47+
48+
def test_herd_namespace_is_common(self):
49+
"""Test that HERD is resolved in the hdmf-common namespace."""
50+
tm = get_type_map()
51+
dt = tm.namespace_catalog.get_spec(CORE_NAMESPACE, "HERD")
52+
self.assertIsNotNone(dt)

0 commit comments

Comments
 (0)