Skip to content

Commit

Permalink
Support passing GroupSpec/DatasetSpec for LinkSpec target_type (#468)
Browse files Browse the repository at this point in the history
  • Loading branch information
rly committed Nov 19, 2020
1 parent b12a62b commit fab5660
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
- Support `pathlib.Path` paths in `HDMFIO.__init__`, `HDF5IO.__init__`, and `HDF5IO.load_namespaces`. @dsleiter (#439)
- Use hdmf-common-schema 1.2.1. See https://hdmf-common-schema.readthedocs.io/en/latest/format_release_notes.html for details.
- Block usage of h5py 3+. h5py>=2.9, <3 is supported. @rly (#461)
- Block usage of numpy>=1.19.4 due to a known issue with numpy on some Windows 10 systems. numpy>1.16, <1.19.4 is supported. @rly (#461)
- Block usage of numpy>=1.19.4 due to a known issue with numpy on some Windows 10 systems. numpy>1.16, <1.19.4 is supported.
@rly (#461)
- Allow passing `GroupSpec` and `DatasetSpec` objects for the 'target_type' argument of `LinkSpec.__init__(...)`.
@rly (#467)

### Internal improvements
- Refactor `HDF5IO.write_dataset` to be more readable. @rly (#428)
Expand Down
11 changes: 9 additions & 2 deletions src/hdmf/spec/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ def build_const_args(cls, spec_dict):

_link_args = [
{'name': 'doc', 'type': str, 'doc': 'a description about what this link represents'},
{'name': _target_type_key, 'type': str, 'doc': 'the target type GroupSpec or DatasetSpec'},
{'name': _target_type_key, 'type': (str, BaseStorageSpec), 'doc': 'the target type GroupSpec or DatasetSpec'},
{'name': 'quantity', 'type': (str, int), 'doc': 'the required number of allowed instance', 'default': 1},
{'name': 'name', 'type': str, 'doc': 'the name of this link', 'default': None}
]
Expand All @@ -776,7 +776,14 @@ class LinkSpec(Spec):
def __init__(self, **kwargs):
doc, target_type, name, quantity = popargs('doc', _target_type_key, 'name', 'quantity', kwargs)
super().__init__(doc, name, **kwargs)
self[_target_type_key] = target_type
if isinstance(target_type, BaseStorageSpec):
if target_type.data_type_def is None:
msg = ("'%s' must be a string or a GroupSpec or DatasetSpec with a '%s' key."
% (_target_type_key, target_type.def_key()))
raise ValueError(msg)
self[_target_type_key] = target_type.data_type_def
else:
self[_target_type_key] = target_type
if quantity != 1:
self['quantity'] = quantity

Expand Down
69 changes: 69 additions & 0 deletions tests/unit/spec_tests/test_link_spec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import json

from hdmf.spec import GroupSpec, LinkSpec
from hdmf.testing import TestCase


class LinkSpecTests(TestCase):

def test_constructor(self):
spec = LinkSpec(
doc='A test link',
target_type='Group1',
quantity='+',
name='Link1',
)
self.assertEqual(spec.doc, 'A test link')
self.assertEqual(spec.target_type, 'Group1')
self.assertEqual(spec.data_type_inc, 'Group1')
self.assertEqual(spec.quantity, '+')
self.assertEqual(spec.name, 'Link1')
json.dumps(spec)

def test_constructor_target_spec_def(self):
group_spec_def = GroupSpec(
data_type_def='Group1',
doc='A test group',
)
spec = LinkSpec(
doc='A test link',
target_type=group_spec_def,
)
self.assertEqual(spec.target_type, 'Group1')
json.dumps(spec)

def test_constructor_target_spec_inc(self):
group_spec_inc = GroupSpec(
data_type_inc='Group1',
doc='A test group',
)
msg = "'target_type' must be a string or a GroupSpec or DatasetSpec with a 'data_type_def' key."
with self.assertRaisesWith(ValueError, msg):
LinkSpec(
doc='A test link',
target_type=group_spec_inc,
)

def test_constructor_defaults(self):
spec = LinkSpec(
doc='A test link',
target_type='Group1',
)
self.assertEqual(spec.quantity, 1)
self.assertIsNone(spec.name)
json.dumps(spec)

def test_required_is_many(self):
quantity_opts = ['?', 1, '*', '+']
is_required = [False, True, False, True]
is_many = [False, False, True, True]
for (quantity, req, many) in zip(quantity_opts, is_required, is_many):
with self.subTest(quantity=quantity):
spec = LinkSpec(
doc='A test link',
target_type='Group1',
quantity=quantity,
name='Link1',
)
self.assertEqual(spec.required, req)
self.assertEqual(spec.is_many(), many)

0 comments on commit fab5660

Please sign in to comment.