Skip to content

Commit

Permalink
bugfix default value in convention
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasprobst committed Dec 26, 2023
1 parent ab7bdf5 commit 2ccff1f
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 30 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

Log of changes in the versions

## v1.1.1
- bugfix: Setting a default value for toolbox validators in convention yaml file was not working. Fixed it.

## v1.1.0
- simplified and clean up much code, especially convention sub package
- added identifier utils
Expand Down
7 changes: 4 additions & 3 deletions h5rdmtoolbox/convention/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import h5py
import inspect
import pathlib
import pydantic
import re
import shutil
import sys
Expand All @@ -13,9 +12,9 @@
from pydoc import locate
from typing import Union, List, Dict, Tuple

from h5rdmtoolbox import errors
from h5rdmtoolbox.repository import RepositoryInterface
from h5rdmtoolbox.wrapper import ds_decoder
from h5rdmtoolbox import errors
from . import cfg
from . import consts
from . import errors
Expand Down Expand Up @@ -399,7 +398,8 @@ def register(self):
add_convention(self)

def validate(self, file_or_filename: Union[str, pathlib.Path, "File"]) -> List[Dict]:
"""Checks a file for compliance with the convention
"""Checks a file for compliance with the convention. It will NOT raise an error but
return a list of invalid attributes.
Parameters
----------
Expand Down Expand Up @@ -469,6 +469,7 @@ def _validate_convention(name, node):

with File(file_or_filename, 'r') as f:
logger.debug(f'Checking file {file_or_filename} for compliance with convention {self.name}')
_validate_convention('/', f)
f.visititems(_validate_convention)

return failed
Expand Down
39 changes: 27 additions & 12 deletions h5rdmtoolbox/convention/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,26 +127,41 @@ def write_convention_module_from_yaml(yaml_filename: pathlib.Path, name=None):
for k, v in class_definitions.items():
f.write(f'\nclass {k[1:]}(BaseModel):')
description = v.get('description', k[1:])
f.write(f'\n{INDENT}"""{description}"""\n{INDENT}')
f.write(f'\n{INDENT}"""{description}"""\n')
validator = {}
_validator_default = {}
for kk, vv in v.items():
_vv = vv.strip('$')
if _vv in toolbox_validators.validators:
# _vv = vv.strip('$')
splitted_validator = vv.strip('$').split('=', 1)
validator_name = splitted_validator[0].strip()
if len(splitted_validator) == 2:
default_value = splitted_validator[1].strip()
else:
default_value = None
_validator_default[kk] = [validator_name, default_value]

if validator_name in toolbox_validators.validators:

try:
if toolbox_validators.validators[_vv].__name__ != 'Annotated': # in py3.8 this will raise an error
validator[kk] = f'toolbox_validators.{toolbox_validators.validators[_vv].__name__}'
used_toolbox_validators[_vv] = f'toolbox_validators.{toolbox_validators.validators[_vv].__name__}'
if toolbox_validators.validators[validator_name].__name__ != 'Annotated': # in py3.8 this will raise an error
validator[kk] = f'toolbox_validators.{toolbox_validators.validators[validator_name].__name__}'
used_toolbox_validators[validator_name] = f'toolbox_validators.{toolbox_validators.validators[validator_name].__name__}'
else:
validator[kk] = f'toolbox_validators.validators["{_vv}"]'
used_toolbox_validators[_vv] = f'toolbox_validators.validators["{_vv}"]'
validator[kk] = f'toolbox_validators.validators["{validator_name}"]'
used_toolbox_validators[validator_name] = f'toolbox_validators.validators["{validator_name}"]'

except AttributeError:
validator[kk] = f'toolbox_validators.validators["{_vv}"]'
used_toolbox_validators[_vv] = f'toolbox_validators.validators["{_vv}"]'
validator[kk] = f'toolbox_validators.validators["{validator_name}"]'
used_toolbox_validators[validator_name] = f'toolbox_validators.validators["{validator_name}"]'
else:
validator[kk] = vv
f.write(f'\n{INDENT}'.join([f'{ak}: {av}' for ak, av in validator.items()]))
for ak, av in validator.items():
validator_value, validator_default = _validator_default[ak]
if validator_default is not None:
f.write(f'{INDENT}{ak}: {used_toolbox_validators.get(validator_name, validator_value)} = {validator_default}\n')
else:
f.write(f'{INDENT}{ak}: {av}\n')
# f.write(f'\n{INDENT}'.join([f'{ak}: {av}' for ak, av in validator.items()]))
f.write('\n\n')

