From 3b6fcded35a93e7bbe5e81dc686977ac0da3874c Mon Sep 17 00:00:00 2001 From: ialarmedalien Date: Tue, 7 Oct 2025 07:32:17 -0700 Subject: [PATCH] Add a type_roots function to find all base types in a schema --- linkml_runtime/utils/schemaview.py | 11 ++++++ tests/test_utils/test_schemaview.py | 58 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/linkml_runtime/utils/schemaview.py b/linkml_runtime/utils/schemaview.py index 9e1b2e89..434c42e1 100644 --- a/linkml_runtime/utils/schemaview.py +++ b/linkml_runtime/utils/schemaview.py @@ -1151,6 +1151,17 @@ def slot_leaves(self, imports: bool = True, mixins: bool = True) -> list[SlotDef c for c in self.all_slots(imports=imports) if self.slot_children(c, mixins=mixins, imports=imports) == [] ] + @lru_cache(None) + def type_roots(self, imports: bool = True) -> list[TypeDefinitionName]: + """Return all types that have no parents. + + :param imports: whether or not to include imports, defaults to True + :type imports: bool, optional + :return: list of all root types + :rtype: list[TypeDefinitionName] + """ + return [t for t in self.all_types(imports=imports) if not self.type_parents(t, imports=imports)] + @lru_cache(None) def is_multivalued(self, slot_name: SlotDefinition) -> bool: """Return True if slot is multivalued, else returns False. diff --git a/tests/test_utils/test_schemaview.py b/tests/test_utils/test_schemaview.py index 311d09e1..695ad789 100644 --- a/tests/test_utils/test_schemaview.py +++ b/tests/test_utils/test_schemaview.py @@ -957,6 +957,64 @@ def test_all_types_induced_types(schema_view_with_imports: SchemaView) -> None: assert len(view.type_ancestors("SymbolString")) == len(["SymbolString", "string"]) +@pytest.mark.parametrize( + ("schema", "type_roots"), + [ + ( + """ + string: + uri: xsd:string + base: str + integer: + uri: xsd:integer + base: int + boolean: + uri: xsd:boolean + base: Bool +""", + {"string", "integer", "boolean"}, + ), + ( + """ + string: + uri: xsd:string + base: str + acronym: + typeof: string + TLA: + typeof: acronym + even_number: + typeof: integer + integer: + uri: xsd:integer + base: int +""", + {"string", "integer"}, + ), + ( + """ + circular_type: + uri: xsd:Circle + typeof: type_circle + + type_circle: + typeof: circular_type + + semi_circular_type: + typeof: circular_type +""", + set(), + ), + ], +) +def test_type_roots(schema: str, type_roots: set[str]) -> None: + """Test the retrieval of types with no parent type.""" + schema_head = "id: https://w3id.org/linkml/tests/types\nname: types\ntypes:\n" + + sv = SchemaView(f"{schema_head}\n{schema}") + assert set(sv.type_roots()) == type_roots + + def test_all_enums(schema_view_with_imports: SchemaView) -> None: """Test all_enums""" view = schema_view_with_imports