Skip to content

Commit 6fe250c

Browse files
committed
fix: do not use nipype.utils.filemanip.ensure_list
Because that function utilizes ``is_container``, which is imported at global context, nipype could not evaluate ``ensure_list`` calls inlined inside ``connect``. This would end up failing with: ``` fMRIPrep failed: name 'is_container' is not defined ``` A new module for nipype connections is created to provide this kind of commodity functions for all NiPreps.
1 parent 9d78796 commit 6fe250c

File tree

6 files changed

+70
-33
lines changed

6 files changed

+70
-33
lines changed

niworkflows/anat/ants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
from nipype.interfaces.fsl.maths import ApplyMask
1616
from nipype.interfaces.ants import N4BiasFieldCorrection, Atropos, MultiplyImages
1717

18-
from ..utils.misc import get_template_specs, select_first as _pop
18+
from ..utils.misc import get_template_specs
19+
from ..utils.connections import pop_file as _pop
1920

2021
# niworkflows
2122
from ..interfaces.ants import (

niworkflows/func/tests/test_util.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55

66
import numpy as np
77
from nipype.pipeline import engine as pe
8-
from nipype.utils.filemanip import fname_presuffix, copyfile, ensure_list
8+
from nipype.utils.filemanip import fname_presuffix, copyfile
99
from nilearn.image import load_img
1010

11+
from ...utils.connections import listify
1112
from niworkflows.interfaces.masks import ROIsPlot
1213

1314
from ..util import init_bold_reference_wf
@@ -40,7 +41,7 @@
4041
ds001240/sub-26_task-rest_bold.nii.gz
4142
ds001362/sub-01_task-taskname_run-01_bold.nii.gz""".splitlines()
4243

43-
bold_datasets = [ensure_list(d) for d in bold_datasets]
44+
bold_datasets = [listify(d) for d in bold_datasets]
4445

4546

4647
def symmetric_overlap(img1, img2):

niworkflows/func/util.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
from nipype.pipeline import engine as pe
88
from nipype.interfaces import utility as niu, fsl, afni
9-
from nipype.utils.filemanip import ensure_list
109

1110
from templateflow.api import get as get_template
1211

@@ -21,6 +20,7 @@
2120
from ..interfaces.masks import SimpleShowMaskRPT
2221
from ..interfaces.registration import EstimateReferenceImage
2322
from ..interfaces.utils import CopyXForm
23+
from ..utils.connections import listify
2424
from ..utils.misc import pass_dummy_scans as _pass_dummy_scans
2525

2626

@@ -176,7 +176,7 @@ def init_bold_reference_wf(
176176

177177
# fmt: off
178178
workflow.connect([
179-
(inputnode, val_bold, [(("bold_file", ensure_list), "in_file")]),
179+
(inputnode, val_bold, [(("bold_file", listify), "in_file")]),
180180
(inputnode, enhance_and_skullstrip_bold_wf, [
181181
("bold_mask", "inputnode.pre_mask"),
182182
]),
@@ -185,7 +185,7 @@ def init_bold_reference_wf(
185185
(gen_ref, enhance_and_skullstrip_bold_wf, [
186186
("ref_image", "inputnode.in_file"),
187187
]),
188-
(val_bold, bold_1st, [(("out_file", ensure_list), "inlist")]),
188+
(val_bold, bold_1st, [(("out_file", listify), "inlist")]),
189189
(gen_ref, calc_dummy_scans, [("n_volumes_to_discard", "algo_dummy_scans")]),
190190
(calc_dummy_scans, outputnode, [("skip_vols_num", "skip_vols")]),
191191
(gen_ref, outputnode, [
@@ -197,7 +197,7 @@ def init_bold_reference_wf(
197197
("outputnode.mask_file", "bold_mask"),
198198
("outputnode.skull_stripped_file", "ref_image_brain"),
199199
]),
200-
(val_bold, validate_1st, [(("out_report", ensure_list), "inlist")]),
200+
(val_bold, validate_1st, [(("out_report", listify), "inlist")]),
201201
(bold_1st, outputnode, [("out", "bold_file")]),
202202
(validate_1st, outputnode, [("out", "validation_report")]),
203203
])
@@ -218,7 +218,7 @@ def init_bold_reference_wf(
218218
)
219219
# fmt: off
220220
workflow.connect([
221-
(inputnode, val_sbref, [(("sbref_file", ensure_list), "in_file")]),
221+
(inputnode, val_sbref, [(("sbref_file", listify), "in_file")]),
222222
(val_sbref, gen_ref, [("out_file", "sbref_file")]),
223223
])
224224
# fmt: on

niworkflows/interfaces/registration.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import numpy as np
99
from nilearn import image as nli
1010
from nilearn.image import index_img
11-
from nipype.utils.filemanip import fname_presuffix, ensure_list
11+
from nipype.utils.filemanip import fname_presuffix
1212
from nipype.interfaces.base import (
1313
traits,
1414
isdefined,
@@ -467,9 +467,7 @@ class EstimateReferenceImage(SimpleInterface):
467467

468468
def _run_interface(self, runtime):
469469
is_sbref = isdefined(self.inputs.sbref_file)
470-
ref_input = ensure_list(
471-
self.inputs.sbref_file if is_sbref else self.inputs.in_file
472-
)
470+
ref_input = self.inputs.sbref_file if is_sbref else self.inputs.in_file
473471

474472
if self.inputs.multiecho:
475473
if len(ref_input) < 2:

niworkflows/utils/connections.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
"""
4+
Utilities for the creation of nipype workflows.
5+
6+
Because these functions are meant to be inlined in nipype's ``connect`` invocations,
7+
all the imports MUST be done in each function's context.
8+
9+
"""
10+
11+
__all__ = [
12+
"listify",
13+
"pop_file",
14+
]
15+
16+
17+
def pop_file(in_files):
18+
"""
19+
Select the first file from a list of filenames.
20+
21+
Used to grab the first echo's file when processing
22+
multi-echo data through workflows that only accept
23+
a single file.
24+
25+
Examples
26+
--------
27+
>>> pop_file('some/file.nii.gz')
28+
'some/file.nii.gz'
29+
>>> pop_file(['some/file1.nii.gz', 'some/file2.nii.gz'])
30+
'some/file1.nii.gz'
31+
32+
"""
33+
if isinstance(in_files, (list, tuple)):
34+
return in_files[0]
35+
return in_files
36+
37+
38+
def listify(value):
39+
"""
40+
Convert to a list (inspired by bids.utils.listify).
41+
42+
Examples
43+
--------
44+
>>> listify('some/file.nii.gz')
45+
['some/file.nii.gz']
46+
>>> listify((0.1, 0.2))
47+
[0.1, 0.2]
48+
>>> listify(None) is None
49+
True
50+
51+
"""
52+
from pathlib import Path
53+
from nipype.interfaces.base import isdefined
54+
if not isdefined(value) or isinstance(value, type(None)):
55+
return value
56+
if isinstance(value, (str, bytes, Path)):
57+
return [str(value)]
58+
return list(value)

niworkflows/utils/misc.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -212,27 +212,6 @@ def splitext(fname):
212212
return stem, basename[len(stem):]
213213

214214

215-
def select_first(in_files):
216-
"""
217-
Select the first file from a list of filenames.
218-
219-
Used to grab the first echo's file when processing
220-
multi-echo data through workflows that only accept
221-
a single file.
222-
223-
Examples
224-
--------
225-
>>> select_first('some/file.nii.gz')
226-
'some/file.nii.gz'
227-
>>> select_first(['some/file1.nii.gz', 'some/file2.nii.gz'])
228-
'some/file1.nii.gz'
229-
230-
"""
231-
if isinstance(in_files, (list, tuple)):
232-
return in_files[0]
233-
return in_files
234-
235-
236215
def _copy_any(src, dst):
237216
import os
238217
import gzip

0 commit comments

Comments
 (0)