# write standard attribute classes:
Expand Down Expand Up @@ -185,7 +200,7 @@ def write_convention_module_from_yaml(yaml_filename: pathlib.Path, name=None):

# with open(py_filename, 'a') as f:
f.write('\nvalidator_dict = {\n' + INDENT)
f.write(f'\n{INDENT}'.join(f"'{k}': {k[1:]}," for k, v in class_definitions.items()))
f.write(f'\n{INDENT}'.join(f"\n'{k}': {k[1:]}," for k, v in class_definitions.items()))
f.write(f'\n{INDENT}'.join(f"'{k}': {v}," for k, v in used_toolbox_validators.items()))
# f.write(f"\n{INDENT}'$int': IntValidator, # see h5rdmtoolbox.convention.toolbox_validators")
# f.write(f"\n{INDENT}'$str': StringValidator, # see h5rdmtoolbox.convention.toolbox_validators")
Expand Down
11 changes: 11 additions & 0 deletions tests/conventions/simple_cv.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
__contact__: https://orcid.org/0000-0001-8729-0482
__institution__: https://orcid.org/members/001G000001e5aUTIAY
__name__: simple_cv

$personOrOrganization:
name: str
orcid: $orcid=None

creator:
validator: $personOrOrganization
description: Name and affiliation of the data creator.
target_method: __init__
default_value: $NONE

units:
default_value: $EMPTY
description: The physical unit of the dataset. If dimensionless, the unit is ''.
Expand Down
49 changes: 34 additions & 15 deletions tests/conventions/test_conventions.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,19 +640,38 @@ def test_engmeta_example(self):
pass

def test_read_invalid_attribute(self):
cv = h5tbx.convention.Convention.from_yaml(__this_dir__ / 'simple_cv.yaml')
h5tbx.use(None)
with h5tbx.File() as h5:
h5.create_dataset('ds', data=[1, 2], attrs=dict(units='invalid'))
with h5tbx.use(cv):
with h5tbx.File(h5.hdf_filename) as h5:
with h5tbx.set_config(ignore_get_std_attr_err=True):
with self.assertWarns(h5tbx.warnings.StandardAttributeValidationWarning):
units = h5.ds.units
self.assertEqual(units, 'invalid')
with h5tbx.use(None):
with h5tbx.File(h5.hdf_filename) as h5:
cv = h5tbx.convention.Convention.from_yaml(__this_dir__ / 'simple_cv.yaml', overwrite=True)
# h5tbx.use(None)
# with h5tbx.File() as h5:
# h5.create_dataset('ds', data=[1, 2], attrs=dict(units='invalid'))
# with h5tbx.use(cv):
# with h5tbx.File(h5.hdf_filename) as h5:
# with h5tbx.set_config(ignore_get_std_attr_err=True):
# with self.assertWarns(h5tbx.warnings.StandardAttributeValidationWarning):
# units = h5.ds.units
# self.assertEqual(units, 'invalid')
# with h5tbx.use(None):
# with h5tbx.File(h5.hdf_filename) as h5:
# with self.assertRaises(AttributeError):
# _ = h5.ds.units
# units = h5.ds.attrs['units']
# self.assertEqual(units, 'invalid')

with h5tbx.use('simple_cv'):
with h5tbx.File(creator={'name': 'Joe'}) as h5:
self.assertEqual(h5.creator.name, 'Joe')
self.assertEqual(h5.creator.orcid, None)
with h5tbx.File(creator={'name': 'Joe',
'invalid': '123'}) as h5:
self.assertEqual(h5.creator.name, 'Joe')
self.assertEqual(h5.creator.orcid, None)
with self.assertRaises(AttributeError):
_ = h5.ds.units
units = h5.ds.attrs['units']
self.assertEqual(units, 'invalid')
self.assertEqual(h5.creator.invalid, '123')
with self.assertRaises(h5tbx.errors.StandardAttributeError):
with h5tbx.File(creator={'name': 'Joe',
'orcid': '123'}) as h5:
pass
with h5tbx.File(creator={'name': 'Joe',
'orcid': h5tbx.__author_orcid__}) as h5:
self.assertEqual(h5.creator.name, 'Joe')
self.assertEqual(str(h5.creator.orcid), h5tbx.__author_orcid__)

0 comments on commit 2ccff1f

Please sign in to comment.