Skip to content

Commit 6af9932

Browse files
committed
Single-step: inline and deeply nested sub-workflows
--print-targets now correctly prefixes sub-workflow steps
1 parent 528d31c commit 6af9932

File tree

4 files changed

+240
-8
lines changed

4 files changed

+240
-8
lines changed

cwltool/main.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,9 @@ def print_targets(
959959
process = make_tool(cast(CommentedMap, cmap(run)), loading_context)
960960
else:
961961
process = run
962-
print_targets(process, stdout, loading_context, shortname(t["id"]) + "/")
962+
print_targets(
963+
process, stdout, loading_context, f"{prefix}{shortname(t['id'])}/"
964+
)
963965

964966

965967
def main(

cwltool/subgraph.py

+25-7
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from ruamel.yaml.comments import CommentedMap, CommentedSeq
1818

1919
from .context import LoadingContext
20-
from .load_tool import load_tool
20+
from .load_tool import load_tool, make_tool
2121
from .process import Process
2222
from .utils import CWLObjectType, aslist
2323
from .workflow import Workflow, WorkflowStep
@@ -64,18 +64,19 @@ def find_step(
6464
) -> Tuple[Optional[CWLObjectType], Optional[WorkflowStep]]:
6565
"""Find the step (raw dictionary and WorkflowStep) for a given step id."""
6666
for st in steps:
67-
if st.tool["id"] == stepid:
67+
st_tool_id = st.tool["id"]
68+
if st_tool_id == stepid:
6869
return st.tool, st
69-
if stepid.startswith(st.tool["id"]):
70-
run: Union[str, Process] = st.tool["run"]
70+
if stepid.startswith(st_tool_id):
71+
run: Union[str, Process, CWLObjectType] = st.tool["run"]
7172
if isinstance(run, Workflow):
7273
result, st2 = find_step(
7374
run.steps, stepid[len(st.tool["id"]) + 1 :], loading_context
7475
)
7576
if result:
7677
return result, st2
77-
elif isinstance(run, str):
78-
process = load_tool(run, loading_context)
78+
elif isinstance(run, CommentedMap) and run["class"] == "Workflow":
79+
process = make_tool(run, loading_context)
7980
if isinstance(process, Workflow):
8081
suffix = stepid[len(st.tool["id"]) + 1 :]
8182
prefix = process.tool["id"]
@@ -84,9 +85,26 @@ def find_step(
8485
else:
8586
sep = "#"
8687
adj_stepid = f"{prefix}{sep}{suffix}"
87-
result2, st3 = find_step(process.steps, adj_stepid, loading_context)
88+
result2, st3 = find_step(
89+
process.steps,
90+
adj_stepid,
91+
loading_context,
92+
)
8893
if result2:
8994
return result2, st3
95+
elif isinstance(run, str):
96+
process = load_tool(run, loading_context)
97+
if isinstance(process, Workflow):
98+
suffix = stepid[len(st.tool["id"]) + 1 :]
99+
prefix = process.tool["id"]
100+
if "#" in prefix:
101+
sep = "/"
102+
else:
103+
sep = "#"
104+
adj_stepid = f"{prefix}{sep}{suffix}"
105+
result3, st4 = find_step(process.steps, adj_stepid, loading_context)
106+
if result3:
107+
return result3, st4
90108
return None, None
91109

92110

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
{
2+
"class": "Workflow",
3+
"cwlVersion": "v1.2",
4+
"id": "count-lines17-wf",
5+
"inputs": [
6+
{
7+
"id": "file1",
8+
"type": "File"
9+
}
10+
],
11+
"outputs": [
12+
{
13+
"id": "count_output",
14+
"outputSource": "step1/count_output",
15+
"type": "int"
16+
}
17+
],
18+
"requirements": [
19+
{
20+
"class": "SubworkflowFeatureRequirement"
21+
},
22+
{
23+
"class": "InlineJavascriptRequirement"
24+
}
25+
],
26+
"steps": [
27+
{
28+
"id": "step1",
29+
"in": [
30+
{
31+
"id": "file1",
32+
"source": "file1"
33+
}
34+
],
35+
"out": [
36+
"count_output"
37+
],
38+
"run": {
39+
"class": "Workflow",
40+
"id": "count-lines17-wf.cwl@step_step1@run",
41+
"inputs": [
42+
{
43+
"id": "file1",
44+
"type": "File"
45+
}
46+
],
47+
"outputs": [
48+
{
49+
"id": "count_output",
50+
"outputSource": "stepZ/output",
51+
"type": "int"
52+
}
53+
],
54+
"requirements": [
55+
{
56+
"class": "SubworkflowFeatureRequirement"
57+
},
58+
{
59+
"class": "InlineJavascriptRequirement"
60+
}
61+
],
62+
"steps": [
63+
{
64+
"id": "stepX",
65+
"in": [
66+
{
67+
"id": "file1",
68+
"source": "file1"
69+
}
70+
],
71+
"out": [
72+
"wc_output"
73+
],
74+
"run": {
75+
"class": "Workflow",
76+
"id": "count-lines17-wf.cwl@step_step1@run@step_stepX@run",
77+
"inputs": [
78+
{
79+
"id": "file1",
80+
"type": "File"
81+
}
82+
],
83+
"outputs": [
84+
{
85+
"id": "wc_output",
86+
"outputSource": "stepY/output",
87+
"type": "File"
88+
}
89+
],
90+
"requirements": [
91+
{
92+
"class": "SubworkflowFeatureRequirement"
93+
},
94+
{
95+
"class": "InlineJavascriptRequirement"
96+
}
97+
],
98+
"steps": [
99+
{
100+
"id": "stepY",
101+
"in": [
102+
{
103+
"id": "file1",
104+
"source": "file1"
105+
}
106+
],
107+
"out": [
108+
"output"
109+
],
110+
"run": {
111+
"baseCommand": [
112+
"wc",
113+
"-l"
114+
],
115+
"class": "CommandLineTool",
116+
"id": "count-lines17-wf.cwl@step_step1@run@step_stepX@run@step_stepY@run",
117+
"inputs": [
118+
{
119+
"id": "file1",
120+
"type": "File"
121+
}
122+
],
123+
"outputs": [
124+
{
125+
"id": "output",
126+
"outputBinding": {
127+
"glob": "output"
128+
},
129+
"type": "File"
130+
}
131+
],
132+
"requirements": [
133+
{
134+
"class": "InlineJavascriptRequirement"
135+
}
136+
],
137+
"stdin": "$(inputs.file1.path)",
138+
"stdout": "output"
139+
}
140+
}
141+
]
142+
}
143+
},
144+
{
145+
"id": "stepZ",
146+
"in": [
147+
{
148+
"id": "file1",
149+
"source": "stepX/wc_output"
150+
}
151+
],
152+
"out": [
153+
"output"
154+
],
155+
"run": {
156+
"class": "ExpressionTool",
157+
"expression": "$({'output': parseInt(inputs.file1.contents)})",
158+
"id": "count-lines17-wf.cwl@step_step1@run@step_stepZ@run",
159+
"inputs": [
160+
{
161+
"id": "file1",
162+
"loadContents": true,
163+
"type": "File"
164+
}
165+
],
166+
"outputs": [
167+
{
168+
"id": "output",
169+
"type": "int"
170+
}
171+
],
172+
"requirements": [
173+
{
174+
"class": "InlineJavascriptRequirement"
175+
}
176+
]
177+
}
178+
}
179+
]
180+
}
181+
}
182+
]
183+
}

tests/test_subgraph.py

+29
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,23 @@ def test_single_process_packed_subwf_step() -> None:
224224
)
225225

226226

227+
def test_single_process_subwf_subwf_inline_step() -> None:
228+
"""Test --single-process on an inline sub-sub-workflow step."""
229+
err_code, stdout, stderr = get_main_output(
230+
[
231+
"--single-process",
232+
"step1/stepX/stepY",
233+
get_data("tests/subgraph/count-lines17-wf.cwl.json"),
234+
get_data("tests/wf/wc-job.json"),
235+
]
236+
)
237+
assert err_code == 0
238+
assert (
239+
json.loads(stdout)["output"]["checksum"]
240+
== "sha1$3596ea087bfdaf52380eae441077572ed289d657"
241+
)
242+
243+
227244
def test_single_step_subwf_step() -> None:
228245
"""Inherit reqs and hints --single-step on sub-workflow step."""
229246
err_code, stdout, stderr = get_main_output(
@@ -308,3 +325,15 @@ def test_print_targets_embedded_reqsinherit() -> None:
308325
]
309326
)
310327
assert err_code == 0
328+
329+
330+
def test_print_targets_embedded_sub_subwfs() -> None:
331+
"""Confirm that --print-targets works with inline sub-sub-workflows."""
332+
err_code, stdout, stderr = get_main_output(
333+
[
334+
"--print-targets",
335+
get_data("tests/subgraph/count-lines17-wf.cwl.json"),
336+
]
337+
)
338+
assert err_code == 0
339+
assert "step1/stepX/stepY" in stdout

0 commit comments

Comments
 (0)