mef3io 1.5.0¶
Released 2026-10-20.
Highlights¶
- Modified copies:
mef3io.transform_session(#73). Writes a copy of a session with only the changes you ask for: chosen channels, new passwords (or none), a new session name, edited subject details, recording location and descriptions. Every time and every sample is copied as stored, and the copy is verified against its source before it gets its final name. It runs on the same copy engine asanonymize_session, with the samecheck/verifypair and refusal codes. From a terminal:python -m mef3io transform. Python and C++, not MATLAB. Guide: Modified copies. It is also the way to re-key a session without de-identifying it (for example one written by mef3io 1.2.0 or earlier with a non-ASCII password). - Cut:
time_range=(start_us, end_us)intransform_sessionandanonymize_session(#103). Keeps only the samples in that window. Blocks inside it are copied byte for byte; the two edge blocks are decoded, trimmed and re-encoded (exact=True, the default) or kept whole (exact=False). Segments are renumbered and their declarations describe what they hold. A cut combines with every other option of the copy. From a terminal:--start,--end,--whole-blocks. - Merge:
mef3io.merge_sessions(#103). Writes one session from two or more: parts of one recording, recordings that take turns, or different amplifiers. Overlapping recorded data, different sampling rates of one channel, different recording-time offsets and different subject identities are refused before anything is written (mef3io.merge.checklists them all), and the result is verified against every source. From a terminal:python -m mef3io merge DST SRC.... Python and C++. Guide: Merging recordings. - Renaming sessions properly (#72). A session's name is stored in four places (the folder, the session-level annotation files, the header of every file, and descriptions that repeat it); renaming only the folder makes meflib and pymef silently miss the session's annotations.
mef3io.rename_session(path, new_name)changes all four as one journaled operation, stampedrenamein the files' provenance, withRenameReport.undo()to reverse it. Times, samples and indexes are not touched.mef3io.name_drift(path)reports whether the four agree;mef3io.fix_name_drift(path)repairs a folder renamed by another tool.mef3io.rename_archive(tar, new_name)renames a.mefd.tarwithout ever replacing a file.mef3io.time_range(path)gives a session's first and last sample time from the first and last segment's metadata only, for ordering many sessions.- An interrupted rename leaves an operation marker in the session.
mef3io.operation_markerreads it and the nextrename_sessionrolls the rename back;mef3io.clear_operation_markerremoves a stale one. Guide: Ordering and naming many sessions. - Anonymizing text is more reliable
(#114,
#115). With annotation text
kept, the removal of known names now matches the way the leftover check
looks (accents, glued names), so mef3io no longer refuses its own copy
after writing it. Text that could still not be cleaned is refused up front
as
text-unremovable, andcheckreports it.mef3io.anonymize.redact_textreturns exactly the text a copy would keep, for applications that show it before copying. - Test coverage is published with each release (#127): measured only when a version is released, with badges in the README.
Fixes¶
- A read starting exactly half a sample off the sampling grid no longer returns a spurious NaN (#129). At rates where half a sample is a whole number of microseconds (500, 250, 1000, 200 Hz...), a window whose start fell exactly between two samples (at 500 Hz, any millisecond time ending in an odd millisecond) came back with one NaN at the first block boundary. The samples before that NaN also sat one place early. Analyses that treat NaN as a gap split there. The first sample of such a window is now the one at or after its start, and every later block follows it without a hole. Windows that start on the grid, or anywhere else off it, read exactly as before. All 1.x versions were affected.
Upgrading¶
- Nothing in existing code breaks. The Python minimum (3.10) is unchanged, existing sessions need no conversion, and every earlier call works as before.
- Reads that start exactly half a sample off the grid change. If you read with such a start time, the first sample is now the one at or after the start (it used to be one place earlier, and a NaN could appear at the first block boundary). Any other start gives the same result as before.
- A session with an operation marker is protected. If a rename was
interrupted, the session holds a
.mef3io-operationfile. Writers,repair_session,recover_session(apply=True),archive_sessionand copies refuse it (copies with the refusal codesource-mid-operation), and aReaderwarns withSessionOperationWarning(aMefWarning; the data is still readable). Runrename_session(orfix_name_drift) on it again, with the samebackup_dir, to roll the rename back. Sessions that never had an interrupted rename are unaffected. - A
Writerkept open across a rename of its folder now refuses to write rather than recreate the session at the old path. Rename between recordings. - Provenance: the operation codes
rename(7) andtransform(8) are now in use (they were reserved), so files may carry them (format reference). - New refusal codes for copies:
text-unremovable,time-range-invalid,time-range-empty,source-mid-operation, and for merges themerge-*codes. The anonymizer's existing codes (such asrename-name-mismatch) now apply to modified copies and merges too. Match on the code, not the message (mef3io.anonymize.REFUSAL_CODES,mef3io.transform.REFUSAL_CODES,mef3io.merge.REFUSAL_CODES). - C++:
AnonymizeOptionsandTransformOptionsshare the baseCopyOptions;AnonymizeRefusalandAnonymizeErrorremain as aliases, so existing code compiles unchanged. - Transform, cut, merge and renaming are in Python and C++ only, not MATLAB.