Skip to content

fix+tst: ensure no pybids does not break testing #2248

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Oct 28, 2017
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ before_install:
hash -r &&
conda config --set always_yes yes --set changeps1 no &&
conda update -q conda &&
conda install python=${TRAVIS_PYTHON_VERSION} &&
conda config --add channels conda-forge &&
conda install -y nipype icu &&
rm -r ${CONDA_HOME}/lib/python${TRAVIS_PYTHON_VERSION}/site-packages/nipype*;
conda install python=${TRAVIS_PYTHON_VERSION} &&
conda install -y icu &&
pip install -r requirements.txt &&
pushd $HOME;
git clone https://github.com/INCF/pybids.git;
cd pybids;
Expand Down
46 changes: 17 additions & 29 deletions nipype/interfaces/bids_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@

BIDSDataGrabber: Query data from BIDS dataset using pybids grabbids.

Change directory to provide relative paths for doctests
>>> import os
>>> import bids
>>> filepath = os.path.realpath(os.path.dirname(bids.__file__))
>>> datadir = os.path.realpath(os.path.join(filepath, 'grabbids/tests/data/'))
>>> os.chdir(datadir)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can replace this with the standard header that changes to the testing/data folder.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't we skipping these tests anyways since it would require pybids to be installed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct, but we can make a directory ds005 with a dummy file (since git does not store empty directories), just not call the bidsgrabber run function. we won't actually call pybids here.


