From dac4f16903171c5b8a03b1a35b9c99e7bb753e25 Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne <73893616+mscheltienne@users.noreply.github.com> Date: Thu, 28 Oct 2021 21:16:58 +0200 Subject: [PATCH] Use locking/unlocking for Info.update() (#9914) * Add update method using __setitem__. * FIX: MNE functions using info.update(). * FIX: Tests using info.update() [circle full] * FIX: Consider argument other as iterable of key/value pairs instead of Mapping. [circle full] * FIX: failing tutorial. [circle full] * Dont run all [skip azp] [skip actions] * FIX: tutorial. [skip azp] [skip actions] Co-authored-by: Eric Larson --- mne/beamformer/tests/test_dics.py | 3 ++- mne/beamformer/tests/test_lcmv.py | 3 ++- mne/io/fieldtrip/utils.py | 4 ++-- mne/io/hitachi/hitachi.py | 2 +- mne/io/meas_info.py | 10 ++++++++++ mne/io/nirx/nirx.py | 2 +- mne/io/reference.py | 3 ++- mne/simulation/raw.py | 6 ++++-- mne/tests/test_cov.py | 3 ++- tutorials/simulation/80_dics.py | 3 +-- 10 files changed, 27 insertions(+), 12 deletions(-) diff --git a/mne/beamformer/tests/test_dics.py b/mne/beamformer/tests/test_dics.py index f4949341e1c..5cf8f4653b3 100644 --- a/mne/beamformer/tests/test_dics.py +++ b/mne/beamformer/tests/test_dics.py @@ -74,7 +74,8 @@ def _simulate_data(fwd, idx): # Somewhere on the frontal lobe by default # Create an info object that holds information about the sensors info = mne.create_info(fwd['info']['ch_names'], sfreq, ch_types='grad') - info.update(fwd['info']) # Merge in sensor position information + with info._unlock(): + info.update(fwd['info']) # Merge in sensor position information # heavily decimate sensors to make it much faster info = mne.pick_info(info, np.arange(info['nchan'])[::5]) fwd = mne.pick_channels_forward(fwd, info['ch_names']) diff --git a/mne/beamformer/tests/test_lcmv.py b/mne/beamformer/tests/test_lcmv.py index 6a0093867d0..db4989a8fc8 100644 --- a/mne/beamformer/tests/test_lcmv.py +++ b/mne/beamformer/tests/test_lcmv.py @@ -125,7 +125,8 @@ def test_lcmv_vector(): # For speed and for rank-deficiency calculation simplicity, # just use grads info = mne.pick_info(info, mne.pick_types(info, meg='grad', exclude=())) - info.update(bads=[], projs=[]) + with info._unlock(): + info.update(bads=[], projs=[]) forward = mne.read_forward_solution(fname_fwd) forward = mne.pick_channels_forward(forward, info['ch_names']) diff --git a/mne/io/fieldtrip/utils.py b/mne/io/fieldtrip/utils.py index a8aac2b3da8..2f190cea70d 100644 --- a/mne/io/fieldtrip/utils.py +++ b/mne/io/fieldtrip/utils.py @@ -76,8 +76,8 @@ def _create_info(ft_struct, raw_info): else: info = create_info(ch_names, sfreq) chs, dig = _create_info_chs_dig(ft_struct) - info.update(chs=chs, dig=dig) - info._update_redundant() + with info._unlock(update_redundant=True): + info.update(chs=chs, dig=dig) return info diff --git a/mne/io/hitachi/hitachi.py b/mne/io/hitachi/hitachi.py index 6d1877dfc83..7dabb6a556f 100644 --- a/mne/io/hitachi/hitachi.py +++ b/mne/io/hitachi/hitachi.py @@ -228,8 +228,8 @@ def __init__(self, fname, preload=False, *, verbose=None): # Create mne structure info = create_info(ch_names, sfreq, ch_types=ch_types) - info.update(info_extra) with info._unlock(): + info.update(info_extra) info['meas_date'] = meas_date for li, loc in enumerate(locs): info['chs'][li]['loc'][:] = loc diff --git a/mne/io/meas_info.py b/mne/io/meas_info.py index 29ceee192e0..cff925f17e5 100644 --- a/mne/io/meas_info.py +++ b/mne/io/meas_info.py @@ -7,6 +7,7 @@ # License: BSD-3-Clause from collections import Counter, OrderedDict +from collections.abc import Mapping import contextlib from copy import deepcopy import datetime @@ -726,6 +727,15 @@ def __setitem__(self, key, val): DeprecationWarning) super().__setitem__(key, val) + def update(self, other=None, **kwargs): + """Update method using __setitem__().""" + iterable = other.items() if isinstance(other, Mapping) else other + if other is not None: + for key, val in iterable: + self[key] = val + for key, val in kwargs.items(): + self[key] = val + @contextlib.contextmanager def _unlock(self, *, update_redundant=False, check_after=False): """Context manager unlocking access to attributes.""" diff --git a/mne/io/nirx/nirx.py b/mne/io/nirx/nirx.py index fc697977172..c7c6bcc164e 100644 --- a/mne/io/nirx/nirx.py +++ b/mne/io/nirx/nirx.py @@ -333,8 +333,8 @@ def __init__(self, fname, saturated, preload=False, verbose=None): info = create_info(chnames, samplingrate, ch_types='fnirs_cw_amplitude') - info.update(subject_info=subject_info, dig=dig) with info._unlock(): + info.update(subject_info=subject_info, dig=dig) info['meas_date'] = meas_date # Store channel, source, and detector locations diff --git a/mne/io/reference.py b/mne/io/reference.py index 67433035091..e975b86fe86 100644 --- a/mne/io/reference.py +++ b/mne/io/reference.py @@ -528,7 +528,8 @@ def set_bipolar_reference(inst, anode, cathode, ch_name=None, ch_info=None, # Set other info-keys from original instance. pick_info = {k: v for k, v in inst.info.items() if k not in ['chs', 'ch_names', 'bads', 'nchan', 'sfreq']} - ref_info.update(pick_info) + with ref_info._unlock(): + ref_info.update(pick_info) # Rereferencing of data. ref_data = multiplier @ inst._data diff --git a/mne/simulation/raw.py b/mne/simulation/raw.py index 3e1c7a2514a..1a49d037838 100644 --- a/mne/simulation/raw.py +++ b/mne/simulation/raw.py @@ -573,7 +573,8 @@ def add_chpi(raw, head_pos=None, interp='cos2', n_jobs=1, verbose=None): sinusoids = 70e-9 * np.sin(2 * np.pi * hpi_freqs[:, np.newaxis] * (np.arange(len(times)) / info['sfreq'])) info = pick_info(info, meg_picks) - info.update(projs=[], bads=[]) # Ensure no 'projs' or 'bads' + with info._unlock(): + info.update(projs=[], bads=[]) # Ensure no 'projs' or 'bads' megcoils, _, _, _ = _prep_meg_channels(info, ignore_ref=False) used = np.zeros(len(raw.times), bool) dev_head_ts.append(dev_head_ts[-1]) # ZOH after time ends @@ -689,7 +690,8 @@ def _iter_forward_solutions(info, trans, src, bem, dev_head_ts, mindist, """Calculate a forward solution for a subject.""" logger.info('Setting up forward solutions') info = pick_info(info, picks) - info.update(projs=[], bads=[]) # Ensure no 'projs' or 'bads' + with info._unlock(): + info.update(projs=[], bads=[]) # Ensure no 'projs' or 'bads' mri_head_t, trans = _get_trans(trans) megcoils, meg_info, compcoils, megnames, eegels, eegnames, rr, info, \ update_kwargs, bem = _prepare_for_forward( diff --git a/mne/tests/test_cov.py b/mne/tests/test_cov.py index 078beea70d4..43bafbb2a63 100644 --- a/mne/tests/test_cov.py +++ b/mne/tests/test_cov.py @@ -721,7 +721,8 @@ def test_low_rank_cov(raw_epochs_events): # test that rank=306 is same as rank='full' epochs_meg = epochs.copy().pick_types(meg=True) assert len(epochs_meg.ch_names) == 306 - epochs_meg.info.update(bads=[], projs=[]) + with epochs_meg.info._unlock(): + epochs_meg.info.update(bads=[], projs=[]) cov_full = compute_covariance(epochs_meg, method='oas', rank='full', verbose='error') assert _cov_rank(cov_full, epochs_meg.info) == 306 diff --git a/tutorials/simulation/80_dics.py b/tutorials/simulation/80_dics.py index ab92dadacc3..799bbea4e57 100644 --- a/tutorials/simulation/80_dics.py +++ b/tutorials/simulation/80_dics.py @@ -155,8 +155,7 @@ def coh_signal_gen(): # Read the info from the sample dataset. This defines the location of the # sensors and such. -info = mne.io.read_info(raw_fname) -info.update(sfreq=sfreq, bads=[]) +info = mne.io.read_raw(raw_fname).crop(0, 1).resample(50).info # Only use gradiometers picks = mne.pick_types(info, meg='grad', stim=True, exclude=())