Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6cb7bea
WIP: Expand ~ in _check_fname()
hoechenberger Jul 26, 2021
5006a4a
docstyle
hoechenberger Jul 28, 2021
996f16b
Add more data types
hoechenberger Jul 28, 2021
438ee16
Merge branch 'main' of https://github.com/mne-tools/mne-python into e…
hoechenberger Jul 28, 2021
2c0d14c
Fix typo
hoechenberger Jul 28, 2021
b4d589f
Add another one
hoechenberger Jul 28, 2021
98f495d
Fixes
hoechenberger Jul 28, 2021
540cc57
Fix fix fix 🐞
hoechenberger Jul 28, 2021
4567d42
Fix FIFF
hoechenberger Jul 28, 2021
97cbbe1
Fix KIT
hoechenberger Jul 28, 2021
5669af2
Fix STC
hoechenberger Jul 28, 2021
80bd7fb
Fix test_plot_evoked_cov()
hoechenberger Jul 28, 2021
704c9f1
Merge branch 'main' of https://github.com/mne-tools/mne-python into e…
hoechenberger Sep 29, 2021
b1d4df3
Merge branch 'main' of https://github.com/mne-tools/mne-python into e…
hoechenberger Oct 5, 2021
44b3863
Fix EEGLAB
hoechenberger Oct 5, 2021
70dd640
Don't touch users' home directory during tests
hoechenberger Oct 5, 2021
1356a2d
Fix docstring
hoechenberger Oct 5, 2021
1c49564
Fix type check
hoechenberger Oct 5, 2021
5209027
Revert doc change
hoechenberger Oct 5, 2021
d45fcd6
Address reviewer comments
hoechenberger Oct 5, 2021
c5f031f
Merge branch 'main' of https://github.com/mne-tools/mne-python into e…
hoechenberger Oct 5, 2021
9a1d5da
Expand `~` in get_subjects_dir() too
hoechenberger Oct 5, 2021
e389a27
Fix for subjects_dir=None
hoechenberger Oct 5, 2021
2c989c5
Fix tests
hoechenberger Oct 6, 2021
2d56533
Try to fix tests on Windows
hoechenberger Oct 6, 2021
6e874f5
Try to fix another test on Windows
hoechenberger Oct 6, 2021
16e865a
Update changelog [skip azp][skip azure]
hoechenberger Oct 6, 2021
921d1e1
Rename _check_path_like() -> _path_like()
hoechenberger Oct 8, 2021
8ddd827
Add TODO comments to remind us of missing overwrite params
hoechenberger Oct 8, 2021
b22313d
Rephrase changelog entry
hoechenberger Oct 8, 2021
25a85a4
Slightly chang _check_fname() logic as suggested in review
hoechenberger Oct 8, 2021
a80604e
Merge branch 'main' of https://github.com/mne-tools/mne-python into e…
hoechenberger Oct 8, 2021
bd76c1e
It seems something went wrong when merging main 🤕
hoechenberger Oct 8, 2021
8a0c8b0
Fix a typo
hoechenberger Oct 8, 2021
48c2504
Fix more issues resulting from merging main
hoechenberger Oct 8, 2021
41555e2
And another one
hoechenberger Oct 8, 2021
622da4d
Hopefully the last one
hoechenberger Oct 8, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ Enhancements

- Add :func:`mne.preprocessing.ieeg.project_sensors_onto_brain` to project ECoG sensors onto the pial surface to compensate for brain shift (:gh:`9800` by `Alex Rockhill`_)

- All functions for reading and writing files should now automatically handle ``~`` (the tilde character) and expand it to the user's home directory. Should you come across any function that doesn't do it, please do let us know! (:gh:`9613` by `Richard Höchenberger`_)

- All functions accepting a FreeSurfer subjects directory via a ``subjects_dir`` parameter can now consume :py:class:`pathlib.Path` objects too (used to be only strings) (:gh:`9613` by `Richard Höchenberger`_)

