Skip to content

HERD.get_object_entities(attribute=...) is asymmetric with add_ref(attribute=...) and fails for non-DataType attributes #1501

Description

@rly

What happened?

HERD.add_ref(container=..., attribute=...) and HERD.get_object_entities(container=..., attribute=...) resolve the attribute argument differently, so a reference added with an attribute cannot be read back with the same attribute.

  • add_ref routes through _validate_object (src/hdmf/common/resources.py:547-591), which handles three cases. For a non-DataType attribute (a scalar attribute such as DynamicTable.description), it stores the parent container with a computed relative_path (e.g. relative_path='description').
  • get_object_entities (src/hdmf/common/resources.py:883-887) instead does container[attribute] and passes the default relative_path=''.

Because container[attribute] uses __getitem__ (column/row lookup on DynamicTable), a scalar attribute name raises KeyError, and for containers that do not implement __getitem__ it raises TypeError. The attribute path in get_object_entities therefore only works for the narrow case where container[attribute] happens to return the same object that was stored (e.g. a DynamicTable column).

Steps to reproduce

from hdmf.common import DynamicTable
from hdmf.common.resources import HERD
from hdmf import Container, HERDManager

class FileC(Container, HERDManager):
    __fields__ = ({'name': 'external_resources', 'child': True, 'required_name': 'external_resources'},)

table = DynamicTable(name='table', description='table')
table.add_column(name='col1', description='column')
table.add_row(id=0, col1='data')
file = FileC(name='file')

er = HERD()
er.add_ref(file=file, container=table, attribute='description',
           key='key1', entity_id='entity_0', entity_uri='entity_0_uri')
print(er.objects.data[0])
# (0, '<uuid>', 'DynamicTable', 'description', '')   <- stored with relative_path='description'

er.get_object_entities(file=file, container=table, attribute='description')
# KeyError: 'description'

# Workaround that does work, showing the data is retrievable:
er.get_object_entities(file=file, container=table, relative_path='description')
# [{'entity_id': 'entity_0', 'entity_uri': 'entity_0_uri'}]

Expected

get_object_entities(attribute=...) should resolve the object the same way add_ref(attribute=...) does (i.e. reuse the _validate_object logic, computing the same relative_path), so that a reference added with a given attribute can be retrieved with the same attribute. At minimum the two methods should be symmetric for every attribute case add_ref supports.

Notes

  • The existing test_get_obj_entities_attribute passes only because it uses a DynamicTable column (attribute='col1'), where both getattr and __getitem__ resolve to the same column object (the DataType-attribute case). It does not cover the non-DataType attribute case exercised by test_add_ref_nested.

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions