Skip to content

DOC: document onset, duration, description, and ch_names attributes of mne.Annotations#13680

Open
Famous077 wants to merge 32 commits intomne-tools:mainfrom
Famous077:doc/annotations-onset-duration-description
Open

DOC: document onset, duration, description, and ch_names attributes of mne.Annotations#13680
Famous077 wants to merge 32 commits intomne-tools:mainfrom
Famous077:doc/annotations-onset-duration-description

Conversation

@Famous077
Copy link
Copy Markdown
Contributor

@Famous077 Famous077 commented Feb 24, 2026

fixes #12379

What does this implement/fix?

--> I have Converted onset, duration, description, and ch_names from plain
instance attributes to documented properties in mne/annotations.py,
so they appear in the API reference with proper NumPy-style docstrings.

Additional information

--> Even experienced MNE users were unaware these attributes existed because
they did not appear in the generated API documentation. A changelog entry
has been added in doc/changes/dev/12379.other.rst.

@scott-huberty
Copy link
Copy Markdown
Contributor

@Famous077 can you please sign in to CircleCI via the sign in with Github option? After you do this and push a commit to this branch, the build docs job should run.

@Famous077
Copy link
Copy Markdown
Contributor Author

Famous077 commented Mar 6, 2026

Hi @larsoner @drammock , Changes have been done as per the suggestions. Can you review it whenever you get time. Waiting for your feedback. Hope changes will be helpful for the org.

Copy link
Copy Markdown
Member

@drammock drammock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on main users are free to mutate my_annots.duration pretty freely.. At least compared to the set_annotations route that contains some checks [...] I'm not sure if that was just an oversight. Do we want to be more strict about correctness in these setters, similar to what's done in _check_o_d_s_c_e?

After a real-time chat with @scott-huberty, I think it makes sense to leverage our code in _check_o_d_s_c_e to validate the input to the setters. May require a slight refactor of _check_o_d_s_c_e to avoid duplicating code (so that, e.g., checking descriptions can be done independently of checking onsets, durations, etc)

.. _Ezequiel Mikulan: https://github.com/ezemikulan
.. _Fahimeh Mamashli: https://github.com/fmamashli
.. _Famous Raj Bhat: https://github.com/Famous077
.. _Famous077: https://github.com/Famous077
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will break the xref in previously-merged changelog entries, please use the same name for all your PRs.

@drammock
Copy link
Copy Markdown
Member

also, please add closes #12379 to the PR description, so that when this is merged, the motiviating issue will auto-close

@Famous077
Copy link
Copy Markdown
Contributor Author

Famous077 commented Mar 11, 2026

also, please add closes #12379 to the PR description, so that when this is merged, the motiviating issue will auto-close

@drammock , Sorry for the delay, due to exams . Mistakely I mentioned issue number inside the default description template . Now, I have updated accordingly.

@Famous077 Famous077 requested a review from scott-huberty March 11, 2026 18:53
@Famous077
Copy link
Copy Markdown
Contributor Author

@drammock Can you review this PR whenever you get time. Let me know if anything needs to be changed.

I see no changes since my last review, other than merges of main. Please address my past review's feedback first.

Hi @drammock, I have addressed all the feedbacks. Can you review it whenever you feel free. Let me know if anything I missed to implement. Waiting for you feedback.

@scott-huberty
Copy link
Copy Markdown
Contributor

Hi @drammock, I have addressed all the feedbacks. Can you review it whenever you feel free. Let me know if anything I missed to implement. Waiting for you feedback.

@Famous077 you missed a comment here and probably here

Here is a small snippet to help give you an idea of the hard edges we are trying to help users avoid. This is why Dan suggested looking into what checks we are doing in _check_o_d_s_c_e(sic) (which gets called when you do .set_annotaitons), and replicate the relevant checks in our setters, ideally without duplicating code.

fpath = mne.datasets.sample.data_path() / "MEG" / "sample" / "sample_audvis_raw.fif"
raw = mne.io.read_raw_fif(fpath)

annots = mne.Annotations(onset=[1,3,2,4], duration=0, description="foo")
raw.set_annotations(annots)
raw.annotations.description = raw.annotations.description[:2] # 🚨 Now this has a different length than onset, duration etc

@scott-huberty scott-huberty marked this pull request as draft March 19, 2026 14:26
@Famous077 Famous077 marked this pull request as ready for review March 19, 2026 16:51
@Famous077
Copy link
Copy Markdown
Contributor Author

Hi @scott-huberty , Changes have been implemented as per the suggestion. Can you verify it is working correctly or not?

@drammock
Copy link
Copy Markdown
Member

@scott-huberty LMK when you're satisfied and I'll take another look

