diff --git a/kgforge/core/archetypes/mapping.py b/kgforge/core/archetypes/mapping.py index feb4cb64..d5a055c0 100644 --- a/kgforge/core/archetypes/mapping.py +++ b/kgforge/core/archetypes/mapping.py @@ -63,7 +63,7 @@ def load(cls, source: str, mapping_type: MappingType = None): if mapping_type is None: e = cls.load_file(source, raise_ex=False) e = e if e is not None else cls.load_url(source, raise_ex=False) - e = e if e is not None else cls.load_text(source, raise_ex=False) + e = e if e is not None else cls.load_str(source, raise_ex=False) if e is not None: return e raise Exception("Mapping loading failed") @@ -73,16 +73,23 @@ def load(cls, source: str, mapping_type: MappingType = None): elif mapping_type == MappingType.URL: return cls.load_url(source) elif mapping_type == MappingType.STR: - return cls.load_text(source) + return cls.load_str(source) else: raise NotImplementedError @classmethod def load_file(cls, filepath, raise_ex=True): - filepath = Path(filepath) - if filepath.is_file(): - return cls(filepath.read_text()) - else: + try: + filepath = Path(filepath) + + if filepath.is_file(): + return cls(filepath.read_text()) + else: + if raise_ex: + raise FileNotFoundError + return None + + except OSError: if raise_ex: raise FileNotFoundError return None @@ -100,7 +107,7 @@ def load_url(cls, url, raise_ex=True): @classmethod @abstractmethod - def load_text(cls, source: str, raise_ex=True): + def load_str(cls, source: str, raise_ex=True): ... def save(self, path: str) -> None: diff --git a/kgforge/specializations/mappings/dictionaries.py b/kgforge/specializations/mappings/dictionaries.py index ddbd5698..27fa2ffa 100644 --- a/kgforge/specializations/mappings/dictionaries.py +++ b/kgforge/specializations/mappings/dictionaries.py @@ -34,9 +34,12 @@ def _normalize_rules(rules: OrderedDict) -> str: return hjson.dumps(rules, indent=4, item_sort_key=sort_attrs) @classmethod - def load_text(cls, source: str, raise_ex=True): + def load_str(cls, source: str, raise_ex=True): + # hjson loading doesn't make one line strings (non dictionaries) fail if len(source.strip()) > 0 and source.strip()[0] != "{": if raise_ex: - raise Exception("Invalid mapping string") + raise hjson.scanner.HjsonDecodeError( + f"Invalid hjson mapping", doc=source, pos=0 + ) return None return cls(source) diff --git a/tests/specializations/mappers/test_mappers.py b/tests/specializations/mappers/test_mappers.py index e2ee2a95..0dbf0508 100644 --- a/tests/specializations/mappers/test_mappers.py +++ b/tests/specializations/mappers/test_mappers.py @@ -13,3 +13,47 @@ # along with Blue Brain Nexus Forge. If not, see . # Placeholder for the generic parameterizable test suite for mappers. + +import pytest +from contextlib import nullcontext as does_not_raise + +from kgforge.core import KnowledgeGraphForge, Resource +from kgforge.specializations.mappers import DictionaryMapper +from kgforge.specializations.mappings import DictionaryMapping + + +@pytest.fixture +def mapping_str(): + return """ + { + type: x.type + id: x.id + content_type: { + unitCode: f"bytes" + value: x.p1 + } + encodingFormat: x.p2 + } + """ + + +@pytest.mark.parametrize( + "json_to_map, exception", + [ + ({"id": "123", "type": "Type", "p1": "v1a", "p2": "v2a"}, does_not_raise()), + ({"id": "123", "p1": "v1a", "p2": "v2a"}, pytest.raises(AttributeError)), + + ], +) +def test_mapping_load_no_mapping_type( + exception, config, json_to_map, mapping_str +): + + forge = KnowledgeGraphForge(config) + + mapping = DictionaryMapping.load(mapping_str) + + with exception: + DictionaryMapper(forge).map(json_to_map, mapping, None) + + diff --git a/tests/specializations/mappings/test_mappings.py b/tests/specializations/mappings/test_mappings.py index 7b899d70..75615934 100644 --- a/tests/specializations/mappings/test_mappings.py +++ b/tests/specializations/mappings/test_mappings.py @@ -18,6 +18,7 @@ import pytest from contextlib import nullcontext as does_not_raise +import hjson from requests import RequestException from kgforge.core.archetypes.mapping import MappingType @@ -31,9 +32,19 @@ "tests/data/nexus-store/file-to-resource-mapping.hjson" ) -mapping_text_valid = "{}" +mapping_str_valid = "{}" -mapping_text_invalid = "i" +mapping_str_invalid = "i" + +mapping_str_invalid_2 = "{something}" + +mapping_str_invalid_3 = "{a:b}" + +mapping_str_valid_2 = """ +{ + a:b +} +""" @pytest.mark.parametrize( @@ -70,8 +81,8 @@ def test_mapping_load_mapping_type(source, mapping_type, exception): [ (mapping_path_valid, does_not_raise()), (mapping_url_valid, pytest.raises(FileNotFoundError)), - (mapping_text_invalid, pytest.raises(FileNotFoundError)), - (mapping_text_valid, pytest.raises(FileNotFoundError)), + (mapping_str_invalid, pytest.raises(FileNotFoundError)), + (mapping_str_valid, pytest.raises(FileNotFoundError)), ], ) def test_mapping_load_file(source, exception): @@ -84,8 +95,8 @@ def test_mapping_load_file(source, exception): [ (mapping_url_valid, does_not_raise()), (mapping_path_valid, pytest.raises(RequestException)), - (mapping_text_invalid, pytest.raises(RequestException)), - (mapping_text_valid, pytest.raises(RequestException)), + (mapping_str_invalid, pytest.raises(RequestException)), + (mapping_str_valid, pytest.raises(RequestException)), ], ) @@ -97,13 +108,17 @@ def test_mapping_load_url(source, exception): @pytest.mark.parametrize( "source, exception", [ - (mapping_path_valid, pytest.raises(Exception)), - (mapping_url_valid, pytest.raises(Exception)), - (mapping_text_invalid, pytest.raises(Exception)), - (mapping_text_valid, does_not_raise()), - + (mapping_path_valid, pytest.raises(hjson.scanner.HjsonDecodeError)), + (mapping_url_valid, pytest.raises(hjson.scanner.HjsonDecodeError)), + (mapping_str_invalid, pytest.raises(hjson.scanner.HjsonDecodeError)), + (mapping_str_invalid_2, pytest.raises(hjson.scanner.HjsonDecodeError)), + (mapping_str_invalid_3, pytest.raises(hjson.scanner.HjsonDecodeError)), + (mapping_str_valid_2, does_not_raise()), + (mapping_str_valid, does_not_raise()), ], ) -def test_mapping_load_text(source, exception): +def test_mapping_load_str(source, exception): with exception: - mapping = DictionaryMapping.load_text(source) + mapping = DictionaryMapping.load_str(source) + +