Skip to content

Commit 5cca0f9

Browse files
authored
Skip recursive map_object for scalars with no range
1 parent b840a70 commit 5cca0f9

2 files changed

Lines changed: 95 additions & 0 deletions

File tree

src/linkml_map/transformer/object_transformer.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,11 @@ def _map_value_by_range(
596596
if source_class_slot.multivalued and isinstance(v, list):
597597
return [self.transform_enum(v1, any_of_enums, source_obj) for v1 in v]
598598
return self.transform_enum(v, any_of_enums, source_obj)
599+
# No range and no any_of enums: nothing to recurse into for scalars
600+
if not isinstance(v, (dict, list)):
601+
return v
602+
if isinstance(v, list) and all(not isinstance(v1, (dict, list)) for v1 in v):
603+
return v
599604

600605
if source_class_slot.multivalued:
601606
if isinstance(v, list):
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""Regression test: scalar slots with no explicit range should not emit warnings.
2+
3+
When a source slot has no range (and no default_range), _map_value_by_range
4+
should short-circuit for scalar and multivalued scalar values instead of
5+
falling through to map_object, which would log "Unexpected: <value>" warnings.
6+
7+
See: https://github.com/linkml/linkml-map/pull/173
8+
"""
9+
10+
import textwrap
11+
12+
from linkml_runtime import SchemaView
13+
14+
from linkml_map.transformer.object_transformer import ObjectTransformer
15+
16+
SOURCE_SCHEMA = textwrap.dedent("""\
17+
id: https://example.org/source
18+
name: source
19+
prefixes:
20+
linkml: https://w3id.org/linkml/
21+
imports:
22+
- linkml:types
23+
24+
classes:
25+
Record:
26+
attributes:
27+
id:
28+
identifier: true
29+
tag: {}
30+
tags:
31+
multivalued: true
32+
""")
33+
34+
TARGET_SCHEMA = textwrap.dedent("""\
35+
id: https://example.org/target
36+
name: target
37+
prefixes:
38+
linkml: https://w3id.org/linkml/
39+
imports:
40+
- linkml:types
41+
42+
classes:
43+
Output:
44+
attributes:
45+
id: {}
46+
tag: {}
47+
tags:
48+
multivalued: true
49+
""")
50+
51+
52+
def _make_transformer() -> ObjectTransformer:
53+
"""Build a transformer with schemas that have slots without explicit ranges."""
54+
transform = {
55+
"source_schema": "https://example.org/source",
56+
"target_schema": "https://example.org/target",
57+
"class_derivations": {
58+
"Output": {
59+
"populated_from": "Record",
60+
"slot_derivations": {
61+
"id": {"populated_from": "id"},
62+
"tag": {"populated_from": "tag"},
63+
"tags": {"populated_from": "tags"},
64+
},
65+
}
66+
},
67+
}
68+
tr = ObjectTransformer()
69+
tr.source_schemaview = SchemaView(SOURCE_SCHEMA)
70+
tr.target_schemaview = SchemaView(TARGET_SCHEMA)
71+
tr.create_transformer_specification(transform)
72+
return tr
73+
74+
75+
def test_scalar_no_range_no_warning(caplog):
76+
"""Mapping a scalar slot with no range should not emit an 'Unexpected' warning."""
77+
tr = _make_transformer()
78+
result = tr.map_object({"id": "r1", "tag": "important"}, "Record")
79+
80+
assert result["tag"] == "important"
81+
assert "Unexpected" not in caplog.text
82+
83+
84+
def test_multivalued_scalar_no_range_no_warning(caplog):
85+
"""Mapping a multivalued scalar slot with no range should not emit an 'Unexpected' warning."""
86+
tr = _make_transformer()
87+
result = tr.map_object({"id": "r1", "tags": ["a", "b", "c"]}, "Record")
88+
89+
assert result["tags"] == ["a", "b", "c"]
90+
assert "Unexpected" not in caplog.text

0 commit comments

Comments
 (0)