Skip to content

Commit 7633382

Browse files
Merge pull request #146 from wfcommons/cwl_stdout_stderr
.out/.err files are now produced optionally by the CWL and Streamflow translators
2 parents 7328279 + 318da88 commit 7633382

4 files changed

Lines changed: 86 additions & 31 deletions

File tree

tests/translators_loggers/test_translators_loggers.py

Lines changed: 1 addition & 0 deletions
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",

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 & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +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"; #OPTIONAL_STDOUT_FILE
19+
cmd = cmd + " ; echo '-- end of stderr for " + inputs.step_name + " --' >> " + runtime.outdir + "/" + inputs.step_name + ".err"; #OPTIONAL_STDERR_FILE
1820
return cmd;
1921
}
2022
shellQuote: false
@@ -30,14 +32,14 @@ inputs:
3032
type: string
3133

3234
outputs:
33-
out:
34-
type: File
35-
outputBinding:
36-
glob: $(inputs.step_name + ".out")
37-
err:
38-
type: File
39-
outputBinding:
40-
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
4143
output_files:
4244
type: File[]
4345
outputBinding:

0 commit comments

Comments
 (0)