Bugs
~~~~
- Fix bug in :meth:`mne.io.Raw.pick` and related functions when parameter list contains channels which are not in info instance (:gh:`9708` **by new contributor** |Evgeny Goldstein|_)
Expand Down
6 changes: 4 additions & 2 deletions mne/cov.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
warn, copy_function_doc_to_method_doc, _pl,
_undo_scaling_cov, _scaled_array, _validate_type,
_check_option, eigh, fill_doc, _on_missing,
_check_on_missing)
_check_on_missing, _check_fname)
from . import viz

from .fixes import (BaseEstimator, EmpiricalCovariance, _logdet,
Expand Down Expand Up @@ -153,7 +153,8 @@ def save(self, fname):
"""
check_fname(fname, 'covariance', ('-cov.fif', '-cov.fif.gz',
'_cov.fif', '_cov.fif.gz'))

# TODO: Add `overwrite` param to method signature
fname = _check_fname(fname=fname, overwrite=True)
fid = start_file(fname)

try:
Expand Down Expand Up @@ -384,6 +385,7 @@ def read_cov(fname, verbose=None):
"""
check_fname(fname, 'covariance', ('-cov.fif', '-cov.fif.gz',
'_cov.fif', '_cov.fif.gz'))
fname = _check_fname(fname=fname, must_exist=True, overwrite='read')
f, tree = fiff_open(fname)[:2]
with f as fid:
return Covariance(**_read_cov(fid, tree, FIFF.FIFFV_MNE_NOISE_COV,
Expand Down
34 changes: 17 additions & 17 deletions mne/epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
_check_combine, ShiftTimeMixin, _build_data_frame,
_check_pandas_index_arguments, _convert_times,
_scale_dataframe_data, _check_time_format, object_size,
_on_missing, _validate_type, _ensure_events)
_on_missing, _validate_type, _ensure_events,
_path_like)
from .utils.docs import fill_doc
from .data.html_templates import epochs_template

Expand Down Expand Up @@ -1738,8 +1739,8 @@ def save(self, fname, split_size='2GB', fmt='single', overwrite=False,
Parameters
----------
fname : str
The name of the file, which should end with -epo.fif or
-epo.fif.gz.
The name of the file, which should end with ``-epo.fif`` or
``-epo.fif.gz``.
split_size : str | int
Large raw files are automatically split into multiple pieces. This
parameter specifies the maximum size of each piece. If the
Expand Down Expand Up @@ -1771,8 +1772,8 @@ def save(self, fname, split_size='2GB', fmt='single', overwrite=False,
check_fname(fname, 'epochs', ('-epo.fif', '-epo.fif.gz',
'_epo.fif', '_epo.fif.gz'))

# check for file existence
_check_fname(fname, overwrite)
# check for file existence and expand `~` if present
fname = _check_fname(fname=fname, overwrite=overwrite)

split_size_bytes = _get_split_size(split_size)

Expand Down Expand Up @@ -3059,14 +3060,11 @@ def read_epochs(fname, proj=True, preload=True, verbose=None):

Parameters
----------
fname : str | file-like
The epochs filename to load. Filename should end with -epo.fif or
-epo.fif.gz. If a file-like object is provided, preloading must be
used.
%(epochs_fname)s
%(proj_epochs)s
preload : bool
If True, read all epochs from disk immediately. If False, epochs will
be read on demand.
If True, read all epochs from disk immediately. If ``False``, epochs
will be read on demand.
%(verbose)s

Returns
Expand Down Expand Up @@ -3100,9 +3098,7 @@ class EpochsFIF(BaseEpochs):

Parameters
----------
fname : str | file-like
The name of the file, which should end with -epo.fif or -epo.fif.gz. If
a file-like object is provided, preloading must be used.
%(epochs_fname)s
%(proj_epochs)s
preload : bool
If True, read all epochs from disk immediately. If False, epochs will
Expand All @@ -3119,9 +3115,13 @@ class EpochsFIF(BaseEpochs):
@verbose
def __init__(self, fname, proj=True, preload=True,
verbose=None): # noqa: D102
if isinstance(fname, str):
check_fname(fname, 'epochs', ('-epo.fif', '-epo.fif.gz',
'_epo.fif', '_epo.fif.gz'))
if _path_like(fname):
check_fname(
fname=fname, filetype='epochs',
endings=('-epo.fif', '-epo.fif.gz', '_epo.fif', '_epo.fif.gz')
)
fname = _check_fname(fname=fname, must_exist=True,
overwrite='read')
elif not preload:
raise ValueError('preload must be used with file-like objects')

Expand Down
9 changes: 7 additions & 2 deletions mne/evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
fill_doc, _check_option, ShiftTimeMixin, _build_data_frame,
_check_pandas_installed, _check_pandas_index_arguments,
_convert_times, _scale_dataframe_data, _check_time_format,
_check_preload)
_check_preload, _check_fname)
from .viz import (plot_evoked, plot_evoked_topomap, plot_evoked_field,
plot_evoked_image, plot_evoked_topo)
from .viz.evoked import plot_evoked_white, plot_evoked_joint
Expand Down Expand Up @@ -127,6 +127,7 @@ def __init__(self, fname, condition=None, proj=True,
verbose=None): # noqa: D102
_validate_type(proj, bool, "'proj'")
# Read the requested data
fname = _check_fname(fname=fname, must_exist=True, overwrite='read')
self.info, self.nave, self._aspect_kind, self.comment, self.times, \
self.data, self.baseline = _read_evoked(fname, condition, kind,
allow_maxshield)
Expand Down Expand Up @@ -278,7 +279,7 @@ def apply_baseline(self, baseline=(None, 0), *, verbose=None):
return self

def save(self, fname):
"""Save dataset to file.
"""Save evoked data to a file.

Parameters
----------
Expand All @@ -295,6 +296,8 @@ def save(self, fname):
Information on baseline correction will be stored with the data,
and will be restored when reading again via `mne.read_evokeds`.
"""
# TODO: Add `overwrite` param to method signature
fname = _check_fname(fname=fname, overwrite=True)
write_evokeds(fname, self)

def __repr__(self): # noqa: D105
Expand Down Expand Up @@ -1398,6 +1401,8 @@ def _write_evokeds(fname, evoked, check=True, *, on_mismatch='raise'):
if check:
check_fname(fname, 'evoked', ('-ave.fif', '-ave.fif.gz',
'_ave.fif', '_ave.fif.gz'))
# TODO: Add `overwrite` param to method signature
fname = _check_fname(fname=fname, overwrite=True)

if not isinstance(evoked, (list, tuple)):
evoked = [evoked]
Expand Down
10 changes: 5 additions & 5 deletions mne/forward/forward.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ def read_forward_solution(fname, include=(), exclude=(), verbose=None):
"""
check_fname(fname, 'forward', ('-fwd.fif', '-fwd.fif.gz',
'_fwd.fif', '_fwd.fif.gz'))

fname = _check_fname(fname=fname, must_exist=True, overwrite='read')
# Open the file, create directory
logger.info('Reading forward solution from %s...' % fname)
f, tree, _ = fiff_open(fname)
Expand Down Expand Up @@ -718,8 +718,8 @@ def write_forward_solution(fname, fwd, overwrite=False, verbose=None):
Parameters
----------
fname : str
File name to save the forward solution to. It should end with -fwd.fif
or -fwd.fif.gz.
File name to save the forward solution to. It should end with
``-fwd.fif`` or ``-fwd.fif.gz``.
fwd : Forward
Forward solution.
%(overwrite)s
Expand Down Expand Up @@ -747,8 +747,8 @@ def write_forward_solution(fname, fwd, overwrite=False, verbose=None):
check_fname(fname, 'forward', ('-fwd.fif', '-fwd.fif.gz',
'_fwd.fif', '_fwd.fif.gz'))

# check for file existence
_check_fname(fname, overwrite)
# check for file existence and expand `~` if present
fname = _check_fname(fname, overwrite)
fid = start_file(fname)
start_block(fid, FIFF.FIFFB_MNE)

Expand Down
12 changes: 8 additions & 4 deletions mne/io/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1416,7 +1416,6 @@ def save(self, fname, picks=None, tmin=0, tmax=None, buffer_size_sec=None,
or all forms of SSS). It is recommended not to concatenate and
then save raw files for this reason.
"""
fname = op.abspath(fname)
endings = ('raw.fif', 'raw_sss.fif', 'raw_tsss.fif',
'_meg.fif', '_eeg.fif', '_ieeg.fif')
endings += tuple([f'{e}.gz' for e in endings])
Expand Down Expand Up @@ -1447,8 +1446,8 @@ def save(self, fname, picks=None, tmin=0, tmax=None, buffer_size_sec=None,
raise ValueError('Complex data must be saved as "single" or '
'"double", not "short"')

# check for file existence
_check_fname(fname, overwrite)
# check for file existence and expand `~` if present
fname = _check_fname(fname=fname, overwrite=overwrite)

if proj:
info = deepcopy(self.info)
Expand Down Expand Up @@ -2148,6 +2147,9 @@ def _write_raw(fname, raw, info, picks, fmt, data_type, reset_range, start,
raise RuntimeError('Cannot write raw file with no data: %s -> %s '
'(max: %s) requested' % (start, stop, n_times_max))

# Expand `~` if present
fname = _check_fname(fname=fname, overwrite=overwrite)

base, ext = op.splitext(fname)
if part_idx > 0:
if split_naming == 'neuromag':
Expand Down Expand Up @@ -2181,7 +2183,9 @@ def _write_raw(fname, raw, info, picks, fmt, data_type, reset_range, start,
raw, info, picks, fid, cals, part_idx, start, stop,
buffer_size, prev_fname, split_size, use_fname,
projector, drop_small_buffer, fmt, fname, reserved_fname,
data_type, reset_range, split_naming, overwrite)
data_type, reset_range, split_naming,
overwrite=True # we've started writing already above
)
if final_fname != use_fname:
assert split_naming == 'bids'
logger.info(f'Renaming BIDS split file {op.basename(final_fname)}')
Expand Down
3 changes: 2 additions & 1 deletion mne/io/eeglab/eeglab.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ def __init__(self, input_fname, events=None, event_id=None, tmin=0,
baseline=None, reject=None, flat=None, reject_tmin=None,
reject_tmax=None, eog=(), verbose=None,
uint16_codec=None): # noqa: D102
input_fname = _check_fname(fname=input_fname, must_exist=True,
overwrite='read')
eeg = _check_load_mat(input_fname, uint16_codec)

if not ((events is None and event_id is None) or
Expand Down Expand Up @@ -507,7 +509,6 @@ def __init__(self, input_fname, events=None, event_id=None, tmin=0,
events = read_events(events)

logger.info('Extracting parameters from %s...' % input_fname)
input_fname = op.abspath(input_fname)
info, eeg_montage, _ = _get_info(eeg, eog=eog)

for key, val in event_id.items():
Expand Down
20 changes: 20 additions & 0 deletions mne/io/fiff/tests/test_raw_fiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# License: BSD-3-Clause

from copy import deepcopy
from pathlib import Path
from functools import partial
from io import BytesIO
import os
Expand Down Expand Up @@ -1786,3 +1787,22 @@ def test_corrupted(tmpdir):
with pytest.warns(RuntimeWarning, match='.*tag directory.*corrupt.*'):
raw_bad = read_raw_fif(bad_fname)
assert_allclose(raw.get_data(), raw_bad.get_data())


@testing.requires_testing_data
def test_expand_user(tmp_path, monkeypatch):
"""Test that we're expanding `~` before reading and writing."""
monkeypatch.setenv('HOME', str(tmp_path))
monkeypatch.setenv('USERPROFILE', str(tmp_path)) # Windows

path_in = Path(fif_fname)
path_out = tmp_path / path_in.name
path_home = Path('~') / path_in.name

shutil.copyfile(
src=path_in,
dst=path_out
)

raw = read_raw_fif(fname=path_home, preload=True)
raw.save(fname=path_home, overwrite=True)
5 changes: 3 additions & 2 deletions mne/io/kit/kit.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from ..pick import pick_types
from ...utils import (verbose, logger, warn, fill_doc, _check_option,
_stamp_to_dt)
_stamp_to_dt, _check_fname)
from ...transforms import apply_trans, als_ras_trans
from ..base import BaseRaw
from ..utils import _mult_cal_one
Expand Down Expand Up @@ -386,8 +386,9 @@ def __init__(self, input_fname, events, event_id=None, tmin=0,
if isinstance(events, str):
events = read_events(events)

input_fname = _check_fname(fname=input_fname, must_exist=True,
overwrite='read')
logger.info('Extracting KIT Parameters from %s...' % input_fname)
input_fname = op.abspath(input_fname)
self.info, kit_info = get_kit_info(
input_fname, allow_unknown_format, standardize_names)
kit_info.update(filename=input_fname)
Expand Down
7 changes: 5 additions & 2 deletions mne/minimum_norm/inverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@
from ..source_estimate import _make_stc, _get_src_type
from ..utils import (check_fname, logger, verbose, warn, _validate_type,
_check_compensation_grade, _check_option,
_check_depth, _check_src_normal)
_check_depth, _check_src_normal, _check_fname)
from ..data.html_templates import inverse_operator_template