@Famous077
Copy link
Copy Markdown
Contributor Author

Hi @scott-huberty , I have addressed your suggestion, All 170 tests pass. Please review when you get a chance if anything else need to be implemented.

Comment on lines +454 to +455
Annotations.duration : Duration of each annotation.
Annotations.description : Description of each annotation.
Copy link
Copy Markdown
Contributor

@scott-huberty scott-huberty Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Annotations.duration : Duration of each annotation.
Annotations.description : Description of each annotation.
:attr:`~mne.Annotations.duration`
:attr:`~mne.Annotations.description`

I think this should work..

@Famous077
Copy link
Copy Markdown
Contributor Author

Hi @scott-huberty , Updated all See Also sections to use :attr: cross-references as suggested. All tests still pass. Let me know if anything else needs changing.

@Famous077 Famous077 requested a review from scott-huberty March 21, 2026 10:29
Copy link
Copy Markdown
Contributor

@scott-huberty scott-huberty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@drammock ready for you to take a final review.

@Famous077
Copy link
Copy Markdown
Contributor Author

Hi @drammock , I have addressed all the suggestions given. Can you review it whenever you get time. Waiting for your feedback.

@Famous077 Famous077 requested a review from scott-huberty March 30, 2026 20:33
Copy link
Copy Markdown
Member

@drammock drammock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jointly reviewed with @scott-huberty



def _check_o_d_s_c_e(onset, duration, description, ch_names, extras):
def _check_onset(onset):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should take an n parameter too, like _check_duration does


@onset.setter
def onset(self, onset):
onset = _check_onset(onset)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
onset = _check_onset(onset)
onset = _check_onset(onset, n=len(self._onset))

Comment on lines +462 to +466
if len(onset) != len(self._duration):
raise ValueError(
f"Length of onset ({len(onset)}) must match the length of "
f"existing duration ({len(self._duration)})."
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with other suggested changes, I think this could then go away.


@duration.setter
def duration(self, duration):
n = len(self._onset)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
n = len(self._onset)
n = len(self._duration)

this shouldn't make a difference... but as written it looks like a typo to be checking onset length in the duration setter. More maintainable this way.

Comment on lines +489 to +493
if len(duration) != n:
raise ValueError(
f"Length of duration ({len(duration)}) must match the length of "
f"existing onset ({n})."
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be guaranteed by _check_duration succeeding?

Comment on lines +517 to +521
if len(description) != n:
raise ValueError(
f"Length of description ({len(description)}) must match the "
f"length of existing onset ({n})."
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be guaranteed by success of _check_description


@ch_names.setter
def ch_names(self, ch_names):
n = len(self._onset)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
n = len(self._onset)
n = len(self._ch_names)

Comment on lines +545 to +550
if len(ch_names) != n:
raise ValueError(
f"Length of ch_names ({len(ch_names)}) must match the length of "
f"existing onset ({n})."
)
self._ch_names = ch_names
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be guaranteed by successful _check_ch_names right?

Comment on lines +1302 to +1306
self._onset = state["onset"]
self._duration = state["duration"]
self._description = state["description"]
self._ch_names = state["ch_names"]
self._extras = state.get("_extras", [None] * len(self._onset))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the new _check_* functions (or check_odsce) here before setting the private attributes.

Comment on lines +1344 to +1347
# Write directly to private attributes to avoid triggering the public
# setter validation, which would raise an error due to temporary length
# mismatches while fields are being extended one at a time.
# The data is already validated by _check_o_d_s_c_e above.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment is in the wrong place??

@Famous077 Famous077 requested a review from drammock April 5, 2026 05:03
@Famous077
Copy link
Copy Markdown
Contributor Author

Hi @drammock , Hi, I've implemented the required changes as per the suggestions. Could you please review the PR when you get a chance and let me know if anything needs to be updated or improved. Thank you

Comment on lines +161 to +169
def _check_onset(onset, n=None):
"""Convert and validate onset to a 1D float array."""
onset = np.atleast_1d(np.array(onset, dtype=float))
if onset.ndim != 1:
raise ValueError(
f"Onset must be a one dimensional array, got {onset.ndim} (shape "
f"{onset.shape})."
)
if n is not None and len(onset) != n:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any instances in which we pass _check_onset(..., n=None). Do we really need to make it optional?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @scott-huberty , The n=None default is needed because _check_onset is called in two places - once inside _check_o_d_s_c_e without n, since length validation is handled there separately, and once in the onset setter with n=len(self._onset) to validate against existing duration. So making it optional lets us reuse the same function in both contexts without duplicating code. Happy to refactor if you prefer a different approach.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing documentation for onset, duration, description attributes in Annotations class?

3 participants