Skip to content

Commit a7a9c85

Browse files
authored
Merge pull request #2538 from effigies/enh/library_interfaces
ENH: Add LibraryBaseInterface
2 parents b5ccdab + 3d5661c commit a7a9c85

23 files changed

+258
-174
lines changed

nipype/algorithms/metrics.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
from scipy.ndimage.measurements import center_of_mass, label
2020

2121
from .. import config, logging
22-
from ..utils.misc import package_check
2322

2423
from ..interfaces.base import (BaseInterface, traits, TraitedSpec, File,
2524
InputMultiPath, BaseInterfaceInputSpec,
2625
isdefined)
26+
from ..interfaces.nipy.base import NipyBaseInterface
2727
from ..utils import NUMPY_MMAP
2828

2929
iflogger = logging.getLogger('interface')
@@ -651,7 +651,7 @@ class SimilarityOutputSpec(TraitedSpec):
651651
traits.Float(desc="Similarity between volume 1 and 2, frame by frame"))
652652

653653

654-
class Similarity(BaseInterface):
654+
class Similarity(NipyBaseInterface):
655655
"""Calculates similarity between two 3D or 4D volumes. Both volumes have to be in
656656
the same coordinate system, same space within that coordinate system and
657657
with the same voxel dimensions.
@@ -674,19 +674,8 @@ class Similarity(BaseInterface):
674674

675675
input_spec = SimilarityInputSpec
676676
output_spec = SimilarityOutputSpec
677-
_have_nipy = True
678-
679-
def __init__(self, **inputs):
680-
try:
681-
package_check('nipy')
682-
except Exception:
683-
self._have_nipy = False
684-
super(Similarity, self).__init__(**inputs)
685677

686678
def _run_interface(self, runtime):
687-
if not self._have_nipy:
688-
raise RuntimeError('nipy is not installed')
689-
690679
from nipy.algorithms.registration.histogram_registration import HistogramRegistration
691680
from nipy.algorithms.registration.affine import Affine
692681

nipype/interfaces/base/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"""
1111
from .core import (Interface, BaseInterface, SimpleInterface, CommandLine,
1212
StdOutCommandLine, MpiCommandLine, SEMLikeCommandLine,
13-
PackageInfo)
13+
LibraryBaseInterface, PackageInfo)
1414

