Skip to content

Commit b47b17c

Browse files
committed
Merge remote-tracking branch 'upstream/master' into fix/bunchmapnode
* upstream/master: better test Revert "my own nipype version" fix test Update __init__.py in fsl interfaces to have new ApplyXFM my own nipype version fix outputs test Revert "fix fast??" fix fast?? fix README after neurostars is down m m ApplyXfm -> ApplyXFM fix: hierarchy typo add: itername to track hierarchy + iterables fix: missing import tst: added test case test: use fullname
2 parents cb365f4 + 199b25d commit b47b17c

File tree

7 files changed

+123
-31
lines changed

7 files changed

+123
-31
lines changed

README.rst

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,21 @@ Information specific to Nipype is located here::
7777
Support and Communication
7878
-------------------------
7979

80-
If you have a problem or would like to ask a question about how to do something in Nipype please submit a question
81-
to `NeuroStars.org <http://neurostars.org>`_ with a *nipype* tag. `NeuroStars.org <http://neurostars.org>`_ is a platform similar to StackOverflow but dedicated to neuroinformatics. All previous Nipype questions are available here::
82-
83-
http://neurostars.org/t/nipype/
84-
80+
If you have a problem or would like to ask a question about how to do something in Nipype please open an issue
81+
in this GitHub repository.
8582

8683
To participate in the Nipype development related discussions please use the following mailing list::
8784

8885
http://mail.python.org/mailman/listinfo/neuroimaging
8986

9087
Please add *[nipype]* to the subject line when posting on the mailing list.
9188

89+
.. warning ::
90+
91+
As of `Nov 23, 2016 <https://twitter.com/neuroquestion/status/801453442132754432>`_,
92+
`NeuroStars <http://neurostars.org>`_ is down. We used to have all previous Nipype
93+
questions available under the `nipype <http://neurostars.org/t/nipype/>`_ label.
94+
9295
9396
Nipype structure
9497
----------------

