Skip to content

Commit af5a1a6

Browse files
follow paths in multi-layer relative imports
1 parent 984e4b2 commit af5a1a6

File tree

17 files changed

+297
-2
lines changed

17 files changed

+297
-2
lines changed

linkml_runtime/utils/schemaview.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,25 @@ def imports_closure(self, imports: bool = True, traverse: Optional[bool] = None,
279279
if sn not in visited:
280280
for i in self.schema_map[sn].imports:
281281
# no self imports ;)
282-
if i != sn:
283-
todo.append(i)
282+
if i == sn:
283+
continue
284+
285+
# resolve relative imports relative to the importing schema, rather than the
286+
# origin schema. Imports can be a URI or Curie, and imports from the same
287+
# directory don't require a ./, so if the current (sn) import is a relative
288+
# path, and the target import doesn't have : (as in a curie or a URI)
289+
# we prepend the relative path. This WILL make the key in the `schema_map` not
290+
# equal to the literal text specified in the importing schema, but this is
291+
# essential to sensible deduplication: eg. for
292+
# - main.yaml (imports ./types.yaml, ./subdir/subschema.yaml)
293+
# - types.yaml
294+
# - subdir/subschema.yaml (imports ./types.yaml)
295+
# - subdir/types.yaml
296+
# we should treat the two `types.yaml` as separate schemas from the POV of the
297+
# origin schema.
298+
if sn.startswith('.') and ':' not in i:
299+
i = os.path.normpath(str(Path(sn).parent / i))
300+
todo.append(i)
284301

285302
# add item to closure
286303
# append + pop (above) is FILO queue, which correctly extends tree leaves,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
id: child
2+
name: child
3+
title: child
4+
description: |
5+
Child class that shares the same name and ID as another child and should *not* be deduplicated,
6+
but imports the dupe schema which *should* be deduplicated
7+
imports:
8+
- linkml:types
9+
- ../../L1_0_1/dupe
10+
classes:
11+
Child1:
12+
attributes:
13+
value:
14+
range: string
15+
ifabsent: "Child1"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
id: child
2+
name: child
3+
title: child
4+
description: |
5+
Child class that shares the same name and ID as another child and should *not* be deduplicated,
6+
but imports the dupe schema which *should* be deduplicated
7+
imports:
8+
- linkml:types
9+
- ../../L1_0_1/dupe
10+
classes:
11+
Child2:
12+
attributes:
13+
value:
14+
range: string
15+
ifabsent: "Child2"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
id: main
2+
name: main
3+
title: main
4+
imports:
5+
- linkml:types
6+
- neighbor
7+
- ../parent
8+
- ../../L0_1/cousin
9+
- ./L2_0_0_0/child
10+
- ./L2_0_0_1/child
11+
classes:
12+
Main:
13+
description: "Our intrepid main class!"
14+
attributes:
15+
value:
16+
range: string
17+
ifabsent: "Main"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
id: neighbor
2+
name: neighbor
3+
title: neighbor
4+
description: neighbor class imported without ./, but has relative imports of its own
5+
imports:
6+
- ../neighborhood_parent
7+
classes:
8+
Neighbor:
9+
description: "Our main class's best friend!"
10+
attributes:
11+
value:
12+
range: string
13+
ifabsent: "Neighbor"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
id: grandchild
2+
name: grandchild
3+
title: grandchild
4+
description: Grandchild schema that should cause a cycle with parent if we are just naively concatenating paths
5+
imports:
6+
- ../../parent
7+
classes:
8+
Grandchild:
9+
description: "spoiled rotten!"
10+
attributes:
11+
value:
12+
range: string
13+
ifabsent: "Grandchild"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
id: dupe
2+
name: dupe
3+
title: dupe
4+
description: A Duplicate schema that is imported from multiple places (but should only actually be imported once)
5+
classes:
6+
Dupe:
7+
description: "A class from the duplicated import!"
8+
attributes:
9+
value:
10+
range: string
11+
ifabsent: "Dupe"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
id: neighborhood_parent
2+
name: neighborhood_parent
3+
title: neighborhood_parent
4+
description: parent of same-directory import
5+
classes:
6+
Neighborhood_Parent:
7+
description: "Keeps the cul-de-sac fed and scolds the speeding traffic"
8+
attributes:
9+
value:
10+
range: string
11+
ifabsent: "Neighborhood_Parent"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
id: parent
2+
name: parent
3+
title: parent
4+
description: Parent of our main schema, imports two layers down in the grandchild
5+
imports:
6+
- linkml:types
7+
- ./L1_0_1/L2_0_1_0/grandchild
8+
classes:
9+
Parent:
10+
attributes:
11+
value:
12+
range: string
13+
ifabsent: "Parent"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
id: apple
2+
name: apple
3+
title: apple
4+
classes:
5+
Apple:
6+
attributes:
7+
value:
8+
range: string
9+
ifabsent: "Apple"

0 commit comments

Comments
 (0)