Skip to content

Commit 3c424ad

Browse files
committed
Merge origin/mlin/wdl-1.2-stdlib - add values() function
Resolved conflicts in tests/test_5stdlib.py: - Kept all test_keys() tests including struct support and version gating - Added test_values() function for WDL 1.2 values() implementation - Both functions now properly tested
2 parents c54d9a3 + a336d9c commit 3c424ad

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

WDL/StdLib.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ def sep(sep: Value.String, iterable: Value.Array) -> Value.String:
151151
if self.wdl_version not in ["draft-2", "1.0", "1.1"]:
152152
# WDL 1.2+ functions
153153
self.contains = _Contains()
154+
self.values = _Values()
154155

155156
def _read(self, parse: Callable[[str], Value.Base]) -> Callable[[Value.File], Value.Base]:
156157
"generate read_* function implementation based on parse"
@@ -1129,6 +1130,31 @@ def _call_eager(self, expr: "Expr.Apply", arguments: List[Value.Base]) -> Value.
11291130
raise Error.EvalError(expr, f"keys() received unexpected argument type: {type(arg)}")
11301131

11311132

1133+
class _Values(EagerFunction):
1134+
# Array[Y] values(Map[P, Y])
1135+
# Returns an array of values from a Map
1136+
1137+
def infer_type(self, expr: "Expr.Apply") -> Type.Base:
1138+
if len(expr.arguments) != 1:
1139+
raise Error.WrongArity(expr, 1)
1140+
arg0ty = expr.arguments[0].type
1141+
if not isinstance(arg0ty, Type.Map) or (expr._check_quant and arg0ty.optional):
1142+
raise Error.StaticTypeMismatch(
1143+
expr.arguments[0], Type.Map((Type.Any(), Type.Any())), arg0ty
1144+
)
1145+
# For Map[P, Y], return Array[Y]
1146+
return Type.Array(arg0ty.item_type[1].copy())
1147+
1148+
def _call_eager(self, expr: "Expr.Apply", arguments: List[Value.Base]) -> Value.Base:
1149+
assert isinstance(arguments[0], Value.Map)
1150+
mapty = arguments[0].type
1151+
assert isinstance(mapty, Type.Map)
1152+
# Return the values (p[1]) from the map
1153+
return Value.Array(
1154+
mapty.item_type[1], [p[1].coerce(mapty.item_type[1]) for p in arguments[0].value], expr
1155+
)
1156+
1157+
11321158
class _AsPairs(EagerFunction):
11331159
def infer_type(self, expr: "Expr.Apply") -> Type.Base:
11341160
if len(expr.arguments) != 1:

tests/test_5stdlib.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,6 +2072,108 @@ def test_keys(self):
20722072
# that may be hit during coercion from read_json, though it's hard to isolate in testing.
20732073
# The runtime error path (unexpected argument type) should be prevented by static type checking.
20742074

2075+
def test_values(self):
2076+
"""Test the values() function from WDL 1.2"""
2077+
2078+
# Basic functionality with different value types
2079+
outputs = self._test_task(R"""
2080+
version 1.2
2081+
task test_values {
2082+
input {
2083+
Map[String, Int] m1 = {"a": 1, "b": 2, "c": 3}
2084+
Map[Int, String] m2 = {1: "one", 2: "two"}
2085+
Map[String, Pair[Int, Int]] m3 = {
2086+
"a": (1, 2),
2087+
"b": (3, 4)
2088+
}
2089+
}
2090+
command {}
2091+
output {
2092+
Array[Int] v1 = values(m1)
2093+
Array[String] v2 = values(m2)
2094+
Array[Pair[Int, Int]] v3 = values(m3)
2095+
Array[Boolean] v4 = values({})
2096+
}
2097+
}
2098+
""")
2099+
self.assertEqual(outputs["v1"], [1, 2, 3])
2100+
self.assertEqual(outputs["v2"], ["one", "two"])
2101+
self.assertEqual(outputs["v3"], [{"left": 1, "right": 2}, {"left": 3, "right": 4}])
2102+
self.assertEqual(outputs["v4"], [])
2103+
2104+
# Complex nested types
2105+
outputs = self._test_task(R"""
2106+
version 1.2
2107+
task test_values_complex {
2108+
input {
2109+
Map[String, Array[Int]] nested = {
2110+
"x": [1, 2],
2111+
"y": [3, 4, 5]
2112+
}
2113+
}
2114+
command {}
2115+
output {
2116+
Array[Array[Int]] vals = values(nested)
2117+
}
2118+
}
2119+
""")
2120+
self.assertEqual(outputs["vals"], [[1, 2], [3, 4, 5]])
2121+
2122+
# Error: wrong arity (too few arguments)
2123+
self._test_task(R"""
2124+
version 1.2
2125+
task bad {
2126+
command {}
2127+
output {
2128+
Array[Int] x = values()
2129+
}
2130+
}
2131+
""", expected_exception=WDL.Error.WrongArity)
2132+
2133+
# Error: wrong arity (too many arguments)
2134+
self._test_task(R"""
2135+
version 1.2
2136+
task bad {
2137+
command {}
2138+
output {
2139+
Array[Int] x = values({"a": 1}, {"b": 2})
2140+
}
2141+
}
2142+
""", expected_exception=WDL.Error.WrongArity)
2143+
2144+
# Error: not available in WDL 1.1
2145+
self._test_task(R"""
2146+
version 1.1
2147+
task bad {
2148+
command {}
2149+
output {
2150+
Array[Int] x = values({"a": 1})
2151+
}
2152+
}
2153+
""", expected_exception=WDL.Error.NoSuchFunction)
2154+
2155+
# Error: not available in WDL 1.0
2156+
self._test_task(R"""
2157+
version 1.0
2158+
task bad {
2159+
command {}
2160+
output {
2161+
Array[Int] x = values({"a": 1})
2162+
}
2163+
}
2164+
""", expected_exception=WDL.Error.NoSuchFunction)
2165+
2166+
# Error: first argument not a map
2167+
self._test_task(R"""
2168+
version 1.2
2169+
task bad {
2170+
command {}
2171+
output {
2172+
Array[Int] x = values([1, 2, 3])
2173+
}
2174+
}
2175+
""", expected_exception=WDL.Error.StaticTypeMismatch)
2176+
20752177
def test_map_pairs(self):
20762178
outputs = self._test_task(R"""
20772179
version development

0 commit comments

Comments
 (0)