nipype/interfaces/fsl/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
"""
99

1010
from .base import (FSLCommand, Info, check_fsl, no_fsl, no_fsl_course_data)
11-
from .preprocess import (FAST, FLIRT, ApplyXfm, BET, MCFLIRT, FNIRT, ApplyWarp,
12-
SliceTimer, SUSAN, PRELUDE, FUGUE, FIRST)
11+
from .preprocess import (FAST, FLIRT, ApplyXfm, ApplyXFM, BET, MCFLIRT, FNIRT,
12+
ApplyWarp, SliceTimer, SUSAN, PRELUDE, FUGUE, FIRST)
1313
from .model import (Level1Design, FEAT, FEATModel, FILMGLS, FEATRegister,
1414
FLAMEO, ContrastMgr, MultipleRegressDesign, L2Model, SMM,
1515
MELODIC, SmoothEstimate, Cluster, Randomise, GLM)

nipype/interfaces/fsl/preprocess.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -323,17 +323,20 @@ def _list_outputs(self):
323323
nclasses = self.inputs.number_classes
324324
# when using multichannel, results basename is based on last
325325
# input filename
326+
_gen_fname_opts = {}
326327
if isdefined(self.inputs.out_basename):
327-
basefile = self.inputs.out_basename
328+
_gen_fname_opts['basename'] = self.inputs.out_basename
329+
_gen_fname_opts['cwd'] = os.getcwd()
328330
else:
329-
basefile = self.inputs.in_files[-1]
331+
_gen_fname_opts['basename'] = self.inputs.in_files[-1]
332+
_gen_fname_opts['cwd'], _, _ = split_filename(_gen_fname_opts['basename'])
330333

331-
outputs['tissue_class_map'] = self._gen_fname(basefile, suffix='_seg')
334+
outputs['tissue_class_map'] = self._gen_fname(suffix='_seg', **_gen_fname_opts)
332335
if self.inputs.segments:
333336
outputs['tissue_class_files'] = []
334337
for i in range(nclasses):
335338
outputs['tissue_class_files'].append(
336-
self._gen_fname(basefile, suffix='_seg_%d' % i))
339+
self._gen_fname(suffix='_seg_%d' % i, **_gen_fname_opts))
337340
if isdefined(self.inputs.output_biascorrected):
338341
outputs['restored_image'] = []
339342
if len(self.inputs.in_files) > 1:
@@ -342,22 +345,21 @@ def _list_outputs(self):
342345
for val, f in enumerate(self.inputs.in_files):
343346
# image numbering is 1-based
344347
outputs['restored_image'].append(
345-
self._gen_fname(basefile,
346-
suffix='_restore_%d' % (val + 1)))
348+
self._gen_fname(suffix='_restore_%d' % (val + 1), **_gen_fname_opts))
347349
else:
348350
# single image segmentation has unnumbered output image
349351
outputs['restored_image'].append(
350-
self._gen_fname(basefile, suffix='_restore'))
352+
self._gen_fname(suffix='_restore', **_gen_fname_opts))
351353

352-
outputs['mixeltype'] = self._gen_fname(basefile, suffix='_mixeltype')
354+
outputs['mixeltype'] = self._gen_fname(suffix='_mixeltype', **_gen_fname_opts)
353355
if not self.inputs.no_pve:
354356
outputs['partial_volume_map'] = self._gen_fname(
355-
basefile, suffix='_pveseg')
357+
suffix='_pveseg', **_gen_fname_opts)
356358
outputs['partial_volume_files'] = []
357359
for i in range(nclasses):
358360
outputs[
359361
'partial_volume_files'].append(
360-
self._gen_fname(basefile, suffix='_pve_%d' % i))
362+
self._gen_fname(suffix='_pve_%d' % i, **_gen_fname_opts))
361363
if self.inputs.output_biasfield:
362364
outputs['bias_field'] = []
363365
if len(self.inputs.in_files) > 1:
@@ -366,18 +368,17 @@ def _list_outputs(self):
366368
for val, f in enumerate(self.inputs.in_files):
367369
# image numbering is 1-based
368370
outputs['bias_field'].append(
369-
self._gen_fname(basefile,
370-
suffix='_bias_%d' % (val + 1)))
371+
self._gen_fname(suffix='_bias_%d' % (val + 1), **_gen_fname_opts))
371372
else:
372373
# single image segmentation has unnumbered output image
373374
outputs['bias_field'].append(
374-
self._gen_fname(basefile, suffix='_bias'))
375+
self._gen_fname(suffix='_bias', **_gen_fname_opts))
375376

376377
if self.inputs.probability_maps:
377378
outputs['probability_maps'] = []
378379
for i in range(nclasses):
379380
outputs['probability_maps'].append(
380-
self._gen_fname(basefile, suffix='_prob_%d' % i))
381+
self._gen_fname(suffix='_prob_%d' % i, **_gen_fname_opts))
381382
return outputs
382383

383384

@@ -563,27 +564,26 @@ def _parse_inputs(self, skip=None):
563564
skip.append('save_log')
564565
return super(FLIRT, self)._parse_inputs(skip=skip)
565566

566-
567-
class ApplyXfmInputSpec(FLIRTInputSpec):
567+
class ApplyXFMInputSpec(FLIRTInputSpec):
568568
apply_xfm = traits.Bool(
569569
True, argstr='-applyxfm', requires=['in_matrix_file'],
570570
desc='apply transformation supplied by in_matrix_file',
571571
usedefault=True)
572572

573573

574-
class ApplyXfm(FLIRT):
574+
class ApplyXFM(FLIRT):
575575
"""Currently just a light wrapper around FLIRT,
576576
with no modifications
577577
578-
ApplyXfm is used to apply an existing tranform to an image
578+
ApplyXFM is used to apply an existing tranform to an image
579579
580580
581581
Examples
582582
--------
583583
584584
>>> import nipype.interfaces.fsl as fsl
585585
>>> from nipype.testing import example_data
586-
>>> applyxfm = fsl.ApplyXfm()
586+
>>> applyxfm = fsl.preprocess.ApplyXFM()
587587
>>> applyxfm.inputs.in_file = example_data('structural.nii')
588588
>>> applyxfm.inputs.in_matrix_file = example_data('trans.mat')
589589
>>> applyxfm.inputs.out_file = 'newfile.nii'
@@ -592,8 +592,18 @@ class ApplyXfm(FLIRT):
592592
>>> result = applyxfm.run() # doctest: +SKIP
593593
594594
"""
595-
input_spec = ApplyXfmInputSpec
595+
input_spec = ApplyXFMInputSpec
596596

597+
class ApplyXfm(ApplyXFM):
598+
"""
599+
.. deprecated:: 0.12.1
600+
Use :py:class:`nipype.interfaces.fsl.ApplyXFM` instead
601+
"""
602+
def __init__(self, **inputs):
603+
super(ApplyXfm, self).__init__(**inputs)
604+
warn(('This interface has been renamed since 0.12.1, please use '
605+
'nipype.interfaces.fsl.ApplyXFM'),
606+
UserWarning)
597607

598608
class MCFLIRTInputSpec(FSLCommandInputSpec):
599609
in_file = File(exists=True, position=0, argstr="-in %s", mandatory=True,

nipype/interfaces/fsl/tests/test_preprocess.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
import shutil
1111

1212
from nipype.testing import (assert_equal, assert_not_equal, assert_raises,
13-
skipif)
13+
skipif, assert_true)
1414

15-
from nipype.utils.filemanip import split_filename
15+
from nipype.utils.filemanip import split_filename, filename_to_list
1616
from .. import preprocess as fsl
1717
from nipype.interfaces.fsl import Info
1818
from nipype.interfaces.base import File, TraitError, Undefined, isdefined
@@ -167,6 +167,33 @@ def test_fast():
167167
"-S 1 %s" % tmp_infile])
168168
teardown_infile(tmp_dir)
169169

170+
@skipif(no_fsl)
171+
def test_fast_list_outputs():
172+
''' By default (no -o), FSL's fast command outputs files into the same
173+
directory as the input files. If the flag -o is set, it outputs files into
174+
the cwd '''
175+
def _run_and_test(opts, output_base):
176+
outputs = fsl.FAST(**opts)._list_outputs()
177+
for output in outputs.values():
178+
filenames = filename_to_list(output)
179+
if filenames is not None:
180+
for filename in filenames:
181+
assert_equal(filename[:len(output_base)], output_base)
182+
183+
# set up
184+
infile, indir = setup_infile()
185+
cwd = tempfile.mkdtemp()
186+
os.chdir(cwd)
187+
yield assert_not_equal, indir, cwd
188+
out_basename = 'a_basename'
189+
190+
# run and test
191+
opts = {'in_files': tmp_infile}
192+
input_path, input_filename, input_ext = split_filename(tmp_infile)
193+
_run_and_test(opts, os.path.join(input_path, input_filename))
194+
195+
opts['out_basename'] = out_basename
196+
_run_and_test(opts, os.path.join(cwd, out_basename))
170197

171198
@skipif(no_fsl)
172199
def setup_flirt():
@@ -587,3 +614,8 @@ def test_first_genfname():
587614
value = first._gen_fname(name='original_segmentations')
588615
expected_value = os.path.abspath('segment_all_none_origsegs.nii.gz')
589616
yield assert_equal, value, expected_value
617+
618+
@skipif(no_fsl)
619+
def test_deprecation():
620+
interface = fsl.ApplyXfm()
621+
yield assert_true, isinstance(interface, fsl.ApplyXFM)

nipype/pipeline/engine/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ def fullname(self):
6969
fullname = self._hierarchy + '.' + self.name
7070
return fullname
7171

72+
@property
73+
def itername(self):
74+
itername = self._id
75+
if self._hierarchy:
76+
itername = self._hierarchy + '.' + self._id
77+
return itername
78+
7279
def clone(self, name):
7380
"""Clone an EngineBase object
7481

