Skip to content

Commit 798591b

Browse files
committed
induced_slot() now materializes non-scalar metaslots
1 parent 81b3c62 commit 798591b

File tree

3 files changed

+119
-1
lines changed

3 files changed

+119
-1
lines changed

linkml_runtime/utils/schemaview.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from linkml_runtime.utils.namespaces import Namespaces
1313
from deprecated.classic import deprecated
1414
from linkml_runtime.utils.context_utils import parse_import_map, map_import
15+
from linkml_runtime.utils.formatutils import is_empty
1516
from linkml_runtime.utils.pattern import PatternResolver
1617
from linkml_runtime.linkml_model.meta import *
1718
from linkml_runtime.exceptions import OrderingError
@@ -1369,7 +1370,10 @@ def induced_slot(self, slot_name: SLOT_NAME, class_name: CLASS_NAME = None, impo
13691370
if v2 is not None:
13701371
v = COMBINE[metaslot_name](v, v2)
13711372
else:
1372-
if v2 is not None:
1373+
# can rewrite below as:
1374+
# 1. if v2:
1375+
# 2. if v2 is not None and ((isinstance(v2, (dict, list)) and v2) or (isinstance(v2, JsonObj) and as_dict(v2)))
1376+
if not is_empty(v2):
13731377
v = v2
13741378
logging.debug(f'{v} takes precedence over {v2} for {induced_slot.name}.{metaslot_name}')
13751379
if v is None:
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
id: DJControllerSchema
2+
name: DJControllerSchema
3+
title: LinkML schema for my DJ controller
4+
imports:
5+
- linkml:types
6+
classes:
7+
DJController:
8+
slots:
9+
- jog_wheels
10+
- tempo
11+
- volume_faders
12+
- crossfaders
13+
slot_usage:
14+
tempo:
15+
examples:
16+
- value: 120.0
17+
- value: 144.0
18+
- value: 126.8
19+
- value: 102.6
20+
annotations:
21+
expected_value: a number between 0 and 300
22+
preferred_unit: BPM
23+
slots:
24+
jog_wheels:
25+
description: The number of jog wheels on the DJ controller
26+
range: integer
27+
examples:
28+
- value: 2
29+
annotations:
30+
expected_value: an integer between 0 and 4
31+
in_subset: decks
32+
tempo:
33+
description: The tempo of the track (in BPM)
34+
range: float
35+
examples:
36+
- value: 120.0
37+
- value: 144.0
38+
annotations:
39+
expected_value: a number between 0 and 200
40+
preferred_unit: BPM
41+
in_subset: decks
42+
volume_faders:
43+
description: The number of volume faders on the DJ controller
44+
range: integer
45+
examples:
46+
- value: 4
47+
annotations:
48+
expected_value: an integer between 0 and 8
49+
in_subset: mixer
50+
crossfaders:
51+
description: The number of crossfaders on the DJ controller
52+
range: integer
53+
examples:
54+
- value: 1
55+
annotations:
56+
expected_value: an integer between 0 and 2
57+
in_subset: mixer
58+
subsets:
59+
decks:
60+
description: A subset that represents the components in the deck portion of a DJ controller
61+
mixer:
62+
description: A subset that represents the components in the mixer portion of a DJ controller

tests/test_issues/test_issue_2224.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import unittest
2+
from unittest import TestCase
3+
from linkml_runtime.utils.schemaview import SchemaView
4+
from jsonasobj2 import JsonObj
5+
6+
from tests.test_issues.environment import env
7+
8+
9+
class Issue2224TestCase(TestCase):
10+
env = env
11+
12+
def test_issue_2224_slot_classes(self):
13+
sv = SchemaView(env.input_path("linkml_issue_2224.yaml"))
14+
cls = sv.induced_class("DJController")
15+
16+
# jog_wheels is a slot asserted at the schema level
17+
# check that the range (scalar value) is being materialized properly
18+
self.assertEqual(cls.attributes["jog_wheels"].range, "integer")
19+
# check that the examples (list) is being materialized properly
20+
self.assertIsInstance(cls.attributes["jog_wheels"].examples, list)
21+
for example in cls.attributes["jog_wheels"].examples:
22+
self.assertEqual(example.value, "2")
23+
for example in cls.attributes["volume_faders"].examples:
24+
self.assertEqual(example.value, "4")
25+
for example in cls.attributes["crossfaders"].examples:
26+
self.assertEqual(example.value, "1")
27+
# check that the annotations (dictionary) is being materialized properly
28+
self.assertIsInstance(cls.attributes["jog_wheels"].annotations, JsonObj)
29+
self.assertEqual(
30+
cls.attributes["jog_wheels"].annotations.expected_value.value,
31+
"an integer between 0 and 4",
32+
)
33+
self.assertEqual(
34+
cls.attributes["volume_faders"].annotations.expected_value.value,
35+
"an integer between 0 and 8",
36+
)
37+
38+
# examples being overriden by slot_usage modification
39+
for example in cls.attributes["tempo"].examples:
40+
self.assertIn(example.value, ["120.0", "144.0", "126.8", "102.6"])
41+
# annotations being overriden by slot_usage modification
42+
self.assertEqual(
43+
cls.attributes["tempo"].annotations.expected_value.value,
44+
"a number between 0 and 300",
45+
)
46+
self.assertEqual(
47+
cls.attributes["tempo"].annotations.preferred_unit.value, "BPM"
48+
)
49+
50+
51+
if __name__ == "__main__":
52+
unittest.main()

0 commit comments

Comments
 (0)