Skip to content

Commit f24166a

Browse files
committed
Made .out and .err file generation option for the CWL and Streamflow
translators
1 parent 47fc44e commit f24166a

4 files changed

Lines changed: 87 additions & 34 deletions

File tree

tests/translators_loggers/test_translators_loggers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ def run_workflow_cwl(container, num_tasks, str_dirpath):
186186
# Note that the input file is hardcoded and Blast-specific
187187
exit_code, output = container.exec_run(cmd="cwltool ./main.cwl --split_fasta_00000001_input ./data/workflow_infile_0001 ",
188188
user="wfcommons", stdout=True, stderr=True)
189+
# print(output.decode())
189190
# Check sanity
190191
assert (exit_code == 0)
191192
# this below is ugly (the 3 is for "workflow", "compile_output_files" and "compile_log_files",
@@ -298,7 +299,7 @@ def test_translator(self, backend) -> None:
298299
if backend == "nextflow_subworkflow":
299300
translator = translator_classes[backend](benchmark.workflow, use_subworkflows=True, max_tasks_per_subworkflow=10)
300301
else:
301-
translator = translator_classes[backend](benchmark.workflow)
302+
translator = translator_classes[backend](benchmark.workflow, generate_stderr_files=False)
302303
translator.translate(output_folder=dirpath)
303304

304305
# Make the directory that holds the translation world-writable,

wfcommons/wfbench/translator/cwl.py

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,17 @@ class CWLTranslator(Translator):
2828
2929
:param workflow: Workflow benchmark object or path to the workflow benchmark JSON instance.
3030
:type workflow: Union[Workflow, pathlib.Path],
31+
:param generate_stdout_files: If true, each step will generate a .out file with stdout from the step's execution
32+
:type generate_stdout_files: Optional[bool]
33+
:param generate_stderr_files: If true, each step will generate a .err file with stderr from the step's execution
34+
:type generate_stderr_files: Optional[bool]
3135
:param logger: The logger where to log information/warning or errors (optional).
3236
:type logger: Logger
3337
"""
3438
def __init__(self,
3539
workflow: Union[Workflow, pathlib.Path],
40+
generate_stdout_files: Optional[bool] = True,
41+
generate_stderr_files: Optional[bool] = True,
3642
logger: Optional[logging.Logger] = None) -> None:
3743
super().__init__(workflow, logger)
3844
self.cwl_script = ["cwlVersion: v1.0",
@@ -41,6 +47,10 @@ def __init__(self,
4147
" MultipleInputFeatureRequirement: {}",
4248
" StepInputExpressionRequirement: {}",
4349
" InlineJavascriptRequirement: {}\n"]
50+
51+
self.generate_stdout_files : bool = (generate_stdout_files is None) or generate_stdout_files
52+
self.generate_stderr_files : bool = (generate_stderr_files is None) or generate_stderr_files
53+
4454
self.yml_script = []
4555
self.parsed_tasks = []
4656
self.task_level_map = defaultdict(lambda: [])
@@ -147,13 +157,23 @@ def _parse_steps(self) -> None:
147157
" step_name:",
148158
f" valueFrom: \"{task.task_id}\"",
149159
f" output_filenames: {{default: {output_files}}}",
150-
" out: [out, err, output_files]\n"
160+
" out: [" # Completed below
151161
]
162+
# Add stdout file?
163+
if self.generate_stdout_files:
164+
code[-1] += "out, "
165+
# Add stderr file?
166+
if self.generate_stderr_files:
167+
code[-1] += "err, "
168+
# Always add output files
169+
code[-1] += "output_files]\n"
152170

153171
self.cwl_script.extend(code)
154172
output_files_sources.append(f" - {task.task_id}/output_files")
155-
log_files_sources.append(f" - {task.task_id}/out")
156-
log_files_sources.append(f" - {task.task_id}/err")
173+
if self.generate_stdout_files:
174+
log_files_sources.append(f" - {task.task_id}/out")
175+
if self.generate_stderr_files:
176+
log_files_sources.append(f" - {task.task_id}/err")
157177

158178
code = [
159179
" compile_output_files:",
@@ -172,22 +192,24 @@ def _parse_steps(self) -> None:
172192
" out: [out]\n"
173193
]
174194

175-
code += [
176-
" compile_log_files:",
177-
" run: clt/folder.cwl",
178-
" in:",
179-
" - id: name",
180-
" valueFrom: \"logs\"",
181-
" - id: item",
182-
" linkMerge: merge_flattened",
183-
" source:",
184-
]
185-
186-
code += log_files_sources
187-
188-
code += [
189-
" out: [out]\n"
190-
]
195+
# Only deal with log files if there are any
196+
if len(log_files_sources) > 0:
197+
code += [
198+
" compile_log_files:",
199+
" run: clt/folder.cwl",
200+
" in:",
201+
" - id: name",
202+
" valueFrom: \"logs\"",
203+
" - id: item",
204+
" linkMerge: merge_flattened",
205+
" source:",
206+
]
207+
208+
code += log_files_sources
209+
210+
code += [
211+
" out: [out]\n"
212+
]
191213

192214
self.cwl_script.extend(code)
193215

@@ -214,10 +236,15 @@ def _parse_inputs_outputs(self) -> None:
214236
code = ["\noutputs:",
215237
" data_folder:",
216238
" type: Directory",
217-
" outputSource: compile_output_files/out",
239+
" outputSource: compile_output_files/out"]
240+
241+
if self.generate_stdout_files or self.generate_stderr_files:
242+
code += [
218243
" log_folder:",
219244
" type: Directory",
220-
" outputSource: compile_log_files/out\n"]
245+
" outputSource: compile_log_files/out"]
246+
247+
code[-1] += "\n"
221248

222249
self.cwl_script.extend(code)
223250

@@ -227,7 +254,22 @@ def _write_cwl_files(self, output_folder: pathlib.Path) -> None:
227254
clt_folder = cwl_folder.joinpath("clt")
228255
clt_folder.mkdir(exist_ok=True)
229256
shutil.copy(this_dir.joinpath("templates/cwl/folder.cwl"), clt_folder)
230-
shutil.copy(this_dir.joinpath("templates/cwl/shell.cwl"), clt_folder)
257+
258+
# Create the shell.cwl file
259+
# shutil.copy(this_dir.joinpath("templates/cwl/shell.cwl"), clt_folder)
260+
updates_shell_cwl = ""
261+
with open(this_dir.joinpath("templates/cwl/shell.cwl"), "r", encoding="utf-8") as f:
262+
for line in f.readlines():
263+
if line.endswith("#OPTIONAL_STDOUT_FILE\n"):
264+
if self.generate_stdout_files:
265+
updates_shell_cwl += line.replace("#OPTIONAL_STDOUT_FILE", "")
266+
elif line.endswith("#OPTIONAL_STDERR_FILE\n"):
267+
if self.generate_stderr_files:
268+
updates_shell_cwl += line.replace("#OPTIONAL_STDERR_FILE", "")
269+
else:
270+
updates_shell_cwl += line
271+
with open(cwl_folder.joinpath(clt_folder / "shell.cwl"), "w", encoding="utf-8") as f:
272+
f.write(updates_shell_cwl)
231273

232274
with open(cwl_folder.joinpath("main.cwl"), "w", encoding="utf-8") as f:
233275
f.write("\n".join(self.cwl_script))

wfcommons/wfbench/translator/streamflow.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,21 @@ class StreamflowTranslator(Translator):
2929
3030
:param workflow: Workflow benchmark object or path to the workflow benchmark JSON instance.
3131
:type workflow: Union[Workflow, pathlib.Path],
32+
:param generate_stdout_files: If true, each CWL step will generate a .out file with stdout from the step's execution
33+
:type generate_stdout_files: Optional[bool]
34+
:param generate_stderr_files: If true, each CWL step will generate a .err file with stderr from the step's execution
35+
:type generate_stderr_files: Optional[bool]
3236
:param logger: The logger where to log information/warning or errors (optional).
3337
:type logger: Logger
3438
"""
3539
def __init__(self,
3640
workflow: Union[Workflow, pathlib.Path],
41+
generate_stdout_files: Optional[bool] = True,
42+
generate_stderr_files: Optional[bool] = True,
3743
logger: Optional[logging.Logger] = None) -> None:
3844
super().__init__(workflow, logger)
45+
self.generate_stdout_files : bool = (generate_stdout_files is None) or generate_stdout_files
46+
self.generate_stderr_files : bool = (generate_stderr_files is None) or generate_stderr_files
3947

