Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: don't use op.realpath when reading raw #9227

Merged
merged 3 commits into from
Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 1 addition & 1 deletion mne/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ def read_annotations(fname, sfreq='auto', uint16_codec=None):
_validate_type(fname, 'path-like', 'fname')
fname = _check_fname(
fname, overwrite='read', must_exist=True,
allow_dir=str(fname).endswith('.ds'), # allow_dir for CTF
need_dir=str(fname).endswith('.ds'), # for CTF
name='fname')
name = op.basename(fname)
if name.endswith(('fif', 'fif.gz')):
Expand Down
3 changes: 2 additions & 1 deletion mne/io/artemis123/artemis123.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import calendar

from .utils import _load_mne_locs, _read_pos
from ...utils import logger, warn, verbose
from ...utils import logger, warn, verbose, _check_fname
from ..utils import _read_segments_file
from ..base import BaseRaw
from ..meas_info import _empty_info
Expand Down Expand Up @@ -308,6 +308,7 @@ def __init__(self, input_fname, preload=False, verbose=None,
from scipy.spatial.distance import cdist
from ...chpi import (compute_chpi_amplitudes, compute_chpi_locs,
_fit_coil_order_dev_head_trans)
input_fname = _check_fname(input_fname, 'read', True, 'input_fname')
fname, ext = op.splitext(input_fname)
if ext == '.txt':
input_fname = fname + '.bin'
Expand Down
2 changes: 1 addition & 1 deletion mne/io/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1394,7 +1394,7 @@ 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.realpath(fname)
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
3 changes: 2 additions & 1 deletion mne/io/boxy/boxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from ..base import BaseRaw
from ..meas_info import create_info
from ..utils import _mult_cal_one
from ...utils import logger, verbose, fill_doc
from ...utils import logger, verbose, fill_doc, _check_fname
from ...annotations import Annotations


Expand Down Expand Up @@ -65,6 +65,7 @@ def __init__(self, fname, preload=False, verbose=None):
raw_extras = dict()
raw_extras['offsets'] = list() # keep track of our offsets
sfreq = None
fname = _check_fname(fname, 'read', True, 'fname')
with open(fname, 'r') as fid:
line_num = 0
i_line = fid.readline()
Expand Down
9 changes: 3 additions & 6 deletions mne/io/ctf/ctf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
# License: BSD (3-clause)

import os
import os.path as op

import numpy as np

from .._digitization import _format_dig_points
from ...utils import (verbose, logger, _clean_names, fill_doc, _check_option,
_validate_type)
_check_fname)

from ..base import BaseRaw
from ..utils import _mult_cal_one, _blk_read_lims
Expand Down Expand Up @@ -91,13 +90,11 @@ class RawCTF(BaseRaw):
def __init__(self, directory, system_clock='truncate', preload=False,
verbose=None, clean_names=False): # noqa: D102
# adapted from mne_ctf2fiff.c
_validate_type(directory, 'path-like', 'directory')
directory = str(directory)
directory = _check_fname(directory, 'read', True, 'directory',
need_dir=True)
if not directory.endswith('.ds'):
raise TypeError('directory must be a directory ending with ".ds", '
f'got {directory}')
if not op.isdir(directory):
raise ValueError('directory does not exist: "%s"' % directory)
_check_option('system_clock', system_clock, ['ignore', 'truncate'])
logger.info('ds directory : %s' % directory)
res4 = _read_res4(directory) # Read the magical res4 file
Expand Down
10 changes: 6 additions & 4 deletions mne/io/ctf/tests/test_ctf.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,14 @@ def test_read_ctf(tmpdir):
assert_allclose(raw.annotations.onset, [2.15])
assert_allclose(raw.annotations.duration, [0.0225])

pytest.raises(TypeError, read_raw_ctf, 1)
pytest.raises(ValueError, read_raw_ctf, ctf_fname_continuous + 'foo.ds')
with pytest.raises(TypeError, match='path-like'):
read_raw_ctf(1)
with pytest.raises(FileNotFoundError, match='does not exist'):
read_raw_ctf(ctf_fname_continuous + 'foo.ds')
# test ignoring of system clock
read_raw_ctf(op.join(ctf_dir, ctf_fname_continuous), 'ignore')
pytest.raises(ValueError, read_raw_ctf,
op.join(ctf_dir, ctf_fname_continuous), 'foo')
with pytest.raises(ValueError, match='system_clock'):
read_raw_ctf(op.join(ctf_dir, ctf_fname_continuous), 'foo')


