Skip to content

Commit e168af4

Browse files
authored
Merge pull request #376 from djarecka/nicolocin-pytest-slurm-tmpdir
fixing issues with workflow on cluster with slurm (closes #369, #368)
2 parents 2d1a0b3 + 7669b81 commit e168af4

File tree

6 files changed

+584
-233
lines changed

6 files changed

+584
-233
lines changed

pydra/engine/submitter.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Handle execution backends."""
22
import asyncio
3+
import time
34
from .workers import SerialWorker, ConcurrentFuturesWorker, SlurmWorker, DaskWorker
45
from .core import is_workflow
56
from .helpers import get_open_loop, load_and_run_async
@@ -150,15 +151,31 @@ async def _run_workflow(self, wf, rerun=False):
150151
The computed workflow
151152
152153
"""
154+
for nd in wf.graph.nodes:
155+
if nd.allow_cache_override:
156+
nd.cache_dir = wf.cache_dir
157+
153158
# creating a copy of the graph that will be modified
154159
# the copy contains new lists with original runnable objects
155160
graph_copy = wf.graph.copy()
156161
# keep track of pending futures
157162
task_futures = set()
158163
tasks, tasks_follow_errored = get_runnable_tasks(graph_copy)
159-
while tasks or len(task_futures):
164+
while tasks or task_futures or graph_copy.nodes:
160165
if not tasks and not task_futures:
161-
raise Exception("Nothing queued or todo - something went wrong")
166+
# it's possible that task_futures is empty, but not able to get any
167+
# tasks from graph_copy (using get_runnable_tasks)
168+
# this might be related to some delays saving the files
169+
# so try to get_runnable_tasks for another minut
170+
ii = 0
171+
while not tasks and graph_copy.nodes:
172+
tasks, follow_err = get_runnable_tasks(graph_copy)
173+
ii += 1
174+
time.sleep(1)
175+
if ii > 60:
176+
raise Exception(
177+
"graph is not empty, but not able to get more tasks - something is wrong (e.g. with the filesystem)"
178+
)
162179
for task in tasks:
163180
# grab inputs if needed
164181
logger.debug(f"Retrieving inputs for {task}")

pydra/engine/tests/test_boutiques.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@
2626
"maskfile", ["test_brain.nii.gz", "test_brain", "test_brain.nii"]
2727
)
2828
@pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
29-
def test_boutiques_1(maskfile, plugin, results_function):
29+
def test_boutiques_1(maskfile, plugin, results_function, tmpdir):
3030
""" simple task to run fsl.bet using BoshTask"""
3131
btask = BoshTask(name="NA", zenodo_id="1482743")
3232
btask.inputs.infile = Infile
3333
btask.inputs.maskfile = maskfile
34+
btask.cache_dir = tmpdir
3435
res = results_function(btask, plugin)
3536

3637
assert res.output.return_code == 0
@@ -97,11 +98,12 @@ def test_boutiques_spec_2():
9798
@pytest.mark.parametrize(
9899
"maskfile", ["test_brain.nii.gz", "test_brain", "test_brain.nii"]
99100
)
100-
def test_boutiques_wf_1(maskfile, plugin):
101+
def test_boutiques_wf_1(maskfile, plugin, tmpdir):
101102
""" wf with one task that runs fsl.bet using BoshTask"""
102103
wf = Workflow(name="wf", input_spec=["maskfile", "infile"])
103104
wf.inputs.maskfile = maskfile
104105
wf.inputs.infile = Infile
106+
wf.cache_dir = tmpdir
105107

