Repository: nipy/nipype

Issue:
Crash/difficulty while building docs
### Summary

It might be the perks of my setup but decided to make a record so if it comes useful for someone else before jumping into trying to figure it out: while building debian package for 1.0.1 (1.0.0+git69-gdb2670326-1 didn't show such problem):

```
: # TODO: We need to set matplotlib backend to Agg before building...
export PYTHONPATH=$PWD MPLCONFIGDIR=/build/nipype-1.0.1/build; cd doc; LC_ALL=C.UTF-8 /usr/bin/make html
make[1]: Entering directory '/build/nipype-1.0.1/doc'
rm -rf _build/* *~ api/generated interfaces/generated users/examples documentation.zip
mkdir -p users/examples
../tools/make_examples.py --no-exec
examples2rst finished.
rm -rf api/generated
python -u ../tools/build_modref_templates.py
Traceback (most recent call last):
  File "../tools/build_modref_templates.py", line 20, in <module>
    docwriter = ApiDocWriter(package)
  File "/build/nipype-1.0.1/tools/apigen.py", line 76, in __init__
    self.package_name = package_name
  File "/build/nipype-1.0.1/tools/apigen.py", line 98, in set_package_name
    self.root_module = __import__(package_name)
  File "/build/nipype-1.0.1/nipype/__init__.py", line 51, in <module>
    from .pipeline import Node, MapNode, JoinNode, Workflow
  File "/build/nipype-1.0.1/nipype/pipeline/__init__.py", line 11, in <module>
    from .engine import Node, MapNode, JoinNode, Workflow
  File "/build/nipype-1.0.1/nipype/pipeline/engine/__init__.py", line 12, in <module>
    from .workflows import Workflow
  File "/build/nipype-1.0.1/nipype/pipeline/engine/workflows.py", line 36, in <module>
    from ...interfaces.base import (traits, TraitedSpec, TraitDictObject,
  File "/build/nipype-1.0.1/nipype/interfaces/__init__.py", line 13, in <module>
    from .io import DataGrabber, DataSink, SelectFiles, BIDSDataGrabber
  File "/build/nipype-1.0.1/nipype/interfaces/io.py", line 41, in <module>
    from ..utils.filemanip import copyfile, list_to_filename, filename_to_list
  File "/build/nipype-1.0.1/nipype/utils/filemanip.py", line 321, in <module>
    _cifs_table = _generate_cifs_table()
  File "/build/nipype-1.0.1/nipype/utils/filemanip.py", line 318, in _generate_cifs_table
    return _parse_mount_table(exit_code, output)
  File "/build/nipype-1.0.1/nipype/utils/filemanip.py", line 297, in _parse_mount_table
    key=lambda x: len(x[0]), reverse=True)
  File "/build/nipype-1.0.1/nipype/utils/filemanip.py", line 296, in <genexpr>
    for l in output.splitlines()),
AttributeError: 'NoneType' object has no attribute 'groups'
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/usr/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "/build/nipype-1.0.1/nipype/utils/config.py", line 360, in free_display
    from .. import config
SystemError: Parent module 'nipype' not loaded, cannot perform relative import
Error in sys.exitfunc:
Traceback (most recent call last):
  File "/usr/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "/build/nipype-1.0.1/nipype/utils/config.py", line 360, in free_display
    from .. import config
SystemError: Parent module 'nipype' not loaded, cannot perform relative import
make[1]: *** [Makefile:38: api] Error 1
make[1]: Leaving directory '/build/nipype-1.0.1/doc'
make: *** [debian/rules:34: doc-stamp] Error 2
dpkg-buildpackage: error: fakeroot debian/rules binary subprocess returned exit status 2

```

what makes it tricky -- upon rerun of `make html` there it doesn't fail... so there is some "state" which makes it trick(ier) to reproduce


Discussion:
This is related to #2444. Can you give the output of `mount` on the system you ran this on?
it is in a chroot(like) env
```
# mount
/proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,relatime)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
devpts on /dev/ptmx type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
```
Hmm. That looks fine. Are there any extra blank lines? That might cause the issue.

Though we should handle failures to match better, anyway...
That is a hook attached to the end of the python session to tear down any possible Xvfb open. I think it is safe to wrap the import within a ```try ... except SystemError```

Resolving patch:
diff --git a/nipype/utils/filemanip.py b/nipype/utils/filemanip.py
--- a/nipype/utils/filemanip.py
+++ b/nipype/utils/filemanip.py
@@ -185,7 +185,8 @@ def fname_presuffix(fname, prefix='', suffix='', newpath=None, use_ext=True):
     '/tmp/prefoopost.nii.gz'
 
     >>> from nipype.interfaces.base import Undefined
-    >>> fname_presuffix(fname, 'pre', 'post', Undefined) == fname_presuffix(fname, 'pre', 'post')
+    >>> fname_presuffix(fname, 'pre', 'post', Undefined) == \
+            fname_presuffix(fname, 'pre', 'post')
     True
 
     """
@@ -289,15 +290,25 @@ def _parse_mount_table(exit_code, output):
     #                          <PATH>^^^^      ^^^^^<FSTYPE>
     # OSX mount example:    /dev/disk2 on / (hfs, local, journaled)
     #                               <PATH>^  ^^^<FSTYPE>
-    pattern = re.compile(r'.*? on (/.*?) (?:type |\()([^\s,]+)(?:, |\)| )')
+    pattern = re.compile(r'.*? on (/.*?) (?:type |\()([^\s,\)]+)')
+
+    # Keep line and match for error reporting (match == None on failure)
+    # Ignore empty lines
+    matches = [(l, pattern.match(l))
+               for l in output.strip().splitlines() if l]
 
     # (path, fstype) tuples, sorted by path length (longest first)
-    mount_info = sorted((pattern.match(l).groups()
-                         for l in output.splitlines()),
+    mount_info = sorted((match.groups() for _, match in matches
+                         if match is not None),
                         key=lambda x: len(x[0]), reverse=True)
     cifs_paths = [path for path, fstype in mount_info
                   if fstype.lower() == 'cifs']
 
+    # Report failures as warnings
+    for line, match in matches:
+        if match is None:
+            fmlogger.debug("Cannot parse mount line: '%s'", line)
+
     return [
         mount for mount in mount_info
         if any(mount[0].startswith(path) for path in cifs_paths)


Test patch:
diff --git a/nipype/utils/tests/test_filemanip.py b/nipype/utils/tests/test_filemanip.py
--- a/nipype/utils/tests/test_filemanip.py
+++ b/nipype/utils/tests/test_filemanip.py
@@ -451,7 +451,16 @@ def test_related_files(file, length, expected_files):
 tmpfs on /proc/sched_debug type tmpfs (rw,nosuid,size=65536k,mode=755)
 tmpfs on /proc/scsi type tmpfs (ro,relatime)
 tmpfs on /sys/firmware type tmpfs (ro,relatime)
-''', 0, [('/data', 'cifs')])
+''', 0, [('/data', 'cifs')]),
+# From @yarikoptic - added blank lines to test for resilience
+(r'''/proc on /proc type proc (rw,relatime)
+sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
+tmpfs on /dev/shm type tmpfs (rw,relatime)
+devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
+
+devpts on /dev/ptmx type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
+
+''', 0, []),
 )