Skip to content
Open
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/14248.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix bug where :func:`mne.io.anonymize_info` could set an incorrect date in ``info["proc_history"]``, causing an ``OverflowError`` when writing FIF files anonymized with a large ``daysback``, by `Eric Larson`_.
22 changes: 17 additions & 5 deletions mne/_fiff/meas_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -2964,8 +2964,14 @@ def _check_dates(info, prepend_error=""):
It's needed because of the limited integer precision
of the fix standard.
"""
for key in ("file_id", "meas_id"):
value = info.get(key)
to_check = [(f"[{key!r}]", info.get(key)) for key in ("file_id", "meas_id")]
for pi, record in enumerate(info.get("proc_history") or []):
prefix = f"['proc_history'][{pi}]"
to_check.append((f"{prefix}['block_id']", record.get("block_id")))
date = record.get("date")
if date is not None:
to_check.append((f"{prefix}['date']", dict(zip(("secs", "usecs"), date))))
for name, value in to_check:
if value is not None:
assert "msecs" not in value
for key_2 in ("secs", "usecs"):
Expand All @@ -2974,7 +2980,7 @@ def _check_dates(info, prepend_error=""):
or value[key_2] > np.iinfo(">i4").max
):
raise RuntimeError(
f"{prepend_error}info[{key}][{key_2}] must be between "
f"{prepend_error}info{name}[{key_2!r}] must be between "
f'"{np.iinfo(">i4").min!r}" and "{np.iinfo(">i4").max!r}", got '
f'"{value[key_2]!r}"'
)
Expand Down Expand Up @@ -3874,10 +3880,16 @@ def anonymize_info(info, daysback=None, keep_his=False, verbose=None):
record["date"] = DATE_NONE
else:
this_t0 = (record["block_id"]["secs"], record["block_id"]["usecs"])
this_t1 = _add_timedelta_to_stamp(this_t0, -delta_t)
if this_t0 == DATE_NONE:
this_t1 = DATE_NONE
else:
this_t1 = _add_timedelta_to_stamp(this_t0, -delta_t)
record["block_id"]["secs"] = this_t1[0]
record["block_id"]["usecs"] = this_t1[1]
record["date"] = _add_timedelta_to_stamp(record["date"], -delta_t)
this_date = tuple(record["date"])
if this_date != DATE_NONE:
this_date = _add_timedelta_to_stamp(this_date, -delta_t)
record["date"] = this_date

hi = info.get("helium_info")
if hi is not None:
Expand Down
16 changes: 16 additions & 0 deletions mne/_fiff/tests/test_meas_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -1090,10 +1090,26 @@ def test_anonymize_with_io(tmp_path, daysback):
"""Test that IO does not break anonymization and all fields."""
raw = read_raw_fif(raw_fname).crop(0, 1)
_complete_info(raw.info)
# maxwell_filter writes records whose stamps are the DATE_NONE placeholder;
# shifting those overflows the int32 stamp for large daysback (gh-14236)
raw.info["proc_history"].insert(
0,
dict(
block_id=_generate_meas_id(),
experimenter="m",
max_info=dict(
max_st=dict(), sss_ctc=dict(), sss_cal=dict(), sss_info=dict(in_order=8)
),
date=DATE_NONE,
),
)
temp_path = tmp_path / "tmp_raw.fif"
raw.save(temp_path)
raw2 = read_raw_fif(temp_path).load_data()
raw2.anonymize(daysback=daysback)
record = raw2.info["proc_history"][0]
assert (record["block_id"]["secs"], record["block_id"]["usecs"]) == DATE_NONE
assert record["date"] == DATE_NONE
raw2.save(temp_path, overwrite=True)
raw3 = read_raw_fif(temp_path)
d = object_diff(raw2.info, raw3.info)
Expand Down
Loading