diff --git a/src/caosadvancedtools/models/parser.py b/src/caosadvancedtools/models/parser.py index 579ece112306673cab78740a9e8dc5c1b84c89c9..cda51d918c6d078e5c19ea84c34f848d4c12e3c2 100644 --- a/src/caosadvancedtools/models/parser.py +++ b/src/caosadvancedtools/models/parser.py @@ -46,8 +46,9 @@ from typing import List, Optional from warnings import warn import jsonschema -import caosdb as db +import linkahead as db +from linkahead.common.datatype import get_list_datatype from .data_model import CAOSDB_INTERNAL_PROPERTIES, DataModel # Keywords which are allowed in data model descriptions. @@ -82,23 +83,6 @@ JSON_SCHEMA_ATOMIC_TYPES = [ ] -def _get_listdatatype(dtype): - """matches a string to check whether the type definition is a list - - returns the type within the list or None, if it cannot be matched with a - list definition - """ - # TODO: string representation should be the same as used by the server: - # e.g. LIST<TEXT> - # this should be changed in the module and the old behavour should be - # marked as depricated - match = re.match(r"^LIST[(<](?P<dt>.*)[)>]$", dtype) - - if match is None: - return None - else: - return match.group("dt") - # Taken from https://stackoverflow.com/a/53647080, CC-BY-SA, 2018 by # https://stackoverflow.com/users/2572431/augurar @@ -412,8 +396,8 @@ debug : bool, optional # is it a property and "datatype" in definition # but not simply an RT of the model - and not (_get_listdatatype(definition["datatype"]) == name and - _get_listdatatype(definition["datatype"]) in self.model)): + and not (get_list_datatype(definition["datatype"]) == name and + get_list_datatype(definition["datatype"]) in self.model)): # and create the new property self.model[name] = db.Property(name=name, @@ -499,9 +483,9 @@ debug : bool, optional n = self._stringify(n) if isinstance(e, dict): - if "datatype" in e and _get_listdatatype(e["datatype"]) is not None: + if "datatype" in e and get_list_datatype(e["datatype"]) is not None: # Reuse the existing datatype for lists. - datatype = db.LIST(_get_listdatatype(e["datatype"])) + datatype = db.LIST(get_list_datatype(e["datatype"])) else: # Ignore a possible e["datatype"] here if it's not a list # since it has been treated in the definition of the @@ -523,6 +507,9 @@ debug : bool, optional def _inherit(self, name, prop, inheritance): if not isinstance(prop, list): + if isinstance(prop, str): + raise YamlDefinitionError( + f"Parents must be a list but is given as string: {name} > {prop}") raise YamlDefinitionError("Parents must be a list, error in line {}".format( prop["__line__"])) @@ -640,19 +627,19 @@ debug : bool, optional dtype = value.datatype is_list = False - try: - if _get_listdatatype(value.datatype) is not None: - dtype = _get_listdatatype(value.datatype) - is_list = True - except TypeError as err: - err.args = (*err.args, f"yaml line: {value.__dict__.get('__line__')}") - raise(err) - - if dtype in self.model: + if get_list_datatype(dtype) is not None: + dtype = get_list_datatype(dtype) + is_list = True + + dtype_name = dtype + if not isinstance(dtype_name, str): + dtype_name = dtype.name + + if dtype_name in self.model: if is_list: - value.datatype = db.LIST(self.model[dtype]) + value.datatype = db.LIST(self.model[dtype_name]) else: - value.datatype = self.model[dtype] + value.datatype = self.model[dtype_name] continue @@ -674,7 +661,7 @@ debug : bool, optional continue raise ValueError("Property {} has an unknown datatype: {}".format( - value.name, value.datatype)) + value.name, dtype_name)) def _set_recordtypes(self): """ properties are defined in first iteration; set remaining as RTs """ diff --git a/unittests/test_yaml_model_parser.py b/unittests/test_yaml_model_parser.py index a26cdf034eea80df0caf64a73ba0d715f66c31db..4f11f3063adfafd1383e9f42c4d747e9ac47e096 100644 --- a/unittests/test_yaml_model_parser.py +++ b/unittests/test_yaml_model_parser.py @@ -566,3 +566,14 @@ def test_yaml_error(): with raises(ValueError, match=r"line 2: .*"): parse_model_from_yaml("unittests/models/model_invalid.yml") + + +def test_inherit_error(): + """Must fail with an understandable exception.""" + model_string = """ +prop1: + inherit_from_obligatory: prop2 + """ + with raises(YamlDefinitionError, + match=r"Parents must be a list but is given as string: prop1 > prop2"): + parse_model_from_string(model_string)