Repository: nilearn/nilearn

Issue:
Bug in _uncompress_file
Hi!
(...still nilearn-sprinting in the train back to Marseille ;) )

I found a bug when trying to download .tar.gz archive files from osf.

I got an error when using _fetch_files:

```
from nilearn.datasets.utils import _fetch_files
opts = {'uncompress': True}
data_dir = '~/nilearn_data'
url = 'https://osf.io/vym9k/download'
_fetch_files(data_dir, [('sphere_right.gii', url, opts)])
```

After digging, I realized the problem is in _uncompress_file: the temporary downloaded file is actually called 'download' on disk (because of the url that's given by osf), and  _uncompress_file does not extract the files if the archive was a .tar.gz one (it does not return an error, and it erases the 'download' file)

Note that:
- if the archive is a zip file, everything works... (as in the fetch_icbm152_2009 function); the temporary file is also called 'download' on disk, but _uncompress_file handles it properly
- if the file on disk is actually called 'whatever.tar.gz' and you run _uncompress_file on it, it also handles it properly

Soooooooooo, to reproduce the problem, the easiest is that:
1. you type https://osf.io/vym9k/download in your browser to get the file
2. you rename the file manually in your unix shell
```
> mv sphere_mesh.tar.gz download
```
3. you try to use _uncompress_file on it
```
from nilearn.datasets.utils import _uncompress_file
_uncompress_file('download')
```
It will: 1. not extract the files in the archive, 2. erase the 'download' file, 3. not return an error.

I hope this is clear enough...

Sylvain




Discussion:

Resolving patch:
diff --git a/nilearn/plotting/displays.py b/nilearn/plotting/displays.py
--- a/nilearn/plotting/displays.py
+++ b/nilearn/plotting/displays.py
@@ -369,6 +369,13 @@ def transform_to_2d(self, data, affine):
         else:
             maximum_intensity_data = np.abs(data_selection).max(axis=max_axis)
 
+        # This work around can be removed bumping matplotlib > 2.1.0. See #1815
+        # in nilearn for the invention of this work around
+
+        if self.direction == 'l' and data_selection.min() is np.ma.masked and \
+                not (self.ax.get_xlim()[0] > self.ax.get_xlim()[1]):
+            self.ax.invert_xaxis()
+
         return np.rot90(maximum_intensity_data)
 
     def draw_position(self, size, bg_color, **kwargs):
@@ -789,12 +796,13 @@ def _map_show(self, img, type='imshow',
         ims = []
         to_iterate_over = zip(self.axes.values(), data_2d_list)
         for display_ax, data_2d in to_iterate_over:
-            if data_2d is not None:
+            if data_2d is not None and data_2d.min() is not np.ma.masked:
                 # If data_2d is completely masked, then there is nothing to
-                # plot. Hence, continued to loop over. This problem came up
-                # with matplotlib 2.1.0. See issue #9280 in matplotlib.
-                if data_2d.min() is np.ma.masked:
-                    continue
+                # plot. Hence, no point to do imshow(). Moreover, we see
+                # problem came up with matplotlib 2.1.0 (issue #9280) when
+                # data is completely masked or with numpy < 1.14
+                # (issue #4595). This work aroung can be removed when bumping
+                # matplotlib version above 2.1.0
                 im = display_ax.draw_2d(data_2d, data_bounds, bounding_box,
                                         type=type, **kwargs)
                 ims.append(im)
@@ -1837,7 +1845,7 @@ def add_graph(self, adjacency_matrix, node_coords,
                               vmin=edge_vmin, vmax=edge_vmax,
                               **edge_kwargs)
             # To obtain the brain left view, we simply invert the x axis
-            if ax.direction == 'l':
+            if ax.direction == 'l' and not (ax.ax.get_xlim()[0] > ax.ax.get_xlim()[1]):
                 ax.ax.invert_xaxis()
 
         if colorbar:
diff --git a/nilearn/plotting/img_plotting.py b/nilearn/plotting/img_plotting.py
--- a/nilearn/plotting/img_plotting.py
+++ b/nilearn/plotting/img_plotting.py
@@ -1079,6 +1079,8 @@ def plot_glass_brain(stat_map_img,
         The plotted image should be in MNI space for this function to work
         properly.
 
+        Only glass brain can be plotted by switching stat_map_img to None.
+
         Parameters
         ----------
         stat_map_img : Niimg-like object
@@ -1185,6 +1187,9 @@ def display_factory(display_mode):
         cbar_vmin=cbar_vmin, cbar_vmax=cbar_vmax, brain_color=brain_color,
         resampling_interpolation=resampling_interpolation, **kwargs)
 
+    if stat_map_img is None and 'l' in display.axes:
+        display.axes['l'].ax.invert_xaxis()
+
     return display
 
 


Test patch:
diff --git a/nilearn/plotting/tests/test_displays.py b/nilearn/plotting/tests/test_displays.py
--- a/nilearn/plotting/tests/test_displays.py
+++ b/nilearn/plotting/tests/test_displays.py
@@ -9,6 +9,7 @@
 from nilearn.plotting.displays import OrthoSlicer, XSlicer, OrthoProjector
 from nilearn.plotting.displays import TiledSlicer
 from nilearn.plotting.displays import LZRYProjector
+from nilearn.plotting.displays import LYRZProjector
 from nilearn.datasets import load_mni152_template
 
 ##############################################################################
@@ -45,7 +46,7 @@ def test_tiled_slicer():
     img = load_mni152_template()
     slicer = TiledSlicer.init_with_figure(img=img, cut_coords=(0, 0, 0),
                                           colorbar=True)
-    slicer.add_overlay(img, cmap=plt.cm.gray,colorbar=True)
+    slicer.add_overlay(img, cmap=plt.cm.gray, colorbar=True)
     # Forcing a layout here, to test the locator code
     with tempfile.TemporaryFile() as fp:
         slicer.savefig(fp)
@@ -85,6 +86,7 @@ def test_contour_fillings_levels_in_add_contours():
     # without passing levels, should work with default levels from
     # matplotlib
     oslicer.add_contours(img, filled=True)
+    oslicer.close()
 
 
 def test_user_given_cmap_with_colorbar():
@@ -112,6 +114,10 @@ def test_data_complete_mask():
     oslicer.add_overlay(img)
     oslicer.close()
 
+    lyrz_projector = LYRZProjector(cut_coords=(0, 0, 0, 0))
+    lyrz_projector.add_overlay(img)
+    lyrz_projector.close()
+
 
 def test_add_markers_cut_coords_is_none():
     # A special case test for add_markers when cut_coords are None. This
diff --git a/nilearn/plotting/tests/test_img_plotting.py b/nilearn/plotting/tests/test_img_plotting.py
--- a/nilearn/plotting/tests/test_img_plotting.py
+++ b/nilearn/plotting/tests/test_img_plotting.py
@@ -1009,3 +1009,21 @@ def test_plot_glass_brain_colorbar_having_nans():
     img = nibabel.Nifti1Image(data, np.eye(4))
     plot_glass_brain(img, colorbar=True)
     plt.close()
+
+
+def test_plot_glass_brain_display_modes_without_img():
+    # Smoke test for work around from PR #1888
+    fig = plot_glass_brain(None, display_mode='lr')
+    fig = plot_glass_brain(None, display_mode='lzry')
+    fig.close()
+
+
+def test_plot_glass_brain_with_completely_masked_img():
+    # Smoke test for PR #1888 with display modes having 'l'
+    data = np.zeros((10, 20, 30))
+    affine = np.eye(4)
+
+    img = nibabel.Nifti1Image(data, affine)
+    plot_glass_brain(img, display_mode='lzry')
+    plot_glass_brain(img, display_mode='lr')
+    plt.close()