Skip to content

Commit 2ca7b3f

Browse files
Correct imports closure order
- remove 'traverse' parameter which is identical to 'imports' - correct test_issues/test_linkml_issue_998.py which was incorrectly specifying number of slots that used an enum? not sure why that was related.
1 parent 390887a commit 2ca7b3f

File tree

3 files changed

+30
-18
lines changed

3 files changed

+30
-18
lines changed

linkml_runtime/utils/schemaview.py

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import os
2-
import pdb
32
import uuid
43
import logging
54
import collections
65
from functools import lru_cache
76
from copy import copy, deepcopy
8-
from collections import defaultdict
7+
from collections import defaultdict, deque
98
from pathlib import Path
109
from typing import Mapping, Tuple
1110

@@ -208,35 +207,48 @@ def load_import(self, imp: str, from_schema: SchemaDefinition = None):
208207
return schema
209208

210209
@lru_cache()
211-
def imports_closure(self, imports: bool = True, traverse=True, inject_metadata=True) -> List[SchemaDefinitionName]:
210+
def imports_closure(self, imports: bool = True, inject_metadata=True) -> List[SchemaDefinitionName]:
212211
"""
213212
Return all imports
214213
215214
:param traverse: if true, traverse recursively
216215
:return: all schema names in the transitive reflexive imports closure
217216
"""
218-
if not imports:
219-
return [self.schema.name]
220217
if self.schema_map is None:
221218
self.schema_map = {self.schema.name: self.schema}
222-
closure = []
219+
220+
closure = deque()
223221
visited = set()
224222
todo = [self.schema.name]
225-
if not traverse:
223+
224+
if not imports:
226225
return todo
226+
227227
while len(todo) > 0:
228+
# visit item
228229
sn = todo.pop()
229-
visited.add(sn)
230230
if sn not in self.schema_map:
231231
imported_schema = self.load_import(sn)
232232
self.schema_map[sn] = imported_schema
233-
s = self.schema_map[sn]
234-
if sn not in closure:
235-
#closure.insert(0, sn)
236-
closure.append(sn)
237-
for i in s.imports:
238-
if i not in visited:
239-
todo.append(i)
233+
234+
# resolve item's imports if it has not been visited already
235+
# we will get duplicates, but not cycles this way, and
236+
# filter out dupes, preserving the first entry, at the end.
237+
if sn not in visited:
238+
for i in self.schema_map[sn].imports:
239+
# no self imports ;)
240+
if i != sn:
241+
todo.append(i)
242+
243+
# add item to closure
244+
# append + pop (above) is FILO queue, which correctly extends tree leaves,
245+
# but in backwards order.
246+
closure.appendleft(sn)
247+
visited.add(sn)
248+
249+
# filter duplicates, keeping first entry
250+
closure = list({k:None for k in closure}.keys())
251+
240252
if inject_metadata:
241253
for s in self.schema_map.values():
242254
for x in {**s.classes, **s.enums, **s.slots, **s.subsets, **s.types}.values():

tests/test_issues/test_linkml_issue_998.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ def test_slots_are_not_duplicated(view):
8181

8282
def test_issue_998_attribute_slot(view):
8383
enum_slots = view.get_slots_by_enum("EmploymentStatusEnum")
84-
assert len(enum_slots) == 1
85-
assert enum_slots[0].name == "employed"
84+
assert len(enum_slots) == 2
85+
assert sorted([slot.name for slot in enum_slots]) == ["employed", "past_employer"]
8686

8787

8888
def test_issue_998_schema_and_attribute_slots(view):

tests/test_utils/test_schemaview.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ def test_imports_closure_order(self):
488488
- input/imports/README.md for explanation of the test schema
489489
"""
490490
sv = SchemaView(SCHEMA_IMPORT_TREE)
491-
closure = sv.imports_closure(imports=True, traverse=True)
491+
closure = sv.imports_closure(imports=True)
492492
target = [
493493
'linkml:types',
494494
's1_1',

0 commit comments

Comments
 (0)