diff --git a/src/hdmf/container.py b/src/hdmf/container.py index b67d10bf7..2b2168e0f 100644 --- a/src/hdmf/container.py +++ b/src/hdmf/container.py @@ -723,9 +723,12 @@ def _generate_field_html(self, key, value, level, access_code): return f'
{key}: {value}
' - # Basic array attributes + # Detects array-like objects that conform to the Array Interface specification + # (e.g., NumPy arrays, HDF5 datasets, DataIO objects). Objects must have both + # 'shape' and 'dtype' attributes. Iterators are excluded as they lack 'shape'. + # This approach keeps the implementation generic without coupling to specific backends methods is_array_data = hasattr(value, "shape") and hasattr(value, "dtype") - + if is_array_data: html_content = self._generate_array_html(value, level + 1) elif hasattr(value, "generate_html_repr"): @@ -751,7 +754,7 @@ def _generate_field_html(self, key, value, level, access_code): def _generate_array_html(self, array, level): - """Generates HTML for array data""" + """Generates HTML for array data (e.g., NumPy arrays, HDF5 datasets, Zarr datasets and DataIO objects).""" is_numpy_array = isinstance(array, np.ndarray) it_was_read_with_io = self.get_read_io() is not None @@ -763,8 +766,12 @@ def _generate_array_html(self, array, level): elif is_data_io: array_info_dict = get_basic_array_info(array.data) repr_html = generate_array_html_repr(array_info_dict, array.data, "DataIO") - elif it_was_read_with_io: # The backend handles the representation - read_io = self.get_read_io() # Note that sometimes numpy array have a read_io attribute + elif it_was_read_with_io: + # The backend handles the representation here. Two special cases worth noting: + # 1. Array-type attributes (e.g., start_frame in ImageSeries) remain NumPy arrays + # even when their parent container has an IO + # 2. Data may have been modified after being read from storage + read_io = self.get_read_io() repr_html = read_io.generate_dataset_html(array) else: # Not sure which object could get here object_class = array.__class__.__name__