Skip to content

Commit ac2c4c0

Browse files
Mathieu Scheltiennelarsoner
andauthored
Backport #12803 and #12804 (#12806)
Co-authored-by: Eric Larson <larson.eric.d@gmail.com>
1 parent bf70766 commit ac2c4c0

File tree

8 files changed

+61
-25
lines changed

8 files changed

+61
-25
lines changed

doc/_includes/institutional-partners.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Current partners
1919
- `Children’s Hospital of Philadelphia Research Institute <https://www.research.chop.edu/imaging/>`_
2020
- `Donders Institute for Brain, Cognition and Behaviour at Radboud University <https://www.ru.nl/donders/>`_
2121
- `Harvard Medical School <https://hms.harvard.edu/>`_
22-
- `Human Neuroscience Platform at Fondation Campus Biotech Geneva <https://hnp.fcbg.ch/>`_
22+
- `Fondation Campus Biotech Geneva <https://fcbg.ch/>`_
2323
- `Institut national de recherche en informatique et en automatique <https://www.inria.fr/>`_
2424
- `Karl-Franzens-Universität Graz <https://www.uni-graz.at/>`_
2525
- `Massachusetts General Hospital <https://www.massgeneral.org/>`_

doc/changes/devel/12803.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix handling of MRI file-path in :class:`mne.SourceSpaces` and safeguard saving of :class:`pathlib.Path` with ``h5io`` by casting to :class:`str`, by `Mathieu Scheltienne`_.

doc/changes/devel/12804.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Cast ``fwd["info"]`` to :class:`~mne.Info` and ``fwd["src"]`` to :class:`~mne.SourceSpaces` when loading a forward solution from an HDF5 file, by `Mathieu Scheltienne`_.

doc/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,9 +1107,9 @@ def append_attr_meth_examples(app, what, name, obj, options, lines):
11071107
size=xl,
11081108
),
11091109
dict(
1110-
name="Human Neuroscience Platforn at Fondation Campus Biotech Geneva", # noqa E501
1110+
name="Fondation Campus Biotech Geneva",
11111111
img="FCBG.svg",
1112-
url="https://hnp.fcbg.ch/",
1112+
url="https://fcbg.ch/",
11131113
size=sm,
11141114
),
11151115
],

mne/_freesurfer.py

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -599,27 +599,30 @@ def read_talxfm(subject, subjects_dir=None, verbose=None):
599599
return mri_mni_t
600600

601601

