Skip to content

Commit 895a284

Browse files
rlyclaude
andauthored
Add HERD repr/_repr_html_ surfacing references as a flattened table (#1510)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent abd0a7f commit 895a284

3 files changed

Lines changed: 66 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## HDMF 6.0.3 (Upcoming)
44

55
### Enhancements
6+
- Added a `HERD`-specific `__repr__` and `_repr_html_` that surface the references as a flattened table, so a `HERD` (especially one read back from a file) no longer appears empty in its default display. @rly [#1510](https://github.com/hdmf-dev/hdmf/pull/1510)
67
- `HERD.add_ref` now defaults `key` to the value of a scalar string `attribute` when `key` is not provided, removing the redundant argument in the common case. @rly [#1511](https://github.com/hdmf-dev/hdmf/pull/1511)
78

89
### Fixed

src/hdmf/common/resources.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,50 @@ def to_dataframe(self, **kwargs):
987987
# return the result
988988
return result_df
989989

990+
def __flattened_dataframe_or_none(self):
991+
"""Return the flattened ``to_dataframe()`` view, or None when there are no references.
992+
993+
``to_dataframe`` raises when the HERD holds no object-key relationships and may fail if the
994+
backing file is closed. The repr methods use this helper so they never raise on display.
995+
"""
996+
if len(self.object_keys) == 0:
997+
return None
998+
try:
999+
return self.to_dataframe()
1000+
except Exception:
1001+
return None
1002+
1003+
def __summary_line(self):
1004+
"""Return a one-line summary of the table sizes."""
1005+
return ("%d key(s), %d entity(ies), %d object(s), %d file(s)"
1006+
% (len(self.keys), len(self.entities), len(self.objects), len(self.files)))
1007+
1008+
def __repr__(self):
1009+
cls = self.__class__
1010+
template = "%s %s.%s at 0x%d" % (self.name, cls.__module__, cls.__name__, id(self))
1011+
template += "\n " + self.__summary_line()
1012+
df = self.__flattened_dataframe_or_none()
1013+
if df is not None and len(df) > 0:
1014+
template += "\n" + repr(df)
1015+
return template
1016+
1017+
def _repr_html_(self):
1018+
"""Generate an HTML representation that surfaces the references as a flattened table."""
1019+
header_text = self.name if self.name == self.__class__.__name__ else \
1020+
f"{self.name} ({self.__class__.__name__})"
1021+
html_repr = self.css_style + self.js_script
1022+
html_repr += "<div class='container-wrap'>"
1023+
html_repr += f"<div class='container-header'><div class='xr-obj-type'><h3>{header_text}</h3></div></div>"
1024+
html_repr += self._closed_file_warning_html()
1025+
html_repr += f"<p class='container-fields'>{self.__summary_line()}</p>"
1026+
df = self.__flattened_dataframe_or_none()
1027+
if df is None or len(df) == 0:
1028+
html_repr += "<p class='container-fields'>No external resource references.</p>"
1029+
else:
1030+
html_repr += df.to_html()
1031+
html_repr += "</div>"
1032+
return html_repr
1033+
9901034
@docval({'name': 'path', 'type': str, 'doc': 'The path to the zip file.'})
9911035
def to_zip(self, **kwargs):
9921036
"""

tests/unit/common/test_resources.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,27 @@ def test_to_dataframe(self):
131131
'entities_idx': 'uint32'})
132132
pd.testing.assert_frame_equal(result_df, expected_df)
133133

134+
def test_repr_empty(self):
135+
er = HERD()
136+
# repr and HTML repr must not raise on an empty HERD (to_dataframe raises when empty)
137+
self.assertIn('0 key(s)', repr(er))
138+
html = er._repr_html_()
139+
self.assertIn('No external resource references', html)
140+
141+
def test_repr_populated(self):
142+
er = HERD()
143+
er.add_ref(file=HERDManagerContainer(name='file'),
144+
container=Container(name='Container'),
145+
key='Mus musculus',
146+
entity_id='NCBI_TAXON:10090',
147+
entity_uri='http://x')
148+
text = repr(er)
149+
self.assertIn('1 key(s), 1 entity(ies), 1 object(s), 1 file(s)', text)
150+
# the HTML repr surfaces the flattened table content
151+
html = er._repr_html_()
152+
self.assertIn('NCBI_TAXON:10090', html)
153+
self.assertIn('http://x', html)
154+
134155
def test_assert_external_resources_equal(self):
135156
file = HERDManagerContainer(name='file')
136157
ref_container_1 = Container(name='Container_1')

0 commit comments

Comments
 (0)