Skip to content

Commit 0dabecb

Browse files
committed
print-targets/subgraph/single_*: load step process + recurse
and tests
1 parent 8622080 commit 0dabecb

13 files changed

+319
-55
lines changed

cwltool/cwlviewer.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
"""Visualize a CWL workflow."""
2-
import os
32
from pathlib import Path
43
from urllib.parse import urlparse
54

cwltool/main.py

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
fetch_document,
5656
jobloaderctx,
5757
load_overrides,
58+
load_tool,
5859
make_tool,
5960
resolve_and_validate_document,
6061
resolve_overrides,
@@ -776,72 +777,76 @@ def inherit_reqshints(tool: Process, parent: Process) -> None:
776777
found = True
777778
break
778779
if not found:
779-
tool.hints.append(parent_req)
780+
tool.hints.append(parent_hint)
780781

781782

782783
def choose_target(
783784
args: argparse.Namespace,
784785
tool: Process,
785-
loadingContext: LoadingContext,
786+
loading_context: LoadingContext,
786787
) -> Optional[Process]:
787788
"""Walk the Workflow, extract the subset matches all the args.targets."""
788-
if loadingContext.loader is None:
789-
raise Exception("loadingContext.loader cannot be None")
789+
if loading_context.loader is None:
790+
raise Exception("loading_context.loader cannot be None")
790791

791792
if isinstance(tool, Workflow):
792793
url = urllib.parse.urlparse(tool.tool["id"])
793794
if url.fragment:
794795
extracted = get_subgraph(
795-
[tool.tool["id"] + "/" + r for r in args.target], tool
796+
[tool.tool["id"] + "/" + r for r in args.target], tool, loading_context
796797
)
797798
else:
798799
extracted = get_subgraph(
799800
[
800-
loadingContext.loader.fetcher.urljoin(tool.tool["id"], "#" + r)
801+
loading_context.loader.fetcher.urljoin(tool.tool["id"], "#" + r)
801802
for r in args.target
802803
],
803804
tool,
805+
loading_context,
804806
)
805807
else:
806808
_logger.error("Can only use --target on Workflows")
807809
return None
808-
if isinstance(loadingContext.loader.idx, MutableMapping):
809-
loadingContext.loader.idx[extracted["id"]] = extracted
810-
tool = make_tool(extracted["id"], loadingContext)
810+
if isinstance(loading_context.loader.idx, MutableMapping):
811+
loading_context.loader.idx[extracted["id"]] = extracted
812+
tool = make_tool(extracted["id"], loading_context)
811813
else:
812-
raise Exception("Missing loadingContext.loader.idx!")
814+
raise Exception("Missing loading_context.loader.idx!")
813815

814816
return tool
815817

816818

817819
def choose_step(
818820
args: argparse.Namespace,
819821
tool: Process,
820-
loadingContext: LoadingContext,
822+
loading_context: LoadingContext,
821823
) -> Optional[Process]:
822824
"""Walk the given Workflow and extract just args.single_step."""
823-
if loadingContext.loader is None:
824-
raise Exception("loadingContext.loader cannot be None")
825+
if loading_context.loader is None:
826+
raise Exception("loading_context.loader cannot be None")
825827

