Repository: nipy/nipype

Issue:
Testing the install:  "UNEXPECTED EXCEPTION: ImportError("No module named 'bids'",)"
### Summary
While testing the installation with `python -c "import nipype; nipype.test()" ` I got the following error
```
==================================================================================================================================== FAILURES =====================================================================================================================================
_____________________________________________________________________________________________________________________ [doctest] nipype.interfaces.bids_utils ______________________________________________________________________________________________________________________
UNEXPECTED EXCEPTION: ImportError("No module named 'bids'",)
Traceback (most recent call last):

  File "/home/athanell/anaconda3/envs/SAH/lib/python3.5/doctest.py", line 1321, in __run
    compileflags, 1), test.globs)

  File "<doctest nipype.interfaces.bids_utils[1]>", line 1, in <module>

ImportError: No module named 'bids'

/home/athanell/.local/lib/python3.5/site-packages/nipype/interfaces/bids_utils.py:11: UnexpectedException
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
================================================================================================================ 1 failed, 143 passed, 5 skipped in 29.53 seconds =================================================================================================================
```
Is this something that will hinder the usage of nipype and should be fixed? If yes how should I go about it?

Thank you
 



### Platform details:

```
{'nibabel_version': '2.1.0', 'sys_executable': '/home/athanell/anaconda3/envs/SAH/bin/python', 'scipy_version': '0.19.1', 'networkx_version': '2.0', 'sys_platform': 'linux', 'pkg_path': '/home/athanell/.local/lib/python3.5/site-packages/nipype', 'commit_source': 'installation', 'nipype_version': '1.0.0-dev', 'numpy_version': '1.13.3', 'sys_version': '3.5.4 |Anaconda, Inc.| (default, Oct  5 2017, 08:00:22) \n[GCC 7.2.0]', 'commit_hash': 'e4e95f3', 'traits_version': '4.6.0'}
1.0.0-dev

```



Discussion:
@athanell - good catch there.  yes, this should be fixed.

the doctest should not assume bids is installed. so proper bids check should be in the `tests` folder. these tests should be run when `have_bids` is True. the doctest should be changed so that it doesn't actually run that line using the doctest skip option.

this also means that during the travis test, bids is being installed as a dependency by default, which should not be the case. 

so three things to fix:

1. copy doctests over to the tests folder into a new test file
2. modify doctests to skip the run command
3. fix travis/nipype install so that non-mandatory dependencies are not installed.

if you send a PR, we can help you work through it.


Thank you @satra 
keeping this open till this is fixed. 
I pointed this problem out here: https://github.com/nipy/nipype/issues/2207#issuecomment-334313563

Resolving patch:
diff --git a/nipype/interfaces/bids_utils.py b/nipype/interfaces/bids_utils.py
--- a/nipype/interfaces/bids_utils.py
+++ b/nipype/interfaces/bids_utils.py
@@ -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)
 
+    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,
@@ -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')
 
@@ -56,9 +54,6 @@ 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.
@@ -66,12 +61,7 @@ class BIDSDataGrabber(BaseInterface):
     >>> 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
@@ -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
@@ -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):
diff --git a/nipype/utils/filemanip.py b/nipype/utils/filemanip.py
--- a/nipype/utils/filemanip.py
+++ b/nipype/utils/filemanip.py
@@ -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


Test patch:
diff --git a/nipype/interfaces/tests/test_bids.py b/nipype/interfaces/tests/test_bids.py
new file mode 100644
--- /dev/null
+++ b/nipype/interfaces/tests/test_bids.py
@@ -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'),
+                    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'
diff --git a/nipype/interfaces/tests/test_resource_monitor.py b/nipype/interfaces/tests/test_resource_monitor.py
--- a/nipype/interfaces/tests/test_resource_monitor.py
+++ b/nipype/interfaces/tests/test_resource_monitor.py
@@ -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):
diff --git a/nipype/testing/data/ds005/filler.txt b/nipype/testing/data/ds005/filler.txt
new file mode 100644