From ad8d5ad531bafd1d388ae94b79e367220c5b09a3 Mon Sep 17 00:00:00 2001 From: Vincent Kelleher Date: Thu, 5 Dec 2024 11:42:54 +0100 Subject: [PATCH] raise an error when two classes have the same ambiguous attribute Closes linkml/linkml#2403 Signed-off-by: Vincent Kelleher --- linkml_runtime/utils/schemaview.py | 4 +-- .../get_slot_with_ambiguous_attributes.yaml | 27 +++++++++++++++++++ tests/test_utils/test_schemaview.py | 11 +++++--- 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 tests/test_utils/input/get_slot_with_ambiguous_attributes.yaml diff --git a/linkml_runtime/utils/schemaview.py b/linkml_runtime/utils/schemaview.py index b937964d..16c68458 100644 --- a/linkml_runtime/utils/schemaview.py +++ b/linkml_runtime/utils/schemaview.py @@ -631,8 +631,8 @@ def get_slot(self, slot_name: SLOT_NAME, imports=True, attributes=True, strict=F for c in self.all_classes(imports=imports).values(): if slot_name in c.attributes: if slot is not None: - # slot name is ambiguous: return a stub slot - return SlotDefinition(slot_name) + raise ValueError(f'Attribute "{slot_name}" is already defined in another class, please use a ' + f'slot in that case') slot = copy(c.attributes[slot_name]) slot.from_schema = c.from_schema slot.owner = c.name diff --git a/tests/test_utils/input/get_slot_with_ambiguous_attributes.yaml b/tests/test_utils/input/get_slot_with_ambiguous_attributes.yaml new file mode 100644 index 00000000..619bbc42 --- /dev/null +++ b/tests/test_utils/input/get_slot_with_ambiguous_attributes.yaml @@ -0,0 +1,27 @@ +id: https://examples.org/get-slot-with-attribute# +name: get-slot-with-attribute + +prefixes: + test: https://examples.org/get-slot-with-attribute# + +default_prefix: test +default_range: string + +classes: + ClassWithAttributes: + attributes: + randomAttribute: + description: "A random attribute for testing purposes" + range: integer + minimum_value: 0 + maximum_value: 999 + + ImportantSecondClass: + description: "Important class to reproduce the error I got as the class loop needs to have at least a + second iteration" + attributes: + randomAttribute: + description: "Now you see the ambiguity intensifying ?" + range: integer + minimum_value: 0 + maximum_value: 111 \ No newline at end of file diff --git a/tests/test_utils/test_schemaview.py b/tests/test_utils/test_schemaview.py index c9f28ff8..a27e9e9f 100644 --- a/tests/test_utils/test_schemaview.py +++ b/tests/test_utils/test_schemaview.py @@ -744,9 +744,6 @@ def test_ambiguous_attributes(): a2x = SlotDefinition('a2', range='BarEnum') view.add_class(ClassDefinition('C2', attributes={a1x.name: a1x, a2x.name: a2x})) - assert view.get_slot(a1.name).range is None - assert view.get_slot(a2.name).range is None - assert view.get_slot(a3.name).range is not None assert len(view.all_slots(attributes=True)) == 3 assert len(view.all_slots(attributes=False)) == 0 assert len(view.all_slots()) == 3 @@ -757,6 +754,14 @@ def test_ambiguous_attributes(): assert view.induced_slot(a2x.name, 'C2').range == a2x.range +def test_ambiguous_attribute_through_get_slot(): + schema_path = os.path.join(INPUT_DIR, "get_slot_with_ambiguous_attributes.yaml") + sv = SchemaView(schema_path) + with pytest.raises(ValueError) as exception: + sv.get_slot("randomAttribute") + assert str(exception.value) == ('Attribute "randomAttribute" is already defined in another class, please use a ' + 'slot in that case') + def test_metamodel_in_schemaview(): view = package_schemaview('linkml_runtime.linkml_model.meta') assert 'meta' in view.imports_closure()