106108
wf.add(
107109
BoshTask(
@@ -128,11 +130,12 @@ def test_boutiques_wf_1(maskfile, plugin):
128130
@pytest.mark.parametrize(
129131
"maskfile", ["test_brain.nii.gz", "test_brain", "test_brain.nii"]
130132
)
131-
def test_boutiques_wf_2(maskfile, plugin):
133+
def test_boutiques_wf_2(maskfile, plugin, tmpdir):
132134
""" wf with two BoshTasks (fsl.bet and fsl.stats) and one ShellTask"""
133135
wf = Workflow(name="wf", input_spec=["maskfile", "infile"])
134136
wf.inputs.maskfile = maskfile
135137
wf.inputs.infile = Infile
138+
wf.cache_dir = tmpdir
136139

137140
wf.add(
138141
BoshTask(

pydra/engine/tests/test_node_task.py

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,10 @@ def test_odir_init():
365365

366366

367367
@pytest.mark.flaky(reruns=2) # when dask
368-
def test_task_nostate_1(plugin_dask_opt):
368+
def test_task_nostate_1(plugin_dask_opt, tmpdir):
369369
""" task without splitter"""
370370
nn = fun_addtwo(name="NA", a=3)
371+
nn.cache_dir = tmpdir
371372
assert np.allclose(nn.inputs.a, [3])
372373
assert nn.state is None
373374

@@ -405,9 +406,10 @@ def test_task_nostate_1_call():
405406

406407

407408
@pytest.mark.flaky(reruns=2) # when dask
408-
def test_task_nostate_1_call_subm(plugin_dask_opt):
409+
def test_task_nostate_1_call_subm(plugin_dask_opt, tmpdir):
409410
""" task without splitter"""
410411
nn = fun_addtwo(name="NA", a=3)
412+
nn.cache_dir = tmpdir
411413
assert np.allclose(nn.inputs.a, [3])
412414
assert nn.state is None
413415

@@ -422,9 +424,10 @@ def test_task_nostate_1_call_subm(plugin_dask_opt):
422424

423425

424426
@pytest.mark.flaky(reruns=2) # when dask
425-
def test_task_nostate_1_call_plug(plugin_dask_opt):
427+
def test_task_nostate_1_call_plug(plugin_dask_opt, tmpdir):
426428
""" task without splitter"""
427429
nn = fun_addtwo(name="NA", a=3)
430+
nn.cache_dir = tmpdir
428431
assert np.allclose(nn.inputs.a, [3])
429432
assert nn.state is None
430433

@@ -450,9 +453,10 @@ def test_task_nostate_1_call_updateinp():
450453
assert nn.output_dir.exists()
451454

452455

453-
def test_task_nostate_2(plugin):
456+
def test_task_nostate_2(plugin, tmpdir):
454457
""" task with a list as an input, but no splitter"""
455458
nn = moment(name="NA", n=3, lst=[2, 3, 4])
459+
nn.cache_dir = tmpdir
456460
assert np.allclose(nn.inputs.n, [3])
457461
assert np.allclose(nn.inputs.lst, [2, 3, 4])
458462
assert nn.state is None
@@ -467,9 +471,10 @@ def test_task_nostate_2(plugin):
467471
assert nn.output_dir.exists()
468472

469473

470-
def test_task_nostate_3(plugin):
474+
def test_task_nostate_3(plugin, tmpdir):
471475
""" task with a dictionary as an input"""
472476
nn = fun_dict(name="NA", d={"a": "ala", "b": "bala"})
477+
nn.cache_dir = tmpdir
473478
assert nn.inputs.d == {"a": "ala", "b": "bala"}
474479

475480
with Submitter(plugin=plugin) as sub:
@@ -489,6 +494,7 @@ def test_task_nostate_4(plugin, tmpdir):
489494
f.write("hello from pydra\n")
490495

491496
nn = fun_file(name="NA", filename=file1)
497+
nn.cache_dir = tmpdir
492498

493499
with Submitter(plugin) as sub:
494500
sub(nn)
@@ -719,13 +725,14 @@ def test_task_nostate_cachelocations_updated(plugin, tmpdir):
719725

720726
@pytest.mark.flaky(reruns=2) # when dask
721727
@pytest.mark.parametrize("input_type", ["list", "array"])
722-
def test_task_state_1(plugin_dask_opt, input_type):
728+
def test_task_state_1(plugin_dask_opt, input_type, tmpdir):
723729
""" task with the simplest splitter"""
724730
a_in = [3, 5]
725731
if input_type == "array":
726732
a_in = np.array(a_in)
727733

728734
nn = fun_addtwo(name="NA").split(splitter="a", a=a_in)
735+
nn.cache_dir = tmpdir
729736

730737
assert nn.state.splitter == "NA.a"
731738
assert nn.state.splitter_rpn == ["NA.a"]
@@ -761,11 +768,12 @@ def test_task_state_1(plugin_dask_opt, input_type):
761768
assert odir.exists()
762769

763770

764-
def test_task_state_1a(plugin):
771+
def test_task_state_1a(plugin, tmpdir):
765772
""" task with the simplest splitter (inputs set separately)"""
766773
nn = fun_addtwo(name="NA")
767774
nn.split(splitter="a")
768775
nn.inputs.a = [3, 5]
776+
nn.cache_dir = tmpdir
769777

770778
assert nn.state.splitter == "NA.a"
771779
assert nn.state.splitter_rpn == ["NA.a"]
@@ -781,11 +789,12 @@ def test_task_state_1a(plugin):
781789
assert results[i].output.out == res[1]
782790

783791

784-
def test_task_state_singl_1(plugin):
792+
def test_task_state_singl_1(plugin, tmpdir):
785793
""" Tasks with two inputs and a splitter (no combiner)
786794
one input is a single value, the other is in the splitter and combiner
787795
"""
788796
nn = fun_addvar(name="NA").split(splitter="a", a=[3, 5], b=10)
797+
nn.cache_dir = tmpdir
789798

790799
assert nn.inputs.a == [3, 5]
791800
assert nn.inputs.b == 10
@@ -839,7 +848,14 @@ def test_task_state_singl_1(plugin):
839848
)
840849
@pytest.mark.parametrize("input_type", ["list", "array", "mixed"])
841850
def test_task_state_2(
842-
plugin, splitter, state_splitter, state_rpn, expected, expected_ind, input_type
851+
plugin,
852+
splitter,
853+
state_splitter,
854+
state_rpn,
855+
expected,
856+
expected_ind,
857+
input_type,
858+
tmpdir,
843859
):
844860
""" Tasks with two inputs and a splitter (no combiner)"""
845861
a_in, b_in = [3, 5], [10, 20]
@@ -848,6 +864,8 @@ def test_task_state_2(
848864
elif input_type == "mixed":
849865
a_in = np.array(a_in)
850866
nn = fun_addvar(name="NA").split(splitter=splitter, a=a_in, b=b_in)
867+
nn.cache_dir = tmpdir
868+
851869
assert (nn.inputs.a == np.array([3, 5])).all()
852870
assert (nn.inputs.b == np.array([10, 20])).all()
853871
assert nn.state.splitter == state_splitter
@@ -883,9 +901,10 @@ def test_task_state_2(
883901
assert odir.exists()
884902

885903

886-
def test_task_state_3(plugin):
904+
def test_task_state_3(plugin, tmpdir):
887905
""" task with the simplest splitter, the input is an empty list"""
888906
nn = fun_addtwo(name="NA").split(splitter="a", a=[])
907+
nn.cache_dir = tmpdir
889908

890909
assert nn.state.splitter == "NA.a"
891910
assert nn.state.splitter_rpn == ["NA.a"]
@@ -904,12 +923,14 @@ def test_task_state_3(plugin):
904923

905924

906925
@pytest.mark.parametrize("input_type", ["list", "array"])
907-
def test_task_state_4(plugin, input_type):
926+
def test_task_state_4(plugin, input_type, tmpdir):
908927
""" task with a list as an input, and a simple splitter """
909928
lst_in = [[2, 3, 4], [1, 2, 3]]
910929
if input_type == "array":
911930
lst_in = np.array(lst_in)
912931
nn = moment(name="NA", n=3, lst=lst_in).split(splitter="lst")
932+
nn.cache_dir = tmpdir
933+
913934
assert np.allclose(nn.inputs.n, 3)
914935
assert np.allclose(nn.inputs.lst, [[2, 3, 4], [1, 2, 3]])
915936
assert nn.state.splitter == "NA.lst"
@@ -935,9 +956,11 @@ def test_task_state_4(plugin, input_type):
935956
assert odir.exists()
936957

937958

938-
def test_task_state_4a(plugin):
959+
def test_task_state_4a(plugin, tmpdir):
939960
""" task with a tuple as an input, and a simple splitter """
940961
nn = moment(name="NA", n=3, lst=[(2, 3, 4), (1, 2, 3)]).split(splitter="lst")
962+
nn.cache_dir = tmpdir
963+
941964
assert np.allclose(nn.inputs.n, 3)
942965
assert np.allclose(nn.inputs.lst, [[2, 3, 4], [1, 2, 3]])
943966
assert nn.state.splitter == "NA.lst"
@@ -955,11 +978,13 @@ def test_task_state_4a(plugin):
955978
assert odir.exists()
956979

957980

958-
def test_task_state_5(plugin):
981+
def test_task_state_5(plugin, tmpdir):
959982
""" task with a list as an input, and the variable is part of the scalar splitter"""
960983
nn = moment(name="NA", n=[1, 3], lst=[[2, 3, 4], [1, 2, 3]]).split(
961984
splitter=("n", "lst")
962985
)
986+
nn.cache_dir = tmpdir
987+
963988
assert np.allclose(nn.inputs.n, [1, 3])
964989
assert np.allclose(nn.inputs.lst, [[2, 3, 4], [1, 2, 3]])
965990
assert nn.state.splitter == ("NA.n", "NA.lst")
@@ -977,13 +1002,15 @@ def test_task_state_5(plugin):
9771002
assert odir.exists()
9781003

9791004

980-
def test_task_state_5_exception(plugin):
1005+
def test_task_state_5_exception(plugin, tmpdir):
9811006
""" task with a list as an input, and the variable is part of the scalar splitter
9821007
the shapes are not matching, so exception should be raised
9831008
"""
9841009
nn = moment(name="NA", n=[1, 3, 3], lst=[[2, 3, 4], [1, 2, 3]]).split(
9851010
splitter=("n", "lst")
9861011
)
1012+
nn.cache_dir = tmpdir
1013+
9871014
assert np.allclose(nn.inputs.n, [1, 3, 3])
9881015
assert np.allclose(nn.inputs.lst, [[2, 3, 4], [1, 2, 3]])
9891016
assert nn.state.splitter == ("NA.n", "NA.lst")
@@ -994,11 +1021,13 @@ def test_task_state_5_exception(plugin):
9941021
assert "shape" in str(excinfo.value)
9951022

9961023

997-
def test_task_state_6(plugin):
1024+
def test_task_state_6(plugin, tmpdir):
9981025
""" ask with a list as an input, and the variable is part of the outer splitter """
9991026
nn = moment(name="NA", n=[1, 3], lst=[[2, 3, 4], [1, 2, 3]]).split(
10001027
splitter=["n", "lst"]
10011028
)
1029+
nn.cache_dir = tmpdir
1030+
10021031
assert np.allclose(nn.inputs.n, [1, 3])
10031032
assert np.allclose(nn.inputs.lst, [[2, 3, 4], [1, 2, 3]])
10041033
assert nn.state.splitter == ["NA.n", "NA.lst"]
@@ -1016,11 +1045,13 @@ def test_task_state_6(plugin):
10161045
assert odir.exists()
10171046

10181047

1019-
def test_task_state_6a(plugin):
1048+
def test_task_state_6a(plugin, tmpdir):
10201049
""" ask with a tuple as an input, and the variable is part of the outer splitter """
10211050
nn = moment(name="NA", n=[1, 3], lst=[(2, 3, 4), (1, 2, 3)]).split(
10221051
splitter=["n", "lst"]
10231052
)
1053+
nn.cache_dir = tmpdir
1054+
10241055
assert np.allclose(nn.inputs.n, [1, 3])
10251056
assert np.allclose(nn.inputs.lst, [[2, 3, 4], [1, 2, 3]])
10261057
assert nn.state.splitter == ["NA.n", "NA.lst"]
@@ -1039,9 +1070,10 @@ def test_task_state_6a(plugin):
10391070

10401071

10411072
@pytest.mark.flaky(reruns=2) # when dask
1042-
def test_task_state_comb_1(plugin_dask_opt):
1073+
def test_task_state_comb_1(plugin_dask_opt, tmpdir):
10431074
""" task with the simplest splitter and combiner"""
10441075
nn = fun_addtwo(name="NA").split(a=[3, 5], splitter="a").combine(combiner="a")
1076+
nn.cache_dir = tmpdir
10451077

10461078
assert (nn.inputs.a == np.array([3, 5])).all()
10471079

@@ -1173,13 +1205,15 @@ def test_task_state_comb_2(
11731205
state_rpn_final,
11741206
expected,
11751207
expected_val,
1208+
tmpdir,
11761209
):
11771210
""" Tasks with scalar and outer splitters and partial or full combiners"""
11781211
nn = (
11791212
fun_addvar(name="NA")
11801213
.split(a=[3, 5], b=[10, 20], splitter=splitter)
11811214
.combine(combiner=combiner)
11821215
)
1216+
nn.cache_dir = tmpdir
11831217

11841218
assert (nn.inputs.a == np.array([3, 5])).all()
11851219

@@ -1219,11 +1253,12 @@ def test_task_state_comb_2(
12191253
assert odir.exists()
12201254

12211255

1222-
def test_task_state_comb_singl_1(plugin):
1256+
def test_task_state_comb_singl_1(plugin, tmpdir):
12231257
""" Tasks with two inputs;
12241258
one input is a single value, the other is in the splitter and combiner
12251259
"""
12261260
nn = fun_addvar(name="NA").split(splitter="a", a=[3, 5], b=10).combine(combiner="a")
1261+
nn.cache_dir = tmpdir
12271262

12281263
assert nn.inputs.a == [3, 5]
12291264
assert nn.inputs.b == 10
@@ -1248,9 +1283,10 @@ def test_task_state_comb_singl_1(plugin):
12481283
assert odir.exists()
12491284

12501285

1251-
def test_task_state_comb_3(plugin):
1286+
def test_task_state_comb_3(plugin, tmpdir):
12521287
""" task with the simplest splitter, the input is an empty list"""
12531288
nn = fun_addtwo(name="NA").split(splitter="a", a=[]).combine(combiner=["a"])
1289+
nn.cache_dir = tmpdir
12541290

12551291
assert nn.state.splitter == "NA.a"
12561292
assert nn.state.splitter_rpn == ["NA.a"]

0 commit comments

Comments
 (0)