@testing.requires_testing_data
Expand Down
2 changes: 1 addition & 1 deletion mne/io/curry/curry.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def _get_curry_file_structure(fname, required=()):
"""Store paths to a dict and check for required files."""
_msg = "The following required files cannot be found: {0}.\nPlease make " \
"sure all required files are located in the same directory as {1}."
_check_fname(fname, overwrite='read', must_exist=True)
fname = _check_fname(fname, 'read', True, 'fname')

# we don't use os.path.splitext to also handle extensions like .cdt.dpa
fname_base, ext = fname.split(".", maxsplit=1)
Expand Down
9 changes: 5 additions & 4 deletions mne/io/eeglab/eeglab.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ..constants import FIFF
from ..meas_info import create_info
from ..base import BaseRaw
from ...utils import logger, verbose, warn, fill_doc, Bunch
from ...utils import logger, verbose, warn, fill_doc, Bunch, _check_fname
from ...channels import make_dig_montage
from ...epochs import BaseEpochs
from ...event import read_events
Expand All @@ -22,7 +22,7 @@
CAL = 1e-6


def _check_fname(fname, dataname):
def _check_eeglab_fname(fname, dataname):
"""Check whether the filename is valid.

Check if the file extension is ``.fdt`` (older ``.dat`` being invalid) or
Expand Down Expand Up @@ -314,6 +314,7 @@ class RawEEGLAB(BaseRaw):
@verbose
def __init__(self, input_fname, eog=(),
preload=False, uint16_codec=None, verbose=None): # noqa: D102
input_fname = _check_fname(input_fname, 'read', True, 'input_fname')
eeg = _check_load_mat(input_fname, uint16_codec)
if eeg.trials != 1:
raise TypeError('The number of trials is %d. It must be 1 for raw'
Expand All @@ -325,7 +326,7 @@ def __init__(self, input_fname, eog=(),

# read the data
if isinstance(eeg.data, str):
data_fname = _check_fname(input_fname, eeg.data)
data_fname = _check_eeglab_fname(input_fname, eeg.data)
logger.info('Reading %s' % data_fname)

super(RawEEGLAB, self).__init__(
Expand Down Expand Up @@ -510,7 +511,7 @@ def __init__(self, input_fname, events=None, event_id=None, tmin=0,
'(event id %i)' % (key, val))

if isinstance(eeg.data, str):
data_fname = _check_fname(input_fname, eeg.data)
data_fname = _check_eeglab_fname(input_fname, eeg.data)
with open(data_fname, 'rb') as data_fid:
data = np.fromfile(data_fid, dtype=np.float32)
data = data.reshape((eeg.nbchan, eeg.pnts, eeg.trials),
Expand Down
4 changes: 2 additions & 2 deletions mne/io/egi/egi.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from ..utils import _read_segments_file, _create_chs
from ..meas_info import _empty_info
from ..constants import FIFF
from ...utils import verbose, logger, warn, _validate_type
from ...utils import verbose, logger, warn, _validate_type, _check_fname


def _read_header(fid):
Expand Down Expand Up @@ -152,7 +152,6 @@ def read_raw_egi(input_fname, eog=None, misc=None,
"""
_validate_type(input_fname, 'path-like', 'input_fname')
input_fname = str(input_fname)

if input_fname.endswith('.mff'):
return _read_raw_egi_mff(input_fname, eog, misc, include,
exclude, preload, channel_naming, verbose)
Expand All @@ -167,6 +166,7 @@ class RawEGI(BaseRaw):
def __init__(self, input_fname, eog=None, misc=None,
include=None, exclude=None, preload=False,
channel_naming='E%d', verbose=None): # noqa: D102
input_fname = _check_fname(input_fname, 'read', True, 'input_fname')
if eog is None:
eog = []
if misc is None:
Expand Down
4 changes: 3 additions & 1 deletion mne/io/egi/egimff.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from ..proj import setup_proj
from ..utils import _create_chs, _mult_cal_one
from ...annotations import Annotations
from ...utils import verbose, logger, warn, _check_option
from ...utils import verbose, logger, warn, _check_option, _check_fname
from ...evoked import EvokedArray


