diff --git a/doc/changelog/1.X.X-changelog.rst b/doc/changelog/1.X.X-changelog.rst index 3af9ed8ca4..4e9c1a6521 100644 --- a/doc/changelog/1.X.X-changelog.rst +++ b/doc/changelog/1.X.X-changelog.rst @@ -1,3 +1,17 @@ +1.4.1 (To Be Determined) +======================== +(`Full changelog `__) + + * FIX: mapnode to generate result file when crashes in single node mode (https://github.com/nipy/nipype/pull/3143) + * FIX: Can't seem to import workflows from niflows in CircleCI (https://github.com/nipy/nipype/pull/3134) + * FIX: Repair aftermath of docs refactor (https://github.com/nipy/nipype/pull/3133) + * FIX: change ANTS number_of_time_steps from Float to Int (https://github.com/nipy/nipype/pull/3118) + * DOC: Revise generation of examples to work in RTD (https://github.com/nipy/nipype/pull/3132) + * DOC: Bring examples generation back to ``doc/conf.py`` (https://github.com/nipy/nipype/pull/3131) + * DOC: Documentation overhaul (https://github.com/nipy/nipype/pull/3124) + * DOC: Deep revision of documentation building (https://github.com/nipy/nipype/pull/3120) + * DOC: Deduplicate code for Sphinx's APIdoc generation (https://github.com/nipy/nipype/pull/3119) + 1.4.0 (December 20, 2019) ========================= (`Full changelog `__) diff --git a/doc/requirements.txt b/doc/requirements.txt index 057147c5b5..772afb185f 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,8 +1,27 @@ +configparser dipy +funcsigs +future>=0.16.0 ipython matplotlib +mock nbsphinx +networkx>=1.9 +neurdflib +nibabel>=2.1.0 +numpy>=1.9.0 +numpydoc +packaging +prov>=1.5.2 +psutil +pydot>=1.2.3 +pydotplus +pytest>=3.0 +python-dateutil>=2.2 +scipy>=0.14 +simplejson>=3.8.0 sphinx-argparse sphinx>=2.1.2 sphinxcontrib-apidoc -sphinxcontrib-napoleon \ No newline at end of file +sphinxcontrib-napoleon +traits>=4.6 \ No newline at end of file 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..f5e2d5016c 100644 --- a/nipype/pipeline/engine/tests/test_nodes.py +++ b/nipype/pipeline/engine/tests/test_nodes.py @@ -314,3 +314,23 @@ 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, deadly_num=7): + if num == deadly_num: + raise RuntimeError("Got the deadly num (%d)." % num) + return num + 1 + + 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") diff --git a/nipype/pipeline/plugins/base.py b/nipype/pipeline/plugins/base.py index 4be8eb232b..599db29418 100644 --- a/nipype/pipeline/plugins/base.py +++ b/nipype/pipeline/plugins/base.py @@ -461,7 +461,7 @@ def _remove_node_dirs(self): ) % (self.procs[idx]._id, outdir) ) - shutil.rmtree(outdir) + shutil.rmtree(outdir, ignore_errors=True) class SGELikeBatchManagerBase(DistributedPluginBase):