Skip to content

Commit

Permalink
Merge branch 'dev' into config
Browse files Browse the repository at this point in the history
  • Loading branch information
mavaylon1 authored Feb 28, 2024
2 parents 041099e + 048b9b8 commit cec4050
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 11 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/run_coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ jobs:
python -m coverage report -m
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ repos:
# hooks:
# - id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.14
rev: v0.2.0
hooks:
- id: ruff
# - repo: https://github.com/econchick/interrogate
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# HDMF Changelog

## HDMF 3.12.1 (Upcoming)
## HDMF 3.12.2 (February 9, 2024)

### Bug fixes
- Fixed recursion error in html representation generation in jupyter notebooks. @stephprince [#1038](https://github.com/hdmf-dev/hdmf/pull/1038)

## HDMF 3.12.1 (February 5, 2024)

### Bug fixes
- Fixed retrieving the correct path for a `HERD` zip file on read. [#1046](https://github.com/hdmf-dev/hdmf/pull/1046)
- Fixed internal links in docstrings and tutorials. @stephprince [#1031](https://github.com/hdmf-dev/hdmf/pull/1031)
- Fixed issue with creating documentation links to classes in docval arguments. @rly [#1036](https://github.com/hdmf-dev/hdmf/pull/1036)
- Fixed issue with validator not validating against the spec that defines the data type of the builder. @rly [#1050](https://github.com/hdmf-dev/hdmf/pull/1050)

## HDMF 3.12.0 (January 16, 2024)

Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ Overall Health
.. image:: https://github.com/hdmf-dev/hdmf/actions/workflows/ruff.yml/badge.svg
:target: https://github.com/hdmf-dev/hdmf/actions/workflows/ruff.yml

.. image:: https://github.com/hdmf-dev/hdmf/actions/workflows/check_external_links.yml/badge.svg
:target: https://github.com/hdmf-dev/hdmf/actions/workflows/check_external_links.yml
.. image:: https://github.com/hdmf-dev/hdmf/actions/workflows/check_sphinx_links.yml/badge.svg
:target: https://github.com/hdmf-dev/hdmf/actions/workflows/check_sphinx_links.yml

.. image:: https://github.com/hdmf-dev/hdmf/actions/workflows/run_pynwb_tests.yml/badge.svg
:target: https://github.com/hdmf-dev/hdmf/actions/workflows/run_pynwb_tests.yml
Expand Down
10 changes: 8 additions & 2 deletions src/hdmf/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,10 @@ def _generate_html_repr(self, fields, level=0, access_code="", is_field=False):
if isinstance(fields, dict):
for key, value in fields.items():
current_access_code = f"{access_code}.{key}" if is_field else f"{access_code}['{key}']"
html_repr += self._generate_field_html(key, value, level, current_access_code)
if hasattr(value, '_generate_field_html'):
html_repr += value._generate_field_html(key, value, level, current_access_code)
else:
html_repr += self._generate_field_html(key, value, level, current_access_code)
elif isinstance(fields, list):
for index, item in enumerate(fields):
access_code += f'[{index}]'
Expand All @@ -707,7 +710,10 @@ def _generate_html_repr(self, fields, level=0, access_code="", is_field=False):
return html_repr

def _generate_field_html(self, key, value, level, access_code):
"""Generates HTML for a single field."""
"""Generates HTML for a single field.
This function can be overwritten by a child class to implement customized html representations.
"""

if isinstance(value, (int, float, str, bool)):
return f'<div style="margin-left: {level * 20}px;" class="container-fields"><span class="field-key"' \
Expand Down
21 changes: 17 additions & 4 deletions src/hdmf/validate/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,8 @@ def __validate_child_builder(self, child_spec, child_builder, parent_builder):
yield self.__construct_illegal_link_error(child_spec, parent_builder)
return # do not validate illegally linked objects
child_builder = child_builder.builder
for child_validator in self.__get_child_validators(child_spec):
child_builder_data_type = child_builder.attributes.get(self.spec.type_key())
for child_validator in self.__get_child_validators(child_spec, child_builder_data_type):
yield from child_validator.validate(child_builder)

def __construct_illegal_link_error(self, child_spec, parent_builder):
Expand All @@ -557,7 +558,7 @@ def __construct_illegal_link_error(self, child_spec, parent_builder):
def __cannot_be_link(spec):
return not isinstance(spec, LinkSpec) and not spec.linkable

def __get_child_validators(self, spec):
def __get_child_validators(self, spec, builder_data_type):
"""Returns the appropriate list of validators for a child spec
Due to the fact that child specs can both inherit a data type via data_type_inc
Expand All @@ -572,9 +573,21 @@ def __get_child_validators(self, spec):
returned. If the spec is a LinkSpec, no additional Validator is returned
because the LinkSpec cannot add or modify fields and the target_type will be
validated by the Validator returned from the ValidatorMap.
For example, if the spec is:
{'doc': 'Acquired, raw data.', 'quantity': '*', 'data_type_inc': 'NWBDataInterface'}
then the returned validators will be:
- a validator for the spec for the builder data type
- a validator for the spec for data_type_def: NWBDataInterface
- a validator for the above spec which might have extended properties
on top of data_type_def: NWBDataInterface
"""
if _resolve_data_type(spec) is not None:
yield self.vmap.get_validator(_resolve_data_type(spec))
if builder_data_type is not None:
yield self.vmap.get_validator(builder_data_type)

spec_data_type = _resolve_data_type(spec)
if spec_data_type is not None:
yield self.vmap.get_validator(spec_data_type)

if isinstance(spec, GroupSpec):
yield GroupValidator(spec, self.vmap)
Expand Down
52 changes: 52 additions & 0 deletions tests/unit/validator_tests/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -1217,3 +1217,55 @@ def test_empty_int_dataset(self):
DatasetBuilder(name='dataInt', data=[], dtype='int') # <-- Empty int dataset
])
self.runBuilderRoundTrip(builder)


class TestValidateSubspec(ValidatorTestBase):
"""When a subtype satisfies a subspec, the validator should also validate
that the subtype satisfies its spec.
"""

def getSpecs(self):
dataset_spec = DatasetSpec('A dataset', data_type_def='Foo')
sub_dataset_spec = DatasetSpec(
doc='A subtype of Foo',
data_type_def='Bar',
data_type_inc='Foo',
attributes=[
AttributeSpec(name='attr1', doc='an example attribute', dtype='text')
],
)
spec = GroupSpec(
doc='A group that contains a Foo',
data_type_def='Baz',
datasets=[
DatasetSpec(doc='Child Dataset', data_type_inc='Foo'),
])
return (spec, dataset_spec, sub_dataset_spec)

def test_validate_subtype(self):
"""Test that when spec A contains dataset B, and C is a subtype of B, using a C builder is valid.
"""
builder = GroupBuilder(
name='my_baz',
attributes={'data_type': 'Baz'},
datasets=[
DatasetBuilder(name='bar', attributes={'data_type': 'Bar', 'attr1': 'value'})
],
)
result = self.vmap.validate(builder)
self.assertEqual(len(result), 0)

def test_validate_subtype_error(self):
"""Test that when spec A contains dataset B, and C is a subtype of B, using a C builder validates
against spec C.
"""
builder = GroupBuilder(
name='my_baz',
attributes={'data_type': 'Baz'},
datasets=[
DatasetBuilder(name='bar', attributes={'data_type': 'Bar'})
],
)
result = self.vmap.validate(builder)
self.assertEqual(len(result), 1)
self.assertValidationError(result[0], MissingError, name='Bar/attr1')

0 comments on commit cec4050

Please sign in to comment.