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:
@SylvainTakerkart  asked me to replace on OSF the two .tar.gz files with the .zip files and send the two new links.
On it.


InterTVA: https://osf.io/aekp7/download
sphere mesh: https://osf.io/b79fy/download
Thanks! I can then move forward with writing my fetchers (just created a PR for the sphere meshes)...

But this bug will still need to be looked at at some point...
~File an issue, with all the details.~

Edit: Sorry I thought this was a PR.

Resolving patch:
diff --git a/nilearn/signal.py b/nilearn/signal.py
--- a/nilearn/signal.py
+++ b/nilearn/signal.py
@@ -488,7 +488,10 @@ def clean(signals, sessions=None, detrend=True, standardize='zscore',
         signals = as_ndarray(signals)
 
     if ensure_finite:
-        signals[np.logical_not(np.isfinite(signals))] = 0
+        mask = np.logical_not(np.isfinite(signals))
+        if mask.any():
+            signals = signals.copy()
+            signals[mask] = 0
 
     # Read confounds
     if confounds is not None:


Test patch:
diff --git a/nilearn/tests/test_signal.py b/nilearn/tests/test_signal.py
--- a/nilearn/tests/test_signal.py
+++ b/nilearn/tests/test_signal.py
@@ -474,6 +474,31 @@ def test_clean_frequencies():
     assert_true(np.sum(Pxx_den_high[f <= high_pass / 2.]) <= 1e-4)
 
 
+def test_clean_finite_no_inplace_mod():
+    """
+    Test for verifying that the passed in signal array is not modified.
+    For PR #2125 . This test is failing on master, passing in this PR.
+    """
+    rng = np.random.RandomState(0)
+    n_samples = 2
+    # n_features  Must be higher than 500
+    n_features = 501
+    x_orig, _, _ = generate_signals(n_features=n_features,
+                              length=n_samples)
+    x_orig_inital_copy = x_orig.copy()
+
+    x_orig_with_nans = x_orig.copy()
+    x_orig_with_nans[0, 0] = np.nan
+    x_orig_with_nans_initial_copy = x_orig_with_nans.copy()
+
+    cleaned_x_orig = clean(x_orig)
+    assert np.array_equal(x_orig, x_orig_inital_copy)
+
+    cleaned_x_orig_with_nans = clean(x_orig_with_nans, ensure_finite=True)
+    assert np.isnan(x_orig_with_nans_initial_copy[0, 0])
+    assert np.isnan(x_orig_with_nans[0, 0])
+
+
 def test_high_variance_confounds():
 
     # C and F order might take different paths in the function. Check that the