INVERSE_METHODS = ('MNE', 'dSPM', 'sLORETA', 'eLORETA')


Expand Down Expand Up @@ -132,7 +133,7 @@ def read_inverse_operator(fname, verbose=None):
"""
check_fname(fname, 'inverse operator', ('-inv.fif', '-inv.fif.gz',
'_inv.fif', '_inv.fif.gz'))

fname = _check_fname(fname=fname, must_exist=True, overwrite='read')
#
# Open the file, create directory
#
Expand Down Expand Up @@ -350,6 +351,8 @@ def write_inverse_operator(fname, inv, verbose=None):
"""
check_fname(fname, 'inverse operator', ('-inv.fif', '-inv.fif.gz',
'_inv.fif', '_inv.fif.gz'))
Comment on lines 352 to 353
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better / simpler just to make check_fname call _check_fname internally and return the str-ified absolute path, then our code ends up more DRY. It seems like every time we check_fname we probably want to call _check_fname anyway...

Or we could go the other way and make _check_fname accept a ext argument, and have it call check_fname internally. I think I'd rather go that way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I was thinking the same, however I don't want to touch this in this PR to avoid unwelcome surprises and to keep the diff smaller.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you open an issue about this? It would make a hopefully straightforward follow-up PR

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue filed under #9819

fname = _check_fname(fname=fname, overwrite=True)

_validate_type(inv, InverseOperator, 'inv')

#
Expand Down
8 changes: 4 additions & 4 deletions mne/report/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from .._freesurfer import _reorient_image, _mri_orientation
from ..utils import (logger, verbose, get_subjects_dir, warn, _ensure_int,
fill_doc, _check_option, _validate_type, _safe_input,
_check_path_like, use_log_level, deprecated)
_path_like, use_log_level, deprecated)
from ..viz import (plot_events, plot_alignment, plot_cov, plot_projs_topomap,
plot_compare_evokeds, set_3d_view, get_3d_backend)
from ..viz.misc import _plot_mri_contours, _get_bem_plotting_surfaces
Expand Down Expand Up @@ -1498,7 +1498,7 @@ def add_figure(self, fig, title, *, caption=None, image_format=None,
figs = (fig,)

for fig in figs:
if _check_path_like(fig):
if _path_like(fig):
raise TypeError(
f'It seems you passed a path to `add_figure`. However, '
f'only Matplotlib figures, Mayavi scenes, and NumPy '
Expand Down Expand Up @@ -1582,9 +1582,9 @@ def add_figs_to_section(self, figs, captions, section='custom',
_check_scale(scale)

if (
_check_path_like(figs) or
_path_like(figs) or
(hasattr(figs, '__iter__') and
any(_check_path_like(f) for f in figs))
any(_path_like(f) for f in figs))
):
raise TypeError(
'It seems you passed a path to `add_figs_to_section`. '
Expand Down
3 changes: 2 additions & 1 deletion mne/report/tests/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,9 @@ def test_add_html():
assert (repr(report))


@testing.requires_testing_data
def test_multiple_figs(tmpdir):
"""Test adding a slider with a series of figures to mne report."""
"""Test adding a slider with a series of figures to a Report."""
tempdir = str(tmpdir)
report = Report(info_fname=raw_fname,
subject='sample', subjects_dir=subjects_dir)
Expand Down
Loading