Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions doc/changes/dev/14075.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix :class:`mne.io.RawArray` constructor metadata retaining stale array data after operations replace the active data, by :newcontrib:`Floze`.
23 changes: 23 additions & 0 deletions mne/io/array/_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,29 @@ class RawArray(BaseRaw):
* AU: misc
"""

_extra_attributes = ("_init_kwargs_without_data",)

@property
def _init_kwargs(self):
"""Return constructor arguments without retaining stale data."""
try:
kwargs = self._init_kwargs_without_data
except AttributeError:
# Support instances serialized before ``data`` was stored dynamically.
kwargs = self.__dict__.pop("_init_kwargs")
self._init_kwargs = kwargs
kwargs = self._init_kwargs_without_data
if kwargs is None:
return None
return dict(data=self._data, **kwargs)

@_init_kwargs.setter
def _init_kwargs(self, kwargs):
if kwargs is not None:
kwargs = kwargs.copy()
kwargs.pop("data", None)
self._init_kwargs_without_data = kwargs

@verbose
def __init__(self, data, info, first_samp=0, copy="auto", verbose=None):
_validate_type(info, "info", "info")
Expand Down
34 changes: 34 additions & 0 deletions mne/io/array/tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.

import gc
import pickle
import weakref
from pathlib import Path

import matplotlib.pyplot as plt
Expand Down Expand Up @@ -74,6 +77,37 @@ def test_array_copy():
RawArray(data.astype(np.float32), info, copy=None)


def test_array_init_kwargs():
"""Test that constructor arguments do not retain stale array data."""
data = np.zeros((1, 1000))
data_ref = weakref.ref(data)
raw = RawArray(data, create_info(1, 1000.0, "eeg"))

init_kwargs = raw._init_kwargs
assert isinstance(init_kwargs, dict)
assert tuple(init_kwargs) == ("data", "info", "first_samp", "copy", "verbose")
assert init_kwargs["data"] is raw._data
del init_kwargs, data

raw.resample(500)
gc.collect()
assert data_ref() is None
assert raw._init_kwargs["data"] is raw._data

raw_copy = raw.copy()
assert raw_copy._init_kwargs["data"] is raw_copy._data
assert raw_copy._data is not raw._data

raw_reconstructed = RawArray(**raw._init_kwargs)
assert_allclose(raw_reconstructed.get_data(), raw.get_data())
assert repr(raw_reconstructed) == repr(raw)

raw_unpickled = pickle.loads(pickle.dumps(raw))
assert raw_unpickled._init_kwargs["data"] is raw_unpickled._data
assert_allclose(raw_unpickled.get_data(), raw.get_data())
assert repr(raw_unpickled) == repr(raw)


@pytest.mark.slowtest
def test_array_raw():
"""Test creating raw from array."""
Expand Down