4048
def translate(self, output_folder: pathlib.Path) -> None:
4149
"""
@@ -46,7 +54,9 @@ def translate(self, output_folder: pathlib.Path) -> None:
4654
"""
4755
# Perform the CWL translation (which will create the output folder)
4856
from wfcommons.wfbench import CWLTranslator
49-
cwl_translator = CWLTranslator(workflow=self.workflow, logger=self.logger)
57+
cwl_translator = CWLTranslator(workflow=self.workflow, logger=self.logger,
58+
generate_stdout_files=self.generate_stdout_files,
59+
generate_stderr_files=self.generate_stderr_files)
5060
cwl_translator.translate(output_folder)
5161

5262
# Generate the streamflow.yml file

wfcommons/wfbench/translator/templates/cwl/shell.cwl

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ arguments:
1515
}
1616
}
1717
cmd = cmd + " > " + runtime.outdir + "/" + inputs.step_name + ".out 2> " + runtime.outdir + "/" + inputs.step_name + ".err";
18-
cmd = cmd + " ; echo '-- end of stdout for " + inputs.step_name + " --' >> " + runtime.outdir + "/" + inputs.step_name + ".out";
19-
cmd = cmd + " ; echo '-- end of stderr for " + inputs.step_name + " --' >> " + runtime.outdir + "/" + inputs.step_name + ".err";
18+
cmd = cmd + " ; echo '-- end of stdout for " + inputs.step_name + " --' >> " + runtime.outdir + "/" + inputs.step_name + ".out"; #OPTIONAL_STDOUT_FILE
19+
cmd = cmd + " ; echo '-- end of stderr for " + inputs.step_name + " --' >> " + runtime.outdir + "/" + inputs.step_name + ".err"; #OPTIONAL_STDERR_FILE
2020
return cmd;
2121
}
2222
shellQuote: false
@@ -32,14 +32,14 @@ inputs:
3232
type: string
3333

3434
outputs:
35-
out:
36-
type: File
37-
outputBinding:
38-
glob: $(inputs.step_name + ".out")
39-
err:
40-
type: File
41-
outputBinding:
42-
glob: $(inputs.step_name + ".err")
35+
out: #OPTIONAL_STDOUT_FILE
36+
type: File #OPTIONAL_STDOUT_FILE
37+
outputBinding: #OPTIONAL_STDOUT_FILE
38+
glob: $(inputs.step_name + ".out") #OPTIONAL_STDOUT_FILE
39+
err: #OPTIONAL_STDERR_FILE
40+
type: File #OPTIONAL_STDERR_FILE
41+
outputBinding: #OPTIONAL_STDERR_FILE
42+
glob: $(inputs.step_name + ".err") #OPTIONAL_STDERR_FILE
4343
output_files:
4444
type: File[]
4545
outputBinding:

0 commit comments

Comments
 (0)