Skip to content
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

Wrap data in set_data_io with a DataChunkIterator to support overriding hdf5 dataset backend configurations #1172

Merged
merged 19 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Enhancements
- Added support to append to a dataset of references for HDMF-Zarr. @mavaylon1 [#1157](https://github.com/hdmf-dev/hdmf/pull/1157)
- Added support for overriding backend configurations of h5py.Dataset objects in Container.set_data_io: [#1172](https://github.com/hdmf-dev/hdmf/pull/1172)

## HDMF 3.14.3 (July 29, 2024)

Expand Down
32 changes: 28 additions & 4 deletions src/hdmf/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import numpy as np
import pandas as pd

from .data_utils import DataIO, append_data, extend_data
from .data_utils import DataIO, append_data, extend_data, AbstractDataChunkIterator, DataChunkIterator
from .utils import docval, get_docval, getargs, ExtenderMeta, get_data_shape, popargs, LabelledDict

from .term_set import TermSet, TermSetWrapper
Expand Down Expand Up @@ -830,7 +830,13 @@ def __smart_str_dict(d, num_indent):
out += '\n' + indent + right_br
return out

def set_data_io(self, dataset_name: str, data_io_class: Type[DataIO], data_io_kwargs: dict = None, **kwargs):
def set_data_io(
self,
dataset_name: str,
data_io_class: Type[DataIO], data_io_kwargs: dict = None,
data_chunk_iterator_class: Type[AbstractDataChunkIterator] = DataChunkIterator,
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
data_chunk_iterator_kwargs: dict = None, **kwargs
):
"""
Apply DataIO object to a dataset field of the Container.

pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -842,6 +848,10 @@ def set_data_io(self, dataset_name: str, data_io_class: Type[DataIO], data_io_kw
Class to use for DataIO, e.g. H5DataIO or ZarrDataIO
data_io_kwargs: dict
keyword arguments passed to the constructor of the DataIO class.
data_chunk_iterator_class: Type[AbstractDataChunkIterator]
Class to use for DataChunkIterator, by default DataChunkIterator
data_chunk_iterator_kwargs: dict
keyword arguments passed to the constructor of the DataChunkIterator class.
**kwargs:
DEPRECATED. Use data_io_kwargs instead.
kwargs are passed to the constructor of the DataIO class.
Expand All @@ -855,8 +865,10 @@ def set_data_io(self, dataset_name: str, data_io_class: Type[DataIO], data_io_kw
)
data_io_kwargs = kwargs
data = self.fields.get(dataset_name)
data_chunk_iterator_kwargs = data_chunk_iterator_kwargs or dict()
if data is None:
raise ValueError(f"{dataset_name} is None and cannot be wrapped in a DataIO class")
data = data_chunk_iterator_class(data=data, **data_chunk_iterator_kwargs)
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
self.fields[dataset_name] = data_io_class(data=data, **data_io_kwargs)


Expand Down Expand Up @@ -900,7 +912,13 @@ def set_dataio(self, **kwargs):
dataio.data = self.__data
self.__data = dataio

def set_data_io(self, data_io_class: Type[DataIO], data_io_kwargs: dict) -> None:
def set_data_io(
self,
data_io_class: Type[DataIO],
data_io_kwargs: dict,
data_chunk_iterator_class: Type[AbstractDataChunkIterator] = DataChunkIterator,
data_chunk_iterator_kwargs: dict = None,
) -> None:
"""
Apply DataIO object to the data held by this Data object.

Expand All @@ -910,8 +928,14 @@ def set_data_io(self, data_io_class: Type[DataIO], data_io_kwargs: dict) -> None
The DataIO to apply to the data held by this Data.
data_io_kwargs: dict
The keyword arguments to pass to the DataIO.
data_chunk_iterator_class: Type[AbstractDataChunkIterator]
The DataChunkIterator to use for the DataIO, by default DataChunkIterator.
data_chunk_iterator_kwargs: dict
The keyword arguments to pass to the DataChunkIterator.
"""
self.__data = data_io_class(data=self.__data, **data_io_kwargs)
data_chunk_iterator_kwargs = data_chunk_iterator_kwargs or dict()
data = data_chunk_iterator_class(data=self.__data, **data_chunk_iterator_kwargs)
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
self.__data = data_io_class(data=data, **data_io_kwargs)

@docval({'name': 'func', 'type': types.FunctionType, 'doc': 'a function to transform *data*'})
def transform(self, **kwargs):
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/test_io_hdf5_h5tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3712,6 +3712,29 @@ def test_set_data_io_old_api(self):
self.assertIsInstance(self.obj.data1, H5DataIO)
self.assertTrue(self.obj.data1.io_settings["chunks"])

def test_set_data_io_h5py_dataset(self):
file_path = get_temp_filepath()
file = File(file_path, 'w')
data = file.create_dataset('data', data=[1, 2, 3, 4, 5], chunks=(3,))
class ContainerWithData(Container):
__fields__ = ('data',)

@docval(
{"name": "name", "doc": "name", "type": str},
{'name': 'data', 'doc': 'field1 doc', 'type': h5py.Dataset},
)
def __init__(self, **kwargs):
super().__init__(name=kwargs["name"])
self.data = kwargs["data"]

container = ContainerWithData("name", data)
container.set_data_io("data", H5DataIO, data_io_kwargs=dict(chunks=(2,)))

self.assertIsInstance(container.data, H5DataIO)
self.assertEqual(container.data.io_settings["chunks"], (2,))

os.remove(file_path)


class TestDataSetDataIO(TestCase):

Expand Down