602-
def _check_mri(mri, subject, subjects_dir):
602+
def _check_mri(mri, subject, subjects_dir) -> str:
603603
"""Check whether an mri exists in the Freesurfer subject directory."""
604-
_validate_type(mri, "path-like", "mri")
605-
if op.isfile(mri) and op.basename(mri) != mri:
606-
return mri
607-
if not op.isfile(mri):
604+
_validate_type(mri, "path-like", mri)
605+
mri = Path(mri)
606+
if mri.is_file() and mri.name != mri:
607+
return str(mri)
608+
elif not mri.is_file():
608609
if subject is None:
609610
raise FileNotFoundError(
610-
f"MRI file {mri!r} not found and no subject provided"
611+
f"MRI file {mri!r} not found and no subject provided."
611612
)
612-
subjects_dir = str(get_subjects_dir(subjects_dir, raise_error=True))
613-
mri = op.join(subjects_dir, subject, "mri", mri)
614-
if not op.isfile(mri):
615-
raise FileNotFoundError(f"MRI file {mri!r} not found")
616-
if op.basename(mri) == mri:
617-
err = (
618-
f"Ambiguous filename - found {mri!r} in current folder.\n"
619-
"If this is correct prefix name with relative or absolute path"
613+
subjects_dir = get_subjects_dir(subjects_dir, raise_error=True)
614+
mri = subjects_dir / subject / "mri" / mri
615+
if not mri.is_file():
616+
raise FileNotFoundError(
617+
f"MRI file {mri!r} not found in the subjects directory "
618+
f"{subjects_dir!r} for subject {subject}."
619+
)
620+
if mri.name == mri:
621+
raise OSError(
622+
f"Ambiguous filename - found {mri!r} in current folder. "
623+
"If this is correct prefix name with relative or absolute path."
620624
)
621-
raise OSError(err)
622-
return mri
625+
return str(mri)
623626

624627

625628
def _read_mri_info(path, units="m", return_img=False, use_nibabel=False):

mne/forward/forward.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
from ..label import Label
5454
from ..source_estimate import _BaseSourceEstimate, _BaseVectorSourceEstimate
5555
from ..source_space._source_space import (
56+
SourceSpaces,
5657
_get_src_nn,
5758
_read_source_spaces_from_tree,
5859
_set_source_space_vertices,
@@ -913,7 +914,10 @@ def _write_forward_hdf5(fname, fwd):
913914

914915
def _read_forward_hdf5(fname):
915916
read_hdf5, _ = _import_h5io_funcs()
916-
return Forward(read_hdf5(fname)["fwd"])
917+
fwd = Forward(read_hdf5(fname)["fwd"])
918+
fwd["info"] = Info(fwd["info"])
919+
fwd["src"] = SourceSpaces(fwd["src"])
920+
return fwd
917921

918922

919923
def _write_forward_solution(fid, fwd):

mne/source_space/_source_space.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,7 +1665,7 @@ def setup_volume_source_space(
16651665
16661666
.. note:: For a discrete source space (``pos`` is a dict),
16671667
``mri`` must be None.
1668-
mri : str | None
1668+
mri : path-like | None
16691669
The filename of an MRI volume (mgh or mgz) to create the
16701670
interpolation matrix over. Source estimates obtained in the
16711671
volume source space can then be morphed onto the MRI volume
@@ -1791,9 +1791,8 @@ def setup_volume_source_space(
17911791
mri = _check_mri(mri, subject, subjects_dir)
17921792
if isinstance(pos, dict):
17931793
raise ValueError(
1794-
"Cannot create interpolation matrix for "
1795-
"discrete source space, mri must be None if "
1796-
"pos is a dict"
1794+
"Cannot create interpolation matrix for discrete source space, mri "
1795+
"must be None if pos is a dict"
17971796
)
17981797

17991798
if volume_label is not None:

mne/utils/check.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,35 @@ def _import_h5py():
167167

168168
def _import_h5io_funcs():
169169
h5io = _soft_import("h5io", "HDF5-based I/O")
170-
return h5io.read_hdf5, h5io.write_hdf5
170+
171+
# Saving to HDF5 does not support pathlib.Path objects, which are more and more used
172+
# in MNE-Python.
173+
# Related issue in h5io: https://github.com/h5io/h5io/issues/113
174+
def cast_path_to_str(data: dict) -> dict:
175+
"""Cast all paths value to string in data."""
176+
keys2cast = []
177+
for key, value in data.items():
178+
if isinstance(value, dict):
179+
cast_path_to_str(value)
180+
if isinstance(value, Path):
181+
data[key] = value.as_posix()
182+
if isinstance(key, Path):
183+
keys2cast.append(key)
184+
for key in keys2cast:
185+
data[key.as_posix()] = data.pop(key)
186+
return data
187+
188+
def write_hdf5(fname, data, *args, **kwargs):
189+
"""Write h5 and cast all paths to string in data."""
190+
if isinstance(data, dict):
191+
data = cast_path_to_str(data)
192+
elif isinstance(data, list):
193+
for k, elt in enumerate(data):
194+
if isinstance(elt, dict):
195+
data[k] = cast_path_to_str(elt)
196+
h5io.write_hdf5(fname, data, *args, **kwargs)
197+
198+
return h5io.read_hdf5, write_hdf5
171199

172200

173201
def _import_pymatreader_funcs(purpose):

0 commit comments

Comments
 (0)