diff --git a/linkml_runtime/utils/schemaview.py b/linkml_runtime/utils/schemaview.py index 42b238c9..1867a404 100644 --- a/linkml_runtime/utils/schemaview.py +++ b/linkml_runtime/utils/schemaview.py @@ -465,8 +465,7 @@ def imports_closure( # visit item sn = todo.pop() if sn not in self.schema_map: - imported_schema = self.load_import(sn) - self.schema_map[sn] = imported_schema + self.schema_map[sn] = self.load_import(sn) # resolve item's imports if it has not been visited already # we will get duplicates, but not cycles this way, and @@ -1904,6 +1903,10 @@ def slot_applicable_range_elements(self, slot: SlotDefinition) -> list[ClassDefi :param slot: :return: list of element types """ + if not slot or not isinstance(slot, SlotDefinition): + err_msg = "A SlotDefinition must be provided to generate the slot applicable range elements." + raise ValueError(err_msg) + is_any = False range_types = [] for r in self.slot_range_as_union(slot): @@ -1930,6 +1933,10 @@ def slot_range_as_union(self, slot: SlotDefinition) -> list[ElementName]: :param slot: :return: list of ranges """ + if not slot or not isinstance(slot, SlotDefinition): + err_msg = "A SlotDefinition must be provided to generate the slot range as union." + raise ValueError(err_msg) + return list({y.range for y in [slot, *[x for x in [*slot.exactly_one_of, *slot.any_of] if x.range]]}) def induced_slot_range(self, slot: SlotDefinition, strict: bool = False) -> set[str | ElementName]: # noqa: FBT001, FBT002 @@ -1947,6 +1954,9 @@ def induced_slot_range(self, slot: SlotDefinition, strict: bool = False) -> set[ :return: set of ranges :rtype: set[str | ElementName] """ + if not slot or not isinstance(slot, SlotDefinition): + err_msg = "A SlotDefinition must be provided to generate the induced slot range." + raise ValueError(err_msg) slot_range = slot.range any_of_range = {x.range for x in slot.any_of if x.range} diff --git a/tests/test_utils/test_schemaview.py b/tests/test_utils/test_schemaview.py index c1eef1ed..52511ccd 100644 --- a/tests/test_utils/test_schemaview.py +++ b/tests/test_utils/test_schemaview.py @@ -2125,6 +2125,41 @@ def test_slot_range( pytest.fail(f"Unexpected range_function value: {range_function}") +@pytest.mark.parametrize( + "slot_argument", + [ + None, + "slot_name", + 12345, + "", + set(), + {"this": "that"}, + True, + False, + lambda x: f"slot {x}", + ClassDefinition(name="whatever"), + SlotDefinitionName("something"), + ], +) +@pytest.mark.parametrize( + ("range_function", "err_desc"), + [ + ("slot_range_as_union", "slot range as union"), + ("induced_slot_range", "induced slot range"), + ("slot_applicable_range_elements", "slot applicable range elements"), + ], +) +def test_range_function_non_slot_input( + schema_view_core: SchemaView, slot_argument: Any, range_function: str, err_desc: str +) -> None: + """Ensure that incorrect input to the range function generates an error message. + + The schema content is not important as this is solely for testing errors when calling `range` functions with the wrong argument. + """ + with pytest.raises(ValueError, match=f"A SlotDefinition must be provided to generate the {err_desc}."): + getattr(schema_view_core, range_function)(slot_argument) + + """End of range-related tests. Phew!"""