Repository: mne-tools/mne-python

Issue:
support path-like in mne.make_forward_solution
after converting a tutorial to use `pathlib.Path` objects in #10846, CircleCI fails:

```
Traceback (most recent call last):
  File "/home/circleci/project/examples/inverse/mixed_source_space_inverse.py", line 101, in <module>
    fwd = mne.make_forward_solution(
  File "<decorator-gen-369>", line 12, in make_forward_solution
  File "/home/circleci/project/mne/forward/_make_forward.py", line 589, in make_forward_solution
    raise TypeError('info should be an instance of Info or string')
TypeError: info should be an instance of Info or string
```

So it seems `mne.make_forward_solution` is apparently not fully supporting path-like. Also the error message could be better (it could tell you what kind of instance it received).

cc @ashdrew 


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
@@ -6,8 +6,8 @@
 Compute MNE inverse solution on evoked data with a mixed source space
 =====================================================================
 
-Create a mixed source space and compute an MNE inverse solution on an evoked
-dataset.
+Create a mixed source space and compute an MNE inverse solution on an
+evoked dataset.
 """
 # Author: Annalisa Pascarella <a.pascarella@iac.cnr.it>
 #
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
@@ -22,7 +22,7 @@
 from ..transforms import (_ensure_trans, transform_surface_to, apply_trans,
                           _get_trans, _print_coord_trans, _coord_frame_name,
                           Transform, invert_transform)
-from ..utils import logger, verbose, warn, _pl, _validate_type
+from ..utils import logger, verbose, warn, _pl, _validate_type, _check_fname
 from ..source_space import (_ensure_src, _filter_source_spaces,
                             _make_discrete_source_space, _complete_vol_src)
 from ..source_estimate import VolSourceEstimate
@@ -537,10 +537,10 @@ def make_forward_solution(info, trans, src, bem, meg=True, eeg=True,
     ----------
     %(info_str)s
     %(trans)s
-    src : str | instance of SourceSpaces
+    src : path-like | instance of SourceSpaces
         If string, should be a source space filename. Can also be an
         instance of loaded or generated SourceSpaces.
-    bem : dict | str
+    bem : path-like | dict
         Filename of the BEM (e.g., "sample-5120-5120-5120-bem-sol.fif") to
         use, or a loaded sphere model (dict).
     meg : bool
@@ -585,10 +585,11 @@ def make_forward_solution(info, trans, src, bem, meg=True, eeg=True,
         bem_extra = 'instance of ConductorModel'
     else:
         bem_extra = bem
-    if not isinstance(info, (Info, str)):
-        raise TypeError('info should be an instance of Info or string')
-    if isinstance(info, str):
+    _validate_type(info, ('path-like', Info), 'info')
+    if not isinstance(info, Info):
         info_extra = op.split(info)[1]
+        info = _check_fname(info, must_exist=True, overwrite='read',
+                            name='info')
         info = read_info(info, verbose=False)
     else:
         info_extra = 'instance of Info'
diff --git a/mne/utils/docs.py b/mne/utils/docs.py
--- a/mne/utils/docs.py
+++ b/mne/utils/docs.py
@@ -1547,9 +1547,10 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75):
 """
 
 docdict['info_str'] = f"""
-info : mne.Info | str
-    {_info_base} If ``str``, then it should be a filepath to a file with
-    measurement information (e.g. :class:`mne.io.Raw`).
+info : mne.Info | path-like
+    {_info_base} If ``path-like``, it should be a :class:`str` or
+    :class:`pathlib.Path` to a file with measurement information
+    (e.g. :class:`mne.io.Raw`).
 """
 
 docdict['int_order_maxwell'] = """
@@ -3306,14 +3307,14 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75):
     during coregistration. Can also be ``'fsaverage'`` to use the built-in
     fsaverage transformation."""
 
-docdict['trans'] = """
-trans : str | dict | instance of Transform | None
-    %s
+docdict['trans'] = f"""
+trans : path-like | dict | instance of Transform | None
+    {_trans_base}
     If trans is None, an identity matrix is assumed.
 
     .. versionchanged:: 0.19
        Support for 'fsaverage' argument.
-""" % (_trans_base,)
+"""
 
 docdict['trans_not_none'] = """
 trans : str | dict | instance of Transform


Test patch:
diff --git a/mne/forward/tests/test_make_forward.py b/mne/forward/tests/test_make_forward.py
--- a/mne/forward/tests/test_make_forward.py
+++ b/mne/forward/tests/test_make_forward.py
@@ -1,6 +1,7 @@
 from itertools import product
 import os
 import os.path as op
+from pathlib import Path
 
 import pytest
 import numpy as np
@@ -225,8 +226,10 @@ def test_make_forward_solution_kit(tmp_path):
 def test_make_forward_solution():
     """Test making M-EEG forward solution from python."""
     with catch_logging() as log:
-        fwd_py = make_forward_solution(fname_raw, fname_trans, fname_src,
-                                       fname_bem, mindist=5., verbose=True)
+        # make sure everything can be path-like (gh #10872)
+        fwd_py = make_forward_solution(
+            Path(fname_raw), Path(fname_trans), Path(fname_src),
+            Path(fname_bem), mindist=5., verbose=True)
     log = log.getvalue()
     assert 'Total 258/258 points inside the surface' in log
     assert (isinstance(fwd_py, Forward))