From d0015342a731c27c19ef8c8edf163884b1a47fc7 Mon Sep 17 00:00:00 2001 From: Satrajit Ghosh Date: Sun, 5 Jan 2020 13:46:09 -0500 Subject: [PATCH 1/2] fix: mapnode to generate result file when crashes in single node mode --- nipype/info.py | 10 +++++- nipype/pipeline/engine/nodes.py | 38 ++++++++++++++++++---- nipype/pipeline/engine/tests/test_nodes.py | 24 ++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/nipype/info.py b/nipype/info.py index c09c1e9e4d..7a2e4ae70e 100644 --- a/nipype/info.py +++ b/nipype/info.py @@ -155,7 +155,15 @@ def get_nipype_gitversion(): # https://github.com/nipy/nipype/pull/2961#issuecomment-512035484 REQUIRES += ["neurdflib"] -TESTS_REQUIRES = ["codecov", "coverage<5", "mock", "pytest", "pytest-cov", "pytest-env"] +TESTS_REQUIRES = [ + "codecov", + "coverage<5", + "mock", + "pytest", + "pytest-cov", + "pytest-env", + "pytest-timeout", +] EXTRA_REQUIRES = { "data": ["datalad"], diff --git a/nipype/pipeline/engine/nodes.py b/nipype/pipeline/engine/nodes.py index 09822cc7ff..aeff5f12da 100644 --- a/nipype/pipeline/engine/nodes.py +++ b/nipype/pipeline/engine/nodes.py @@ -1366,13 +1366,39 @@ def _run_interface(self, execute=True, updatehash=False): nodenames = [nnametpl.format(i) for i in range(nitems)] # Run mapnode - result = self._collate_results( - _node_runner( - self._make_nodes(cwd), - updatehash=updatehash, - stop_first=str2bool(self.config["execution"]["stop_on_first_crash"]), - ) + outdir = self.output_dir() + result = InterfaceResult( + interface=self._interface.__class__, + runtime=Bunch( + cwd=outdir, + returncode=1, + environ=dict(os.environ), + hostname=socket.gethostname(), + ), + inputs=self._interface.inputs.get_traitsfree(), ) + try: + result = self._collate_results( + _node_runner( + self._make_nodes(cwd), + updatehash=updatehash, + stop_first=str2bool( + self.config["execution"]["stop_on_first_crash"] + ), + ) + ) + except Exception as msg: + result.runtime.stderr = "%s\n\n%s".format( + getattr(result.runtime, "stderr", ""), msg + ) + _save_resultfile( + result, + outdir, + self.name, + rebase=str2bool(self.config["execution"]["use_relative_paths"]), + ) + raise + # And store results _save_resultfile(result, cwd, self.name, rebase=False) # remove any node directories no longer required diff --git a/nipype/pipeline/engine/tests/test_nodes.py b/nipype/pipeline/engine/tests/test_nodes.py index 6fd88011ee..a7d47a1d3d 100644 --- a/nipype/pipeline/engine/tests/test_nodes.py +++ b/nipype/pipeline/engine/tests/test_nodes.py @@ -314,3 +314,27 @@ def test_outputmultipath_collapse(tmpdir): assert ifres.outputs.out == [4] assert ndres.outputs.out == [4] assert select_nd.result.outputs.out == [4] + + +@pytest.mark.timeout(30) +def test_mapnode_single(tmpdir): + tmpdir.chdir() + + def _producer(num=1, output_file=None, deadly_num=7): + if num == deadly_num: + raise RuntimeError("Got the deadly num (%d)." % num) + if output_file is None: + output_file = "producer_output_%05d" % num + with open(output_file, "w") as ofile: + ofile.write("%d" % num) + return output_file + + pnode = pe.MapNode( + niu.Function(function=_producer), name="ProducerNode", iterfield=["num"] + ) + pnode.inputs.num = [7] + wf = pe.Workflow(name="PC_Workflow") + wf.add_nodes([pnode]) + wf.base_dir = os.path.abspath("./test_output") + with pytest.raises(RuntimeError): + wf.run(plugin="MultiProc") From e04131dd29e9d112eab4f2acbc27df123f897f83 Mon Sep 17 00:00:00 2001 From: Satrajit Ghosh Date: Sun, 5 Jan 2020 13:54:56 -0500 Subject: [PATCH 2/2] enh: simplify test further --- nipype/pipeline/engine/tests/test_nodes.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/nipype/pipeline/engine/tests/test_nodes.py b/nipype/pipeline/engine/tests/test_nodes.py index a7d47a1d3d..f5e2d5016c 100644 --- a/nipype/pipeline/engine/tests/test_nodes.py +++ b/nipype/pipeline/engine/tests/test_nodes.py @@ -320,14 +320,10 @@ def test_outputmultipath_collapse(tmpdir): def test_mapnode_single(tmpdir): tmpdir.chdir() - def _producer(num=1, output_file=None, deadly_num=7): + def _producer(num=1, deadly_num=7): if num == deadly_num: raise RuntimeError("Got the deadly num (%d)." % num) - if output_file is None: - output_file = "producer_output_%05d" % num - with open(output_file, "w") as ofile: - ofile.write("%d" % num) - return output_file + return num + 1 pnode = pe.MapNode( niu.Function(function=_producer), name="ProducerNode", iterfield=["num"]