Change directory to provide relative paths for doctests
>>> import os
>>> filepath = os.path.dirname( os.path.realpath( __file__ ) )
>>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data'))
>>> os.chdir(datadir)
"""
from os.path import join, dirname
import json
from .. import logging
from .base import (traits,
DynamicTraitedSpec,
Expand All @@ -24,13 +24,11 @@
Str,
Undefined)

have_pybids = True
try:
from bids import grabbids as gb
import json
except ImportError:
have_pybids = False
else:
have_pybids = True

LOGGER = logging.getLogger('workflows')

Expand All @@ -56,22 +54,14 @@ class BIDSDataGrabber(BaseInterface):
Examples
--------

>>> from nipype.interfaces.bids_utils import BIDSDataGrabber
>>> from os.path import basename

By default, the BIDSDataGrabber fetches anatomical and functional images
from a project, and makes BIDS entities (e.g. subject) available for
filtering outputs.

>>> bg = BIDSDataGrabber()
>>> bg.inputs.base_dir = 'ds005/'
>>> bg.inputs.subject = '01'
>>> results = bg.run()
>>> basename(results.outputs.anat[0]) # doctest: +ALLOW_UNICODE
'sub-01_T1w.nii.gz'

>>> basename(results.outputs.func[0]) # doctest: +ALLOW_UNICODE
'sub-01_task-mixedgamblestask_run-01_bold.nii.gz'
>>> results = bg.run() # doctest: +SKIP


Dynamically created, user-defined output fields can also be defined to
Expand All @@ -83,9 +73,7 @@ class BIDSDataGrabber(BaseInterface):
>>> bg.inputs.base_dir = 'ds005/'
>>> bg.inputs.subject = '01'
>>> bg.inputs.output_query['dwi'] = dict(modality='dwi')
>>> results = bg.run()
>>> basename(results.outputs.dwi[0]) # doctest: +ALLOW_UNICODE
'sub-01_dwi.nii.gz'
>>> results = bg.run() # doctest: +SKIP

"""
input_spec = BIDSDataGrabberInputSpec
Expand All @@ -104,32 +92,32 @@ def __init__(self, infields=None, **kwargs):
If no matching items, returns Undefined.
"""
super(BIDSDataGrabber, self).__init__(**kwargs)
if not have_pybids:
raise ImportError(
"The BIDSEventsGrabber interface requires pybids."
" Please make sure it is installed.")

if not isdefined(self.inputs.output_query):
self.inputs.output_query = {"func": {"modality": "func"},
"anat": {"modality": "anat"}}

# If infields is None, use all BIDS entities
if infields is None:
# If infields is empty, use all BIDS entities
if not infields is None and have_pybids:
bids_config = join(dirname(gb.__file__), 'config', 'bids.json')
bids_config = json.load(open(bids_config, 'r'))
infields = [i['name'] for i in bids_config['entities']]

self._infields = infields
self._infields = infields or []

# used for mandatory inputs check
undefined_traits = {}
for key in infields:
for key in self._infields:
self.inputs.add_trait(key, traits.Any)
undefined_traits[key] = kwargs[key] if key in kwargs else Undefined

self.inputs.trait_set(trait_change_notify=False, **undefined_traits)

def _run_interface(self, runtime):
if not have_pybids:
raise ImportError(
"The BIDSEventsGrabber interface requires pybids."
" Please make sure it is installed.")
return runtime

def _list_outputs(self):
Expand Down
50 changes: 50 additions & 0 deletions nipype/interfaces/tests/test_bids.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import os
import json
import sys

import pytest
from nipype.interfaces.bids_utils import BIDSDataGrabber
from nipype.utils.filemanip import dist_is_editable

have_pybids = True
try:
import bids
from bids import grabbids as gb
filepath = os.path.realpath(os.path.dirname(bids.__file__))
datadir = os.path.realpath(os.path.join(filepath, 'grabbids/tests/data/'))
except ImportError:
have_pybids = False


# There are three reasons these tests will be skipped:
@pytest.mark.skipif(not have_pybids,
reason="Pybids is not installed")
@pytest.mark.skipif(sys.version_info < (3, 0),
reason="Pybids no longer supports Python 2")
@pytest.mark.skipif(not dist_is_editable('pybids'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this bit necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from the initial #2174 - that's how the testing data was done

also #2207

reason="Pybids is not installed in editable mode")
def test_bids_grabber(tmpdir):
tmpdir.chdir()
bg = BIDSDataGrabber()
bg.inputs.base_dir = os.path.join(datadir, 'ds005')
bg.inputs.subject = '01'
results = bg.run()
assert os.path.basename(results.outputs.anat[0]) == 'sub-01_T1w.nii.gz'
assert os.path.basename(results.outputs.func[0]) == (
'sub-01_task-mixedgamblestask_run-01_bold.nii.gz')


@pytest.mark.skipif(not have_pybids,
reason="Pybids is not installed")
@pytest.mark.skipif(sys.version_info < (3, 0),
reason="Pybids no longer supports Python 2")
@pytest.mark.skipif(not dist_is_editable('pybids'),
reason="Pybids is not installed in editable mode")
def test_bids_fields(tmpdir):
tmpdir.chdir()
bg = BIDSDataGrabber(infields = ['subject'], outfields = ['dwi'])
bg.inputs.base_dir = os.path.join(datadir, 'ds005')
bg.inputs.subject = '01'
bg.inputs.output_query['dwi'] = dict(modality='dwi')
results = bg.run()
assert os.path.basename(results.outputs.dwi[0]) == 'sub-01_dwi.nii.gz'
1 change: 1 addition & 0 deletions nipype/interfaces/tests/test_resource_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class UseResources(CommandLine):
_always_run = True


@pytest.mark.skip(reason="inconsistent readings")
@pytest.mark.skipif(os.getenv('CI_SKIP_TEST', False), reason='disabled in CI tests')
@pytest.mark.parametrize("mem_gb,n_procs", [(0.5, 3), (2.2, 8), (0.8, 4), (1.5, 1)])
def test_cmdline_profiling(tmpdir, mem_gb, n_procs):
Expand Down
Empty file.
17 changes: 17 additions & 0 deletions nipype/utils/filemanip.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,3 +646,20 @@ def write_rst_dict(info, prefix=''):
for key, value in sorted(info.items()):
out.append('{}* {} : {}'.format(prefix, key, str(value)))
return '\n'.join(out) + '\n\n'


def dist_is_editable(dist):
"""Is distribution an editable install?

Parameters
----------
dist : string
Package name

# Borrowed from `pip`'s' API
"""
for path_item in sys.path:
egg_link = os.path.join(path_item, dist + '.egg-link')
if os.path.isfile(egg_link):
return True
return False