Skip to content

Commit eb48e0a

Browse files
authored
Merge pull request #553 from djarecka/enh/callable_template
function in output_file_template
2 parents 1bc0f75 + 2067a05 commit eb48e0a

File tree

4 files changed

+185
-1
lines changed

4 files changed

+185
-1
lines changed

pydra/engine/helpers_file.py

+4
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,11 @@ def _template_formatting(field, inputs, inputs_dict_st):
678678
"""
679679
from .specs import MultiOutputFile
680680

681+
# if a template is a function it has to be run first with the inputs as the only arg
681682
template = field.metadata["output_file_template"]
683+
if callable(template):
684+
template = template(inputs)
685+
682686
# as default, we assume that keep_extension is True
683687
keep_extension = field.metadata.get("keep_extension", True)
684688

pydra/engine/specs.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,11 @@ def _check_requires(self, fld, inputs):
624624
# if the output has output_file_template field,
625625
# adding all input fields from the template to requires
626626
if "output_file_template" in fld.metadata:
627-
inp_fields = re.findall(r"{\w+}", fld.metadata["output_file_template"])
627+
template = fld.metadata["output_file_template"]
628+
# if a template is a function it has to be run first with the inputs as the only arg
629+
if callable(template):
630+
template = template(inputs)
631+
inp_fields = re.findall(r"{\w+}", template)
628632
field_required += [
629633
el[1:-1] for el in inp_fields if el[1:-1] not in field_required
630634
]

pydra/engine/tests/test_shelltask.py

+66
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,72 @@ def test_shell_cmd_inputsspec_11():
15561556
assert out_file.name == "test1" or out_file.name == "test2"
15571557

15581558

1559+
@pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
1560+
def test_shell_cmd_inputspec_12(tmpdir, plugin, results_function):
1561+
"""
1562+
providing output name using input_spec
1563+
output_file_template is provided as a function that returns
1564+
various templates depending on the values of inputs fields
1565+
"""
1566+
cmd = "cp"
1567+
file = tmpdir.mkdir("data_inp").join("file.txt")
1568+
file.write("content\n")
1569+
1570+
def template_function(inputs):
1571+
if inputs.number % 2 == 0:
1572+
return "{file_orig}_even"
1573+
else:
1574+
return "{file_orig}_odd"
1575+
1576+
my_input_spec = SpecInfo(
1577+
name="Input",
1578+
fields=[
1579+
(
1580+
"file_orig",
1581+
attr.ib(
1582+
type=File,
1583+
metadata={"position": 2, "help_string": "new file", "argstr": ""},
1584+
),
1585+
),
1586+
(
1587+
"number",
1588+
attr.ib(
1589+
type=int,
1590+
metadata={"help_string": "a number", "mandatory": True},
1591+
),
1592+
),
1593+
(
1594+
"file_copy",
1595+
attr.ib(
1596+
type=str,
1597+
metadata={
1598+
"output_file_template": template_function,
1599+
"help_string": "output file",
1600+
"argstr": "",
1601+
},
1602+
),
1603+
),
1604+
],
1605+
bases=(ShellSpec,),
1606+
)
1607+
1608+
shelly = ShellCommandTask(
1609+
name="shelly",
1610+
executable=cmd,
1611+
input_spec=my_input_spec,
1612+
file_orig=file,
1613+
number=2,
1614+
cache_dir=tmpdir,
1615+
)
1616+
1617+
res = results_function(shelly, plugin)
1618+
assert res.output.stdout == ""
1619+
assert res.output.file_copy.exists()
1620+
assert res.output.file_copy.name == "file_even.txt"
1621+
# checking if it's created in a good place
1622+
assert shelly.output_dir == res.output.file_copy.parent
1623+
1624+
15591625
@pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
15601626
def test_shell_cmd_inputspec_copyfile_1(plugin, results_function, tmpdir):
15611627
"""shelltask changes a file in place,

pydra/engine/tests/test_shelltask_inputspec.py

+110
Original file line numberDiff line numberDiff line change
@@ -1721,6 +1721,116 @@ def test_shell_cmd_inputs_template_10():
17211721
assert shelly.output_names == ["return_code", "stdout", "stderr", "outA"]
17221722

17231723

1724+
def test_shell_cmd_inputs_template_function_1():
1725+
"""one input field uses output_file_template that is a simple function
1726+
this can be easily done by simple template as in test_shell_cmd_inputs_template_1
1727+
"""
1728+
1729+
# a function that return an output template
1730+
def template_fun(inputs):
1731+
return "{inpA}_out"
1732+
1733+
my_input_spec = SpecInfo(
1734+
name="Input",
1735+
fields=[
1736+
(
1737+
"inpA",
1738+
attr.ib(
1739+
type=str,
1740+
metadata={
1741+
"position": 1,
1742+
"help_string": "inpA",
1743+
"argstr": "",
1744+
"mandatory": True,
1745+
},
1746+
),
1747+
),
1748+
(
1749+
"outA",
1750+
attr.ib(
1751+
type=str,
1752+
metadata={
1753+
"position": 2,
1754+
"help_string": "outA",
1755+
"argstr": "-o",
1756+
"output_file_template": template_fun,
1757+
},
1758+
),
1759+
),
1760+
],
1761+
bases=(ShellSpec,),
1762+
)
1763+
1764+
shelly = ShellCommandTask(
1765+
executable="executable", input_spec=my_input_spec, inpA="inpA"
1766+
)
1767+
1768+
assert shelly.cmdline == f"executable inpA -o {str(shelly.output_dir / 'inpA_out')}"
1769+
1770+
1771+
def test_shell_cmd_inputs_template_function_2():
1772+
"""one input field uses output_file_template that is a function,
1773+
depending on a value of an input it returns different template
1774+
"""
1775+
1776+
# a function that return an output template that depends on value of the input
1777+
def template_fun(inputs):
1778+
if inputs.inpB % 2 == 0:
1779+
return "{inpA}_even"
1780+
else:
1781+
return "{inpA}_odd"
1782+
1783+
my_input_spec = SpecInfo(
1784+
name="Input",
1785+
fields=[
1786+
(
1787+
"inpA",
1788+
attr.ib(
1789+
type=str,
1790+
metadata={
1791+
"position": 1,
1792+
"help_string": "inpA",
1793+
"argstr": "",
1794+
"mandatory": True,
1795+
},
1796+
),
1797+
),
1798+
(
1799+
"inpB",
1800+
attr.ib(
1801+
type=int,
1802+
metadata={
1803+
"help_string": "inpB",
1804+
"mandatory": True,
1805+
},
1806+
),
1807+
),
1808+
(
1809+
"outA",
1810+
attr.ib(
1811+
type=str,
1812+
metadata={
1813+
"position": 2,
1814+
"help_string": "outA",
1815+
"argstr": "-o",
1816+
"output_file_template": template_fun,
1817+
},
1818+
),
1819+
),
1820+
],
1821+
bases=(ShellSpec,),
1822+
)
1823+
1824+
shelly = ShellCommandTask(
1825+
executable="executable",
1826+
input_spec=my_input_spec,
1827+
inpA="inpA",
1828+
inpB=1,
1829+
)
1830+
1831+
assert shelly.cmdline == f"executable inpA -o {str(shelly.output_dir / 'inpA_odd')}"
1832+
1833+
17241834
def test_shell_cmd_inputs_template_1_st():
17251835
"""additional inputs, one uses output_file_template (and argstr)
17261836
testing cmdline when splitter defined

0 commit comments

Comments
 (0)