Skip to content

Commit 12a43ca

Browse files
committed
adding methods to ShellOutputSpec and TaskBase that allows to predic the generated output names before running the task
1 parent ac1c09d commit 12a43ca

File tree

3 files changed

+88
-2
lines changed

3 files changed

+88
-2
lines changed

pydra/engine/core.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,27 @@ def set_state(self, splitter, combiner=None):
311311

312312
@property
313313
def output_names(self):
314-
"""Get the names of the outputs generated by the task."""
314+
"""Get the names of the outputs from the task's output_spec
315+
(not everything has to be generated, see generated_output_names).
316+
"""
315317
return [f.name for f in attr.fields(make_klass(self.output_spec))]
316318

319+
@property
320+
def generated_output_names(self):
321+
""" Get the names of the outputs generated by the task.
322+
If the spec doesn't have generated_output_names method,
323+
it uses output_names.
324+
The results depends on the input provided to the task
325+
"""
326+
output_klass = make_klass(self.output_spec)
327+
if hasattr(output_klass, "generated_output_names"):
328+
output = output_klass(**{f.name: None for f in attr.fields(output_klass)})
329+
return output.generated_output_names(
330+
inputs=self.inputs, output_dir=self.output_dir
331+
)
332+
else:
333+
return self.output_names
334+
317335
@property
318336
def can_resume(self):
319337
"""Whether the task accepts checkpoint-restart."""

pydra/engine/specs.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ def collect_additional_outputs(self, inputs, output_dir):
408408
raise AttributeError(
409409
"File has to have default value or metadata"
410410
)
411-
elif not fld.default == attr.NOTHING:
411+
elif fld.default != attr.NOTHING:
412412
additional_out[fld.name] = self._field_defaultvalue(
413413
fld, output_dir
414414
)
@@ -420,6 +420,34 @@ def collect_additional_outputs(self, inputs, output_dir):
420420
raise Exception("not implemented (collect_additional_output)")
421421
return additional_out
422422

423+
def generated_output_names(self, inputs, output_dir):
424+
""" Returns a list of all outputs that will be generated by the task.
425+
Takes into account the task input and the requires list for the output fields.
426+
TODO: should be in all Output specs?
427+
"""
428+
output_names = ["return_code", "stdout", "stderr"]
429+
for fld in attr_fields(self):
430+
if fld.name not in ["return_code", "stdout", "stderr"]:
431+
if fld.type is File:
432+
# assuming that field should have either default or metadata, but not both
433+
if (
434+
fld.default is None or fld.default == attr.NOTHING
435+
) and not fld.metadata: # TODO: is it right?
436+
raise AttributeError(
437+
"File has to have default value or metadata"
438+
)
439+
elif fld.default != attr.NOTHING:
440+
output_names.append(fld.name)
441+
elif (
442+
fld.metadata
443+
and self._field_metadata(fld, inputs, output_dir)
444+
!= attr.NOTHING
445+
):
446+
output_names.append(fld.name)
447+
else:
448+
raise Exception("not implemented (collect_additional_output)")
449+
return output_names
450+
423451
def _field_defaultvalue(self, fld, output_dir):
424452
"""Collect output file if the default value specified."""
425453
if not isinstance(fld.default, (str, Path)):

pydra/engine/tests/test_shelltask.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2654,6 +2654,12 @@ def test_shell_cmd_inputspec_outputspec_2():
26542654
)
26552655
shelly.inputs.file1 = "new_file_1.txt"
26562656
shelly.inputs.file2 = "new_file_2.txt"
2657+
# all fileds from output_spec should be in output_names and generated_output_names
2658+
assert (
2659+
shelly.output_names
2660+
== shelly.generated_output_names
2661+
== ["return_code", "stdout", "stderr", "newfile1", "newfile2"]
2662+
)
26572663

26582664
res = shelly()
26592665
assert res.output.stdout == ""
@@ -2714,6 +2720,20 @@ def test_shell_cmd_inputspec_outputspec_2a():
27142720
output_spec=my_output_spec,
27152721
)
27162722
shelly.inputs.file1 = "new_file_1.txt"
2723+
# generated_output_names shoule know that newfile2 will not be generated
2724+
assert shelly.output_names == [
2725+
"return_code",
2726+
"stdout",
2727+
"stderr",
2728+
"newfile1",
2729+
"newfile2",
2730+
]
2731+
assert shelly.generated_output_names == [
2732+
"return_code",
2733+
"stdout",
2734+
"stderr",
2735+
"newfile1",
2736+
]
27172737

27182738
res = shelly()
27192739
assert res.output.stdout == ""
@@ -2834,6 +2854,20 @@ def test_shell_cmd_inputspec_outputspec_3a():
28342854
)
28352855
shelly.inputs.file1 = "new_file_1.txt"
28362856
shelly.inputs.file2 = "new_file_2.txt"
2857+
# generated_output_names shoule know that newfile2 will not be generated
2858+
assert shelly.output_names == [
2859+
"return_code",
2860+
"stdout",
2861+
"stderr",
2862+
"newfile1",
2863+
"newfile2",
2864+
]
2865+
assert shelly.generated_output_names == [
2866+
"return_code",
2867+
"stdout",
2868+
"stderr",
2869+
"newfile1",
2870+
]
28372871

28382872
res = shelly()
28392873
assert res.output.stdout == ""
@@ -2884,6 +2918,12 @@ def test_shell_cmd_inputspec_outputspec_4():
28842918
)
28852919
shelly.inputs.file1 = "new_file_1.txt"
28862920
shelly.inputs.additional_inp = 2
2921+
# generated_output_names should be the same as output_names
2922+
assert (
2923+
shelly.output_names
2924+
== shelly.generated_output_names
2925+
== ["return_code", "stdout", "stderr", "newfile1"]
2926+
)
28872927

28882928
res = shelly()
28892929
assert res.output.stdout == ""

0 commit comments

Comments
 (0)