nipype/pipeline/engine/tests/test_join.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,46 @@ def test_set_join_node_file_input():
602602
os.chdir(cwd)
603603
rmtree(wd)
604604

605+
def test_nested_workflow_join():
606+
"""Test collecting join inputs within a nested workflow"""
607+
cwd = os.getcwd()
608+
wd = mkdtemp()
609+
os.chdir(wd)
610+
611+
# Make the nested workflow
612+
def nested_wf(i, name='smallwf'):
613+
#iterables with list of nums
614+
inputspec = pe.Node(IdentityInterface(fields=['n']), name='inputspec')
615+
inputspec.iterables = [('n', i)]
616+
# increment each iterable before joining
617+
pre_join = pe.Node(IncrementInterface(),
618+
name='pre_join')
619+
# rejoin nums into list
620+
join = pe.JoinNode(IdentityInterface(fields=['n']),
621+
joinsource='inputspec',
622+
joinfield='n',
623+
name='join')
624+
#define and connect nested workflow
625+
wf = pe.Workflow(name='wf_%d'%i[0])
626+
wf.connect(inputspec, 'n', pre_join, 'input1')
627+
wf.connect(pre_join, 'output1', join, 'n')
628+
return wf
629+
# master wf
630+
meta_wf = pe.Workflow(name='meta', base_dir='.')
631+
# add each mini-workflow to master
632+
for i in [[1,3],[2,4]]:
633+
mini_wf = nested_wf(i)
634+
meta_wf.add_nodes([mini_wf])
635+
636+
result = meta_wf.run()
637+
638+
# there should be six nodes in total
639+
assert_equal(len(result.nodes()), 6,
640+
"The number of expanded nodes is incorrect.")
641+
642+
os.chdir(cwd)
643+
rmtree(wd)
644+
605645

606646
if __name__ == "__main__":
607647
import nose

nipype/pipeline/engine/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ def generate_expanded_graph(graph_in):
711711
in_edges = jedge_dict[jnode] = {}
712712
edges2remove = []
713713
for src, dest, data in graph_in.in_edges_iter(jnode, True):
714-
in_edges[src._id] = data
714+
in_edges[src.itername] = data
715715
edges2remove.append((src, dest))
716716

717717
for src, dest in edges2remove:
@@ -796,7 +796,7 @@ def make_field_func(*pair):
796796
expansions = defaultdict(list)
797797
for node in graph_in.nodes_iter():
798798
for src_id, edge_data in list(old_edge_dict.items()):
799-
if node._id.startswith(src_id):
799+
if node.itername.startswith(src_id):
800800
expansions[src_id].append(node)
801801
for in_id, in_nodes in list(expansions.items()):
802802
logger.debug("The join node %s input %s was expanded"

0 commit comments

Comments
 (0)