1515
from .specs import (BaseTraitedSpec, TraitedSpec, DynamicTraitedSpec,
1616
BaseInterfaceInputSpec, CommandLineInputSpec,

nipype/interfaces/base/core.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,35 @@ def _format_arg(self, name, spec, value):
12671267
return super(SEMLikeCommandLine, self)._format_arg(name, spec, value)
12681268

12691269

1270+
class LibraryBaseInterface(BaseInterface):
1271+
_pkg = None
1272+
imports = ()
1273+
1274+
def __init__(self, check_import=True, *args, **kwargs):
1275+
super(LibraryBaseInterface, self).__init__(*args, **kwargs)
1276+
if check_import:
1277+
import importlib
1278+
failed_imports = []
1279+
for pkg in (self._pkg,) + tuple(self.imports):
1280+
try:
1281+
importlib.import_module(pkg)
1282+
except ImportError:
1283+
failed_imports.append(pkg)
1284+
if failed_imports:
1285+
iflogger.warn('Unable to import %s; %s interface may fail to '
1286+
'run', failed_imports, self.__class__.__name__)
1287+
1288+
@property
1289+
def version(self):
1290+
if self._version is None:
1291+
import importlib
1292+
try:
1293+
self._version = importlib.import_module(self._pkg).__version__
1294+
except (ImportError, AttributeError):
1295+
pass
1296+
return super(LibraryBaseInterface, self).version
1297+
1298+
12701299
class PackageInfo(object):
12711300
_version = None
12721301
version_cmd = None
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from __future__ import unicode_literals
3+
from ..core import LibraryBaseInterface
4+
5+
6+
def test_LibraryBaseInterface_inputs():
7+
input_map = dict(
8+
ignore_exception=dict(
9+
deprecated='1.0.0',
10+
nohash=True,
11+
usedefault=True,
12+
), )
13+
inputs = LibraryBaseInterface.input_spec()
14+
15+
for key, metadata in list(input_map.items()):
16+
for metakey, value in list(metadata.items()):
17+
assert getattr(inputs.traits()[key], metakey) == value

nipype/interfaces/cmtk/base.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# -*- coding: utf-8 -*-
2+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
""" Base interface for cmtk """
5+
6+
from ..base import LibraryBaseInterface
7+
from ...utils.misc import package_check
8+
9+
10+
class CFFBaseInterface(LibraryBaseInterface):
11+
_pkg = 'cfflib'
12+
13+
14+
# Originally set in convert, nbs, nx, parcellation
15+
# Set here to be imported, in case anybody depends on its presence
16+
# Remove in 2.0
17+
have_cmp = True
18+
try:
19+
package_check('cmp')
20+
except ImportError:
21+
have_cmp = False
22+
23+
have_cfflib = True
24+
try:
25+
package_check('cfflib')
26+
except ImportError:
27+
have_cfflib = False
28+
29+
have_cv = True
30+
try:
31+
package_check('cviewer')
32+
except ImportError:
33+
have_cv = False

nipype/interfaces/cmtk/convert.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,10 @@
88
import string
99
import networkx as nx
1010

11-
from ...utils.misc import package_check
1211
from ...utils.filemanip import split_filename
13-
from ..base import (BaseInterface, BaseInterfaceInputSpec, traits, File,
12+
from ..base import (BaseInterfaceInputSpec, traits, File,
1413
TraitedSpec, InputMultiPath, isdefined)
15-
16-
have_cfflib = True
17-
try:
18-
package_check('cfflib')
19-
except Exception as e:
20-
have_cfflib = False
21-
else:
22-
import cfflib as cf
14+
from .base import CFFBaseInterface, have_cfflib
2315

2416

2517
class CFFConverterInputSpec(BaseInterfaceInputSpec):
@@ -67,7 +59,7 @@ class CFFConverterOutputSpec(TraitedSpec):
6759
connectome_file = File(exists=True, desc='Output connectome file')
6860

6961

70-
class CFFConverter(BaseInterface):
62+
class CFFConverter(CFFBaseInterface):
7163
"""
7264
Creates a Connectome File Format (CFF) file from input networks, surfaces, volumes, tracts, etcetera....
7365
@@ -87,6 +79,7 @@ class CFFConverter(BaseInterface):
8779
output_spec = CFFConverterOutputSpec
8880

8981
def _run_interface(self, runtime):
82+
import cfflib as cf
9083
a = cf.connectome()
9184

9285
if isdefined(self.inputs.title):
@@ -232,7 +225,7 @@ class MergeCNetworksOutputSpec(TraitedSpec):
232225
exists=True, desc='Output CFF file with all the networks added')
233226

234227

235-
class MergeCNetworks(BaseInterface):
228+
class MergeCNetworks(CFFBaseInterface):
236229
""" Merges networks from multiple CFF files into one new CFF file.
237230
238231
Example
@@ -248,6 +241,7 @@ class MergeCNetworks(BaseInterface):
248241
output_spec = MergeCNetworksOutputSpec
249242

250243
def _run_interface(self, runtime):
244+
import cfflib as cf
251245
extracted_networks = []
252246

253247
for i, con in enumerate(self.inputs.in_files):

nipype/interfaces/cmtk/nbs.py

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,11 @@
1010
import networkx as nx
1111

1212
from ... import logging
13-
from ...utils.misc import package_check
14-
from ..base import (BaseInterface, BaseInterfaceInputSpec, traits, File,
13+
from ..base import (LibraryBaseInterface, BaseInterfaceInputSpec, traits, File,
1514
TraitedSpec, InputMultiPath, OutputMultiPath, isdefined)
15+
from .base import have_cv
1616
iflogger = logging.getLogger('interface')
1717

18-
have_cv = True
19-
try:
20-
package_check('cviewer')
21-
except Exception as e:
22-
have_cv = False
23-
else:
24-
import cviewer.libs.pyconto.groupstatistics.nbs as nbs
25-
2618

2719
def ntwks_to_matrices(in_files, edge_key):
2820
first = nx.read_gpickle(in_files[0])
@@ -92,7 +84,7 @@ class NetworkBasedStatisticOutputSpec(TraitedSpec):
9284
desc='Output network with edges identified by the NBS')
9385

9486

95-
class NetworkBasedStatistic(BaseInterface):
87+
class NetworkBasedStatistic(LibraryBaseInterface):
9688
"""
9789
Calculates and outputs the average network given a set of input NetworkX gpickle files
9890
@@ -111,11 +103,10 @@ class NetworkBasedStatistic(BaseInterface):
111103
"""
112104
input_spec = NetworkBasedStatisticInputSpec
113105
output_spec = NetworkBasedStatisticOutputSpec
106+
_pkg = 'cviewer'
114107

115108
def _run_interface(self, runtime):
116-
117-
if not have_cv:
118-
raise ImportError("cviewer library is not available")
109+
from cviewer.libs.pyconto.groupstatistics import nbs
119110

120111
THRESH = self.inputs.threshold
121112
K = self.inputs.number_of_permutations

nipype/interfaces/cmtk/nx.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,12 @@
1414

1515
from ... import logging
1616
from ...utils.filemanip import split_filename
17-
from ...utils.misc import package_check
1817
from ..base import (BaseInterface, BaseInterfaceInputSpec, traits, File,
1918
TraitedSpec, InputMultiPath, OutputMultiPath, isdefined)
19+
from .base import have_cmp
2020

2121
iflogger = logging.getLogger('interface')
2222

23-
have_cmp = True
24-
try:
25-
package_check('cmp')
26-
except Exception as e:
27-
have_cmp = False
28-
else:
29-
import cmp
30-
3123

3224
def read_unknown_ntwk(ntwk):
3325
if not isinstance(ntwk, nx.classes.graph.Graph):

nipype/interfaces/cmtk/parcellation.py

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,22 @@
88
import os
99
import os.path as op
1010
import shutil
11-
import warnings
1211

1312
import numpy as np
1413
import nibabel as nb
1514
import networkx as nx
1615

1716
from ... import logging
18-
from ...utils.misc import package_check
19-
from ..base import (BaseInterface, BaseInterfaceInputSpec, traits, File,
17+
from ..base import (BaseInterface, LibraryBaseInterface,
18+
BaseInterfaceInputSpec, traits, File,
2019
TraitedSpec, Directory, isdefined)
20+
from .base import have_cmp
2121
iflogger = logging.getLogger('interface')
2222

23-
have_cmp = True
24-
try:
25-
package_check('cmp')
26-
except Exception as e:
27-
have_cmp = False
28-
else:
29-
import cmp
30-
from cmp.util import runCmd
31-
3223

3324
def create_annot_label(subject_id, subjects_dir, fs_dir, parcellation_name):
25+
import cmp
26+
from cmp.util import runCmd
3427
iflogger.info("Create the cortical labels necessary for our ROIs")
3528
iflogger.info("=================================================")
3629
fs_label_dir = op.join(op.join(subjects_dir, subject_id), 'label')
@@ -174,6 +167,8 @@ def create_annot_label(subject_id, subjects_dir, fs_dir, parcellation_name):
174167
def create_roi(subject_id, subjects_dir, fs_dir, parcellation_name, dilation):
175168
""" Creates the ROI_%s.nii.gz files using the given parcellation information
176169
from networks. Iteratively create volume. """
170+
import cmp
171+
from cmp.util import runCmd
177172
iflogger.info("Create the ROIs:")
178173
output_dir = op.abspath(op.curdir)
179174
fs_dir = op.join(subjects_dir, subject_id)
@@ -306,6 +301,8 @@ def create_roi(subject_id, subjects_dir, fs_dir, parcellation_name, dilation):
306301

307302

308303
def create_wm_mask(subject_id, subjects_dir, fs_dir, parcellation_name):
304+
import cmp
305+
import scipy.ndimage.morphology as nd
309306
iflogger.info("Create white matter mask")
310307
fs_dir = op.join(subjects_dir, subject_id)
311308
cmp_config = cmp.configuration.PipelineConfiguration()
@@ -328,11 +325,6 @@ def create_wm_mask(subject_id, subjects_dir, fs_dir, parcellation_name):
328325
aseg = nb.load(op.join(fs_dir, 'mri', 'aseg.nii.gz'))
329326
asegd = aseg.get_data()
330327

331-
try:
332-
import scipy.ndimage.morphology as nd
333-
except ImportError:
334-
raise Exception('Need scipy for binary erosion of white matter mask')
335-
336328
# need binary erosion function
337329
imerode = nd.binary_erosion
338330

@@ -438,6 +430,7 @@ def create_wm_mask(subject_id, subjects_dir, fs_dir, parcellation_name):
438430

439431
def crop_and_move_datasets(subject_id, subjects_dir, fs_dir, parcellation_name,
440432
out_roi_file, dilation):
433+
from cmp.util import runCmd
441434
fs_dir = op.join(subjects_dir, subject_id)
442435
cmp_config = cmp.configuration.PipelineConfiguration()
443436
cmp_config.parcellation_scheme = "Lausanne2008"
@@ -549,7 +542,7 @@ class ParcellateOutputSpec(TraitedSpec):
549542
)
550543

551544

552-
class Parcellate(BaseInterface):
545+
class Parcellate(LibraryBaseInterface):
553546
"""Subdivides segmented ROI file into smaller subregions
554547
555548
This interface implements the same procedure as in the ConnectomeMapper's
@@ -571,6 +564,8 @@ class Parcellate(BaseInterface):
571564

572565
input_spec = ParcellateInputSpec
573566
output_spec = ParcellateOutputSpec
567+
_pkg = 'cmp'
568+
imports = ('scipy', )
574569

575570
def _run_interface(self, runtime):
576571
if self.inputs.subjects_dir:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from __future__ import unicode_literals
3+
from ..base import CFFBaseInterface
4+
5+
6+
def test_CFFBaseInterface_inputs():
7+
input_map = dict(
8+
ignore_exception=dict(
9+
deprecated='1.0.0',
10+
nohash=True,
11+
usedefault=True,
12+
), )
13+
inputs = CFFBaseInterface.input_spec()
14+
15+
for key, metadata in list(input_map.items()):
16+
for metakey, value in list(metadata.items()):
17+
assert getattr(inputs.traits()[key], metakey) == value

nipype/interfaces/cmtk/tests/test_nbs.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ def test_importerror(creating_graphs, tmpdir):
4141

4242
with pytest.raises(ImportError) as e:
4343
nbs.run()
44-
assert "cviewer library is not available" == str(e.value)
4544

4645

4746
@pytest.mark.skipif(not have_cv, reason="cviewer has to be available")

0 commit comments

Comments
 (0)