826828
if isinstance(tool, Workflow):
827829
url = urllib.parse.urlparse(tool.tool["id"])
828830
if url.fragment:
829-
extracted = get_step(tool, tool.tool["id"] + "/" + args.single_step)
831+
extracted = get_step(
832+
tool, tool.tool["id"] + "/" + args.single_step, loading_context
833+
)
830834
else:
831835
extracted = get_step(
832836
tool,
833-
loadingContext.loader.fetcher.urljoin(
837+
loading_context.loader.fetcher.urljoin(
834838
tool.tool["id"], "#" + args.single_step
835839
),
840+
loading_context,
836841
)
837842
else:
838843
_logger.error("Can only use --single-step on Workflows")
839844
return None
840-
if isinstance(loadingContext.loader.idx, MutableMapping):
841-
loadingContext.loader.idx[extracted["id"]] = extracted
842-
tool = make_tool(extracted["id"], loadingContext)
845+
if isinstance(loading_context.loader.idx, MutableMapping):
846+
loading_context.loader.idx[extracted["id"]] = extracted
847+
tool = make_tool(extracted["id"], loading_context)
843848
else:
844-
raise Exception("Missing loadingContext.loader.idx!")
849+
raise Exception("Missing loading_context.loader.idx!")
845850

846851
return tool
847852

@@ -866,7 +871,7 @@ def choose_process(
866871
extracted, step_pos = get_process(
867872
tool,
868873
step_id,
869-
loadingContext.loader.idx,
874+
loadingContext,
870875
)
871876
else:
872877
_logger.error("Can only use --single-process on Workflows")
@@ -909,6 +914,33 @@ def check_working_directories(
909914
return None
910915

911916

917+
def print_targets(
918+
tool: Process,
919+
stdout: Union[TextIO, StreamWriter],
920+
loading_context: LoadingContext,
921+
prefix: str = "",
922+
) -> None:
923+
"""Recursively find targets for --subgraph and friends."""
924+
for f in ("outputs", "inputs"):
925+
if tool.tool[f]:
926+
_logger.info("%s %s%s targets:", prefix[:-1], f[0].upper(), f[1:-1])
927+
stdout.write(
928+
" "
929+
+ "\n ".join([f"{prefix}{shortname(t['id'])}" for t in tool.tool[f]])
930+
+ "\n"
931+
)
932+
if "steps" in tool.tool:
933+
_logger.info("%s steps targets:", prefix[:-1])
934+
for t in tool.tool["steps"]:
935+
stdout.write(f" {prefix}{shortname(t['id'])}\n")
936+
run: Union[str, Process] = t["run"]
937+
if isinstance(run, str):
938+
process = load_tool(run, loading_context)
939+
else:
940+
process = run
941+
print_targets(process, stdout, loading_context, shortname(t["id"]) + "/")
942+
943+
912944
def main(
913945
argsl: Optional[List[str]] = None,
914946
args: Optional[argparse.Namespace] = None,
@@ -1106,14 +1138,7 @@ def main(
11061138
return 0
11071139

11081140
if args.print_targets:
1109-
for f in ("outputs", "steps", "inputs"):
1110-
if tool.tool[f]:
1111-
_logger.info("%s%s targets:", f[0].upper(), f[1:-1])
1112-
stdout.write(
1113-
" "
1114-
+ "\n ".join([shortname(t["id"]) for t in tool.tool[f]])
1115-
+ "\n"
1116-
)
1141+
print_targets(tool, stdout, loadingContext)
11171142
return 0
11181143

11191144
if args.target:

cwltool/subgraph.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
Optional,
1111
Set,
1212
Tuple,
13+
Union,
1314
cast,
1415
)
1516

1617
from ruamel.yaml.comments import CommentedMap
1718

19+
from .context import LoadingContext
20+
from .load_tool import load_tool
1821
from .process import Process
1922
from .utils import CWLObjectType, aslist
2023
from .workflow import Workflow, WorkflowStep
@@ -57,16 +60,38 @@ def declare_node(nodes: Dict[str, Node], nodeid: str, tp: Optional[str]) -> Node
5760

5861

5962
def find_step(
60-
steps: List[WorkflowStep], stepid: str
63+
steps: List[WorkflowStep], stepid: str, loading_context: LoadingContext
6164
) -> Tuple[Optional[CWLObjectType], int]:
6265
"""Find the step and step_index for a given step id."""
6366
for index, st in enumerate(steps):
6467
if st.tool["id"] == stepid:
6568
return st.tool, index
69+
if stepid.startswith(st.tool["id"]):
70+
run: Union[str, Process] = st.tool["run"]
71+
if isinstance(run, Workflow):
72+
result, index = find_step(
73+
run.steps, stepid[len(st.tool["id"]) + 1 :], loading_context
74+
)
75+
if result:
76+
return result, index
77+
elif isinstance(run, str):
78+
process = load_tool(run, loading_context)
79+
if isinstance(process, Workflow):
80+
suffix = stepid[len(st.tool["id"]) + 1 :]
81+
prefix = process.tool["id"]
82+
adj_stepid = f"{prefix}#{suffix}"
83+
result, index = find_step(
84+
process.steps, adj_stepid, loading_context
85+
)
86+
if result:
87+
return result, index
6688
return None, -1
6789

6890

69-
def get_subgraph(roots: MutableSequence[str], tool: Workflow) -> CommentedMap:
91+
def get_subgraph(
92+
roots: MutableSequence[str], tool: Workflow, loading_context: LoadingContext
93+
) -> CommentedMap:
94+
"""Extract the subgraph for the given roots."""
7095
if tool.tool["class"] != "Workflow":
7196
raise Exception("Can only extract subgraph from workflow")
7297

@@ -128,7 +153,7 @@ def get_subgraph(roots: MutableSequence[str], tool: Workflow) -> CommentedMap:
128153
df = urllib.parse.urldefrag(u)
129154
rn = str(df[0] + "#" + df[1].replace("/", "_"))
130155
if nodes[v].type == STEP:
131-
wfstep = find_step(tool.steps, v)[0]
156+
wfstep = find_step(tool.steps, v, loading_context)[0]
132157
if wfstep is not None:
133158
for inp in cast(
134159
MutableSequence[CWLObjectType], wfstep["inputs"]
@@ -169,11 +194,13 @@ def get_subgraph(roots: MutableSequence[str], tool: Workflow) -> CommentedMap:
169194
return extracted
170195

171196

172-
def get_step(tool: Workflow, step_id: str) -> CommentedMap:
173-
197+
def get_step(
198+
tool: Workflow, step_id: str, loading_context: LoadingContext
199+
) -> CommentedMap:
200+
"""Extract a single WorkflowStep for the given step_id."""
174201
extracted = CommentedMap()
175202

176-
step = find_step(tool.steps, step_id)[0]
203+
step = find_step(tool.steps, step_id, loading_context)[0]
177204
if step is None:
178205
raise Exception(f"Step {step_id} was not found")
179206

@@ -202,17 +229,19 @@ def get_step(tool: Workflow, step_id: str) -> CommentedMap:
202229

203230

204231
def get_process(
205-
tool: Workflow, step_id: str, index: Mapping[str, Any]
232+
tool: Workflow, step_id: str, loading_context: LoadingContext
206233
) -> Tuple[Any, int]:
207234
"""Find the underlying Process for a given Workflow step id."""
208-
step, step_pos = find_step(tool.steps, step_id)
235+
if loading_context.loader is None:
236+
raise Exception("loadingContext.loader cannot be None")
237+
step, step_pos = find_step(tool.steps, step_id, loading_context)
209238
if step is None:
210239
raise Exception(f"Step {step_id} was not found")
211240

212-
run = step["run"]
241+
run: Union[str, Any] = step["run"]
213242

214243
if isinstance(run, str):
215-
process = index[run]
244+
process = loading_context.loader.idx[run]
216245
else:
217246
process = run
218247
return process, step_pos

cwltool/workflow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from schema_salad.sourceline import SourceLine, indent
2121

2222
from . import command_line_tool, context, procgenerator
23-
from .checker import static_checker, circular_dependency_checker
23+
from .checker import circular_dependency_checker, static_checker
2424
from .context import LoadingContext, RuntimeContext, getdefault
2525
from .errors import WorkflowException
2626
from .load_tool import load_tool
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class: CommandLineTool
2+
cwlVersion: v1.2
3+
inputs:
4+
in: string
5+
outputs:
6+
out:
7+
type: File
8+
outputBinding:
9+
glob: out
10+
11+
baseCommand: ["/bin/sh", "-c", "echo $TEST_ENV"]
12+
13+
stdout: out

tests/subgraph/env-tool2_req.cwl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class: CommandLineTool
2+
cwlVersion: v1.2
3+
inputs:
4+
in: string
5+
outputs:
6+
out:
7+
type: File
8+
outputBinding:
9+
glob: out
10+
11+
requirements:
12+
EnvVarRequirement:
13+
envDef:
14+
TEST_ENV: $(inputs.in)
15+
16+
baseCommand: ["/bin/sh", "-c", "echo $TEST_ENV"]
17+
18+
stdout: out
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env cwl-runner
2+
class: Workflow
3+
cwlVersion: v1.2
4+
5+
inputs:
6+
in: string
7+
8+
outputs:
9+
out:
10+
type: File
11+
outputSource: step1/out
12+
13+
hints:
14+
EnvVarRequirement:
15+
envDef:
16+
TEST_ENV: override
17+
18+
steps:
19+
step1:
20+
run: env-tool2.cwl
21+
in:
22+
in: in
23+
out: [out]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env cwl-runner
2+
class: Workflow
3+
cwlVersion: v1.2
4+
5+
inputs:
6+
in: string
7+
8+
outputs:
9+
out:
10+
type: File
11+
outputSource: step1/out
12+
13+
hints:
14+
EnvVarRequirement:
15+
envDef:
16+
TEST_ENV: override
17+
18+
steps:
19+
step1:
20+
run: env-tool2_req.cwl
21+
in:
22+
in: in
23+
out: [out]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env cwl-runner
2+
class: Workflow
3+
cwlVersion: v1.2
4+
5+
inputs:
6+
in: string
7+
8+
outputs:
9+
out:
10+
type: File
11+
outputSource: step1/out
12+
13+
hints:
14+
EnvVarRequirement:
15+
envDef:
16+
TEST_ENV: override-from-parent-hint
17+
18+
steps:
19+
step1:
20+
run: env-tool2_no_env.cwl
21+
in:
22+
in: in
23+
out: [out]

0 commit comments

Comments
 (0)