Expand Down Expand Up @@ -398,6 +398,8 @@ def __init__(self, input_fname, eog=None, misc=None,
include=None, exclude=None, preload=False,
channel_naming='E%d', verbose=None):
"""Init the RawMff class."""
input_fname = _check_fname(input_fname, 'read', True, 'input_fname',
need_dir=True)
logger.info('Reading EGI MFF Header from %s...' % input_fname)
egi_info = _read_header(input_fname)
if eog is None:
Expand Down
3 changes: 2 additions & 1 deletion mne/io/eximia/eximia.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from ..base import BaseRaw
from ..utils import _read_segments_file, _file_size
from ..meas_info import create_info
from ...utils import logger, verbose, warn, fill_doc
from ...utils import logger, verbose, warn, fill_doc, _check_fname


@fill_doc
Expand Down Expand Up @@ -52,6 +52,7 @@ class RawEximia(BaseRaw):

@verbose
def __init__(self, fname, preload=False, verbose=None):
fname = _check_fname(fname, 'read', True, 'fname')
data_name = op.basename(fname)
logger.info('Loading %s' % data_name)
# Create vhdr and vmrk files so that we can use mne_brain_vision2fiff
Expand Down
14 changes: 7 additions & 7 deletions mne/io/fiff/raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from ...event import AcqParserFIF
from ...utils import (check_fname, logger, verbose, warn, fill_doc, _file_like,
_on_missing)
_on_missing, _check_fname)


@fill_doc
Expand Down Expand Up @@ -75,13 +75,13 @@ class Raw(BaseRaw):
def __init__(self, fname, allow_maxshield=False, preload=False,
on_split_missing='raise', verbose=None): # noqa: D102
raws = []
do_check_fname = not _file_like(fname)
do_check_ext = not _file_like(fname)
next_fname = fname
while next_fname is not None:
raw, next_fname, buffer_size_sec = \
self._read_raw_file(next_fname, allow_maxshield,
preload, do_check_fname)
do_check_fname = False
preload, do_check_ext)
do_check_ext = False
raws.append(raw)
if next_fname is not None:
if not op.exists(next_fname):
Expand Down Expand Up @@ -132,19 +132,19 @@ def __init__(self, fname, allow_maxshield=False, preload=False,

@verbose
def _read_raw_file(self, fname, allow_maxshield, preload,
do_check_fname=True, verbose=None):
do_check_ext=True, verbose=None):
"""Read in header information from a raw file."""
logger.info('Opening raw data file %s...' % fname)

# Read in the whole file if preload is on and .fif.gz (saves time)
if not _file_like(fname):
if do_check_fname:
if do_check_ext:
endings = ('raw.fif', 'raw_sss.fif', 'raw_tsss.fif',
'_meg.fif', '_eeg.fif', '_ieeg.fif')
endings += tuple([f'{e}.gz' for e in endings])
check_fname(fname, 'raw', endings)
# filename
fname = op.realpath(fname)
larsoner marked this conversation as resolved.
Show resolved Hide resolved
fname = _check_fname(fname, 'read', True, 'fname')
ext = os.path.splitext(fname)[1].lower()
whole_file = preload if '.gz' in ext else False
del ext
Expand Down
35 changes: 29 additions & 6 deletions mne/io/fiff/tests/test_raw_fiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import os.path as op
import pathlib
import pickle
import shutil
import sys

import numpy as np
Expand All @@ -28,7 +29,7 @@
compute_proj_raw, pick_types, pick_channels, create_info,
pick_info)
from mne.utils import (requires_pandas, assert_object_equal, _dt_to_stamp,
requires_mne, run_subprocess, run_tests_if_main,
requires_mne, run_subprocess,
assert_and_remove_boundary_annot)
from mne.annotations import Annotations

Expand Down Expand Up @@ -1361,12 +1362,16 @@ def test_add_channels():
@testing.requires_testing_data
def test_save(tmpdir):
"""Test saving raw."""
raw = read_raw_fif(fif_fname, preload=False)
temp_fname = tmpdir.join('test_raw.fif')
shutil.copyfile(fif_fname, temp_fname)
raw = read_raw_fif(temp_fname, preload=False)
# can't write over file being read
pytest.raises(ValueError, raw.save, fif_fname)
raw = read_raw_fif(fif_fname, preload=True)
with pytest.raises(ValueError, match='to the same file'):
raw.save(temp_fname)
raw.load_data()
# can't overwrite file without overwrite=True
pytest.raises(IOError, raw.save, fif_fname)
with pytest.raises(IOError, match='file exists'):
raw.save(fif_fname)

# test abspath support and annotations
orig_time = _dt_to_stamp(raw.info['meas_date'])[0] + raw._first_time
Expand Down Expand Up @@ -1720,4 +1725,22 @@ def test_bad_acq(fname):
assert tag == ent


run_tests_if_main()
@pytest.mark.skipif(sys.platform not in ('darwin', 'linux'),
reason='Needs proper symlinking')
def test_split_symlink(tmpdir):
"""Test split files with symlinks."""
# regression test for gh-9221
first = str(tmpdir.mkdir('first').join('test_raw.fif'))
raw = read_raw_fif(fif_fname).pick('meg').load_data()
raw.save(first, buffer_size_sec=1, split_size='10MB', verbose=True)
second = first[:-4] + '-1.fif'
assert op.isfile(second)
assert not op.isfile(first[:-4] + '-2.fif')
new_first = tmpdir.mkdir('a').join('test_raw.fif')
new_second = tmpdir.mkdir('b').join('test_raw-1.fif')
shutil.move(first, new_first)
shutil.move(second, new_second)
os.symlink(new_first, first)
os.symlink(new_second, second)
raw_new = read_raw_fif(first)
assert_allclose(raw_new.get_data(), raw.get_data())
3 changes: 2 additions & 1 deletion mne/io/nedf/nedf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from ..base import BaseRaw
from ..meas_info import create_info
from ..utils import _mult_cal_one
from ...utils import warn, verbose
from ...utils import warn, verbose, _check_fname


def _getsubnodetext(node, name):
Expand Down Expand Up @@ -131,6 +131,7 @@ class RawNedf(BaseRaw):
"""Raw object from NeuroElectrics nedf file."""

def __init__(self, filename, preload=False, verbose=None):
filename = _check_fname(filename, 'read', True, 'filename')
with open(filename, mode='rb') as fid:
header = fid.read(_HDRLEN)
header, dt, dt_last, n_samp, n_full = _parse_nedf_header(header)
Expand Down
3 changes: 2 additions & 1 deletion mne/io/nihon/nihon.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import numpy as np

from ...utils import fill_doc, logger, verbose, warn
from ...utils import fill_doc, logger, verbose, warn, _check_fname
from ..base import BaseRaw
from ..meas_info import create_info
from ...annotations import Annotations
Expand Down Expand Up @@ -308,6 +308,7 @@ class RawNihon(BaseRaw):

@verbose
def __init__(self, fname, preload=False, verbose=None):
fname = _check_fname(fname, 'read', True, 'fname')
fname = _ensure_path(fname)
data_name = fname.name
logger.info('Loading %s' % data_name)
Expand Down
9 changes: 5 additions & 4 deletions mne/io/nirx/nirx.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from ..meas_info import create_info, _format_dig_points
from ...annotations import Annotations
from ...transforms import apply_trans, _get_trans
from ...utils import logger, verbose, fill_doc, warn
from ...utils import (logger, verbose, fill_doc, warn, _check_fname,
_validate_type)


@fill_doc
Expand Down Expand Up @@ -69,12 +70,12 @@ def __init__(self, fname, preload=False, verbose=None):
from ...externals.pymatreader import read_mat
from ...coreg import get_mni_fiducials # avoid circular import prob
logger.info('Loading %s' % fname)

_validate_type(fname, 'path-like', 'fname')
fname = str(fname)
if fname.endswith('.hdr'):
fname = op.dirname(op.abspath(fname))

if not op.isdir(fname):
raise FileNotFoundError('The path you specified does not exist.')
fname = _check_fname(fname, 'read', True, 'fname', need_dir=True)

# Check if required files exist and store names for later use
files = dict()
Expand Down
2 changes: 1 addition & 1 deletion mne/io/nirx/tests/test_nirx.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_nirx_hdr_load():
@requires_testing_data
def test_nirx_missing_warn():
"""Test reading NIRX files when missing data."""
with pytest.raises(FileNotFoundError, match='The path you'):
with pytest.raises(FileNotFoundError, match='does not exist'):
read_raw_nirx(fname_nirx_15_2_short + "1", preload=True)


Expand Down
Loading