Repository: nilearn/nilearn

Issue:
`fetch_atlas_harvard_oxford` returns same object for `maps` and `filename`
This is also true for `fetch_atlas_juelich` since they both share the same internal logic.
`maps` should indeed be the `Nifti1Image`, but we should either remove `filename` or change it to be the path to the `Nifti1Image`.

Also, this is the only fetcher which returns a `Bunch` object without a `description`.

```python
from nilearn import datasets
b = datasets.fetch_atlas_harvard_oxford("cort-maxprob-thr0-1mm")
assert b.maps == b.filename
b.filename
```
```
<nibabel.nifti1.Nifti1Image at 0x7f1ff37a4280>
```
```python
b.maps
```
```
<nibabel.nifti1.Nifti1Image at 0x7f1ff37a4280>
```


Discussion:
From what I understand, the atlas image is loaded [here](https://github.com/nilearn/nilearn/blob/e1c2f8bd9cd22d9cce9028bb3a56ac7df73810b5/nilearn/datasets/atlas.py#L408) from the `_get_atlas_data_and_labels`.

The solution to expose the atlas filename would be to also return the filename defined [here](https://github.com/nilearn/nilearn/blob/e1c2f8bd9cd22d9cce9028bb3a56ac7df73810b5/nilearn/datasets/atlas.py#L584) in the `_get_atlas_data_and_labels` method.

Does that seem a fair solution to you? I might improve the according tests as well in the process.
Sounds right.

Resolving patch:
diff --git a/nilearn/datasets/atlas.py b/nilearn/datasets/atlas.py
--- a/nilearn/datasets/atlas.py
+++ b/nilearn/datasets/atlas.py
@@ -405,7 +405,12 @@ def fetch_atlas_harvard_oxford(atlas_name, data_dir=None,
     if is_probabilistic and symmetric_split:
         raise ValueError("Region splitting not supported for probabilistic "
                          "atlases")
-    atlas_img, names, is_lateralized = _get_atlas_data_and_labels(
+    (
+        atlas_img,
+        atlas_filename,
+        names,
+        is_lateralized
+    ) = _get_atlas_data_and_labels(
         "HarvardOxford",
         atlas_name,
         symmetric_split=symmetric_split,
@@ -414,14 +419,18 @@ def fetch_atlas_harvard_oxford(atlas_name, data_dir=None,
         verbose=verbose)
     atlas_niimg = check_niimg(atlas_img)
     if not symmetric_split or is_lateralized:
-        return Bunch(filename=atlas_img, maps=atlas_niimg, labels=names)
+        return Bunch(filename=atlas_filename, maps=atlas_niimg, labels=names)
     new_atlas_data, new_names = _compute_symmetric_split("HarvardOxford",
                                                          atlas_niimg,
                                                          names)
     new_atlas_niimg = new_img_like(atlas_niimg,
                                    new_atlas_data,
                                    atlas_niimg.affine)
-    return Bunch(filename=atlas_img, maps=new_atlas_niimg, labels=new_names)
+    return Bunch(
+        filename=atlas_filename,
+        maps=new_atlas_niimg,
+        labels=new_names,
+    )
 
 
 @fill_doc
@@ -521,7 +530,7 @@ def fetch_atlas_juelich(atlas_name, data_dir=None,
     if is_probabilistic and symmetric_split:
         raise ValueError("Region splitting not supported for probabilistic "
                          "atlases")
-    atlas_img, names, _ = _get_atlas_data_and_labels("Juelich",
+    atlas_img, atlas_filename, names, _ = _get_atlas_data_and_labels("Juelich",
                                                      atlas_name,
                                                      data_dir=data_dir,
                                                      resume=resume,
@@ -542,7 +551,7 @@ def fetch_atlas_juelich(atlas_name, data_dir=None,
     new_atlas_niimg = new_img_like(atlas_niimg,
                                    new_atlas_data,
                                    atlas_niimg.affine)
-    return Bunch(filename=atlas_img, maps=new_atlas_niimg,
+    return Bunch(filename=atlas_filename, maps=new_atlas_niimg,
                  labels=list(new_names))
 
 
@@ -605,7 +614,7 @@ def _get_atlas_data_and_labels(atlas_source, atlas_name, symmetric_split=False,
     # The label indices should range from 0 to nlabel + 1
     assert list(names.keys()) == list(range(n + 2))
     names = [item[1] for item in sorted(names.items())]
-    return atlas_img, names, is_lateralized
+    return atlas_img, atlas_file, names, is_lateralized
 
 
 def _merge_probabilistic_maps_juelich(atlas_data, names):


Test patch:
diff --git a/nilearn/datasets/tests/test_atlas.py b/nilearn/datasets/tests/test_atlas.py
--- a/nilearn/datasets/tests/test_atlas.py
+++ b/nilearn/datasets/tests/test_atlas.py
@@ -150,7 +150,7 @@ def test_fetch_atlas_source(tmp_path, request_mocker):
         atlas._get_atlas_data_and_labels('new_source', 'not_inside')
 
 
-def _write_to_xml(ho_dir, filename, is_symm):
+def _write_sample_atlas_metadata(ho_dir, filename, is_symm):
     with open(os.path.join(ho_dir, filename + '.xml'), 'w') as dm:
         if(not is_symm):
             dm.write("<?xml version='1.0' encoding='us-ascii'?>\n"
@@ -172,26 +172,18 @@ def _write_to_xml(ho_dir, filename, is_symm):
         dm.close()
 
 
-def _test_result_xml(res, is_symm):
-    if not is_symm:
-        assert isinstance(res.maps, nibabel.Nifti1Image)
-        assert isinstance(res.labels, list)
-        assert len(res.labels) == 4
-        assert res.labels[0] == "Background"
-        assert res.labels[1] == "R1"
-        assert res.labels[2] == "R2"
-        assert res.labels[3] == "R3"
-    else:
-        assert isinstance(res.maps, nibabel.Nifti1Image)
-        assert isinstance(res.labels, list)
-        assert len(res.labels) == 7
-        assert res.labels[0] == "Background"
-        assert res.labels[1] == "Left R1"
-        assert res.labels[2] == "Right R1"
-        assert res.labels[3] == "Left R2"
-        assert res.labels[4] == "Right R2"
-        assert res.labels[5] == "Left R3"
-        assert res.labels[6] == "Right R3"
+def _test_atlas_instance_should_match_data(atlas, is_symm):
+    assert isinstance(atlas.filename, str) or isinstance(atlas.filename, Path)
+    assert isinstance(atlas.maps, nibabel.Nifti1Image)
+    assert isinstance(atlas.labels, list)
+
+    expected_atlas_labels = (
+        ["Background", "Left R1", "Right R1", "Left R2",
+         "Right R2", "Left R3", "Right R3"]
+        if is_symm
+        else ["Background", "R1", "R2", "R3"]
+    )
+    assert atlas.labels == expected_atlas_labels
 
 
 @pytest.fixture
@@ -242,17 +234,29 @@ def atlas_data():
                           ("Juelich", "", "maxprob-thr0-1mm", False, True)])
 def test_fetch_atlas_fsl(name, label_fname, fname, is_symm, split,
                          atlas_data, fsl_fetcher, tmp_path, request_mocker):
-    ho_dir = str(tmp_path / 'fsl' / 'data' / 'atlases')
-    os.makedirs(ho_dir)
-    nifti_dir = os.path.join(ho_dir, name)
-    os.makedirs(nifti_dir)
-    _write_to_xml(ho_dir, f"{name}{label_fname}", is_symm=is_symm)
-    target_atlas_fname = f'{name}-{fname}.nii.gz'
-    target_atlas_nii = os.path.join(nifti_dir, target_atlas_fname)
+    # Create directory which will contain fake atlas data
+    atlas_dir = tmp_path / 'fsl' / 'data' / 'atlases'
+    nifti_dir = atlas_dir / name
+    nifti_dir.mkdir(parents=True)
+    # Write labels and sample atlas image in directory
+    _write_sample_atlas_metadata(
+        atlas_dir,
+        f"{name}{label_fname}",
+        is_symm=is_symm,
+    )
+    target_atlas_nii = nifti_dir / f'{name}-{fname}.nii.gz'
     nibabel.Nifti1Image(atlas_data, np.eye(4) * 3).to_filename(
         target_atlas_nii)
-    ho_wo = fsl_fetcher(fname, data_dir=str(tmp_path), symmetric_split=split)
-    _test_result_xml(ho_wo, is_symm=is_symm or split)
+    # Check that the fetch lead to consistent results
+    atlas_instance = fsl_fetcher(
+        fname,
+        data_dir=tmp_path,
+        symmetric_split=split,
+    )
+    _test_atlas_instance_should_match_data(
+        atlas_instance,
+        is_symm=is_symm or split,
+    )
 
 
 def test_fetch_atlas_craddock_2012(tmp_path, request_mocker):