Skip to content

Commit fee2360

Browse files
committed
Fix integer order_index step keys in native workflow loading
Galaxy `WorkflowContentsManager._workflow_to_dict_export()` serializes workflow steps using the integer ``order_index`` as the dict key (e.g. ``{0: {...}, 1: {...}}``), but ``NativeGalaxyWorkflow.steps`` is typed as ``dict[str, NativeStep]``. Pydantic v2 does not coerce integer keys to strings, so ``NativeGalaxyWorkflow.model_validate()`` raised a ``ValidationError`` whenever ``load_native()`` / ``from_galaxy_native()`` was called on a workflow dict produced directly by Galaxy (e.g. during export or invocation download). Fix: stringify every step key in ``_normalize_native_for_validation`` before the model is validated, so both lax and strict paths see string keys (strict still rejects them because the coercion happens after the strict-vs-lax branch).
1 parent d53403a commit fee2360

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

gxformat2/normalized/_native.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ def _normalize_native_for_validation(data: dict[str, Any]) -> dict[str, Any]:
216216
for key, step in data["steps"].items():
217217
if isinstance(step, dict):
218218
step = _normalize_step_for_validation(step)
219-
steps[key] = step
219+
steps[str(key)] = step # Galaxy may use integer order_index as keys
220220
data["steps"] = steps
221221
return data
222222

tests/test_load_native.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ def test_valid_tags_accepted(self):
3131
result = load_native(wf, strict=True)
3232
assert result.tags == ["a", "b"]
3333

34+
def test_integer_step_keys_rejected(self):
35+
wf = {
36+
**MINIMAL_WORKFLOW,
37+
"steps": {
38+
0: {
39+
"id": 0,
40+
"type": "data_input",
41+
},
42+
},
43+
}
44+
with pytest.raises(ValidationError, match="Input should be a valid string"):
45+
load_native(wf, strict=True)
46+
3447

3548
class TestLoadNativeLax:
3649
"""Lax mode normalizes known quirks."""
@@ -127,6 +140,20 @@ def test_action_arguments_dict_unchanged(self):
127140
pja = result.steps["0"].post_job_actions["RenameOut"]
128141
assert pja.action_arguments == {"newname": "renamed"}
129142

143+
def test_integer_step_keys_normalized_to_strings(self):
144+
"""Galaxy serializes steps with integer order_index keys; lax mode converts them to strings."""
145+
wf = {
146+
**MINIMAL_WORKFLOW,
147+
"steps": {
148+
0: {
149+
"id": 0,
150+
"type": "data_input",
151+
},
152+
},
153+
}
154+
result = load_native(wf, strict=False)
155+
assert set(result.steps.keys()) == {"0"}
156+
130157
def test_does_not_mutate_input(self):
131158
wf = {**MINIMAL_WORKFLOW, "tags": "a,b"}
132159
original_tags = wf["tags"]

0 commit comments

Comments
 (0)