Repository: mne-tools/mne-python

Issue:
update path handling in examples/inverse/mixed_source_space_inverse.py
all of our `data_path` functions now return `pathlib.Path` objects, which allows things like the following:

https://github.com/mne-tools/mne-python/blob/37a9e5587443109515a85cdc4396af4e014ecf82/examples/datasets/brainstorm_data.py#L32-L35

Compare this to the old pattern (using `os.path.join`, often imported as `op.join`):

```py
raw_path = os.path.join(data_path, 'MEG', 'bst_raw',
                        'subj001_somatosensory_20111109_01_AUX-f.ds')
```

or the *really* old pattern (using the `+` operator on plain strings):

```py
raw_path = (data_path + '/MEG/bst_raw/' +
            'subj001_somatosensory_20111109_01_AUX-f.ds')
```

Update the paths in the file mentioned in the issue title to use the new pathlib-style, which is considered the modern, best-practice way to handle file paths.



Discussion:

Resolving patch:
diff --git a/examples/inverse/mixed_source_space_inverse.py b/examples/inverse/mixed_source_space_inverse.py
--- a/examples/inverse/mixed_source_space_inverse.py
+++ b/examples/inverse/mixed_source_space_inverse.py
@@ -15,7 +15,6 @@
 
 # %%
 
-import os.path as op
 import matplotlib.pyplot as plt
 
 from nilearn import plotting
@@ -26,21 +25,21 @@
 # Set dir
 data_path = mne.datasets.sample.data_path()
 subject = 'sample'
-data_dir = op.join(data_path, 'MEG', subject)
-subjects_dir = op.join(data_path, 'subjects')
-bem_dir = op.join(subjects_dir, subject, 'bem')
+data_dir = data_path / 'MEG' / subject
+subjects_dir = data_path / 'subjects'
+bem_dir = subjects_dir / subject / 'bem'
 
 # Set file names
-fname_mixed_src = op.join(bem_dir, '%s-oct-6-mixed-src.fif' % subject)
-fname_aseg = op.join(subjects_dir, subject, 'mri', 'aseg.mgz')
+fname_mixed_src = bem_dir / f'{subject}-oct-6-mixed-src.fif'
+fname_aseg = subjects_dir / subject / 'mri' / 'aseg.mgz'
 
-fname_model = op.join(bem_dir, '%s-5120-bem.fif' % subject)
-fname_bem = op.join(bem_dir, '%s-5120-bem-sol.fif' % subject)
+fname_model = bem_dir / f'{subject}-5120-bem.fif'
+fname_bem = bem_dir / f'{subject}-5120-bem-sol.fif'
 
-fname_evoked = data_dir + '/sample_audvis-ave.fif'
-fname_trans = data_dir + '/sample_audvis_raw-trans.fif'
-fname_fwd = data_dir + '/sample_audvis-meg-oct-6-mixed-fwd.fif'
-fname_cov = data_dir + '/sample_audvis-shrunk-cov.fif'
+fname_evoked = data_dir / f'{subject}_audvis-ave.fif'
+fname_trans = data_dir / f'{subject}_audvis_raw-trans.fif'
+fname_fwd = data_dir / f'{subject}_audvis-meg-oct-6-mixed-fwd.fif'
+fname_cov = data_dir / f'{subject}_audvis-shrunk-cov.fif'
 
 # %%
 # Set up our source space
@@ -92,9 +91,9 @@
 #
 # We can also export source positions to NIfTI file and visualize it again:
 
-nii_fname = op.join(bem_dir, '%s-mixed-src.nii' % subject)
+nii_fname = bem_dir / f'{subject}-mixed-src.nii'
 src.export_volume(nii_fname, mri_resolution=True, overwrite=True)
-plotting.plot_img(nii_fname, cmap='nipy_spectral')
+plotting.plot_img(str(nii_fname), cmap='nipy_spectral')
 
 # %%
 # Compute the fwd matrix
diff --git a/mne/forward/_make_forward.py b/mne/forward/_make_forward.py
--- a/mne/forward/_make_forward.py
+++ b/mne/forward/_make_forward.py
@@ -10,6 +10,7 @@
 
 from copy import deepcopy
 from contextlib import contextmanager
+from pathlib import Path
 import os
 import os.path as op
 
@@ -443,8 +444,10 @@ def _prepare_for_forward(src, mri_head_t, info, bem, mindist, n_jobs,
     cmd = 'make_forward_solution(%s)' % (', '.join([str(a) for a in arg_list]))
     mri_id = dict(machid=np.zeros(2, np.int32), version=0, secs=0, usecs=0)
 
+    info_trans = str(trans) if isinstance(trans, Path) else trans
     info = Info(chs=info['chs'], comps=info['comps'],
-                dev_head_t=info['dev_head_t'], mri_file=trans, mri_id=mri_id,
+                dev_head_t=info['dev_head_t'], mri_file=info_trans,
+                mri_id=mri_id,
                 meas_file=info_extra, meas_id=None, working_dir=os.getcwd(),
                 command_line=cmd, bads=info['bads'], mri_head_t=mri_head_t)
     info._update_redundant()
diff --git a/mne/utils/numerics.py b/mne/utils/numerics.py
--- a/mne/utils/numerics.py
+++ b/mne/utils/numerics.py
@@ -5,18 +5,17 @@
 #
 # License: BSD-3-Clause
 
-from contextlib import contextmanager
 import hashlib
-from io import BytesIO, StringIO
-from math import sqrt
 import numbers
 import operator
 import os
-import os.path as op
-from math import ceil
 import shutil
 import sys
+from contextlib import contextmanager
 from datetime import datetime, timedelta, timezone
+from io import BytesIO, StringIO
+from math import ceil, sqrt
+from pathlib import Path
 
 import numpy as np
 
@@ -427,7 +426,7 @@ def _replace_md5(fname):
     # adapted from sphinx-gallery
     assert fname.endswith('.new')
     fname_old = fname[:-4]
-    if op.isfile(fname_old) and hashfunc(fname) == hashfunc(fname_old):
+    if os.path.isfile(fname_old) and hashfunc(fname) == hashfunc(fname_old):
         os.remove(fname)
     else:
         shutil.move(fname, fname_old)
@@ -697,7 +696,7 @@ def object_size(x, memo=None):
     id_ = id(x)
     if id_ in memo:
         return 0  # do not add already existing ones
-    if isinstance(x, (bytes, str, int, float, type(None))):
+    if isinstance(x, (bytes, str, int, float, type(None), Path)):
         size = sys.getsizeof(x)
     elif isinstance(x, np.ndarray):
         # On newer versions of NumPy, just doing sys.getsizeof(x) works,


Test patch: