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__