From cd051acba835c2fc641d9032107d656d4924e5c9 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Wed, 1 Jul 2026 13:27:41 +0200 Subject: [PATCH 1/7] Add CLAUDE.md --- CLAUDE.md | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000..c50ded2ec22 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,142 @@ +# CLAUDE.md + +This file provides guidance to AI coding agents when working with code in this repository. + +## What this is + +MNE-Python is a large open-source library for exploring, analyzing, and visualizing human +neurophysiological data (MEG, EEG, sEEG, ECoG, fNIRS, etc.): I/O for dozens of vendor formats, +preprocessing, source estimation, time-frequency, connectivity, statistics, decoding, and 2D/3D +visualization. + +## AI-assistance policy (read first) + +This project has an explicit policy on AI-generated contributions (`CONTRIBUTING.md`): +- Fully-automated PRs/issues are not accepted; a human must review, understand, and be able to + explain every change before it's submitted under their name. +- Any PR that used AI assistance must disclose that in the PR description (tool + scope of use). +- Do not paste AI-generated text into issue/PR descriptions or comments. +- Be careful that generated code for specialized algorithms doesn't reproduce copyrighted + passages from papers/other codebases (license risk). + +## Common commands + +Install an editable dev environment (see `pyproject.toml` dependency groups): +```bash +pip install -e ".[test_extra,doc]" # or use `uv sync` with the [dependency-groups] in pyproject.toml +pre-commit install --install-hooks +``` + +Lint / format (ruff, codespell, yamllint, rstcheck, toml-sort, zizmor — all via pre-commit): +```bash +make ruff # alias for `pre-commit run -a` +``` + +Run tests: +```bash +# whole suite (slow; needs the testing dataset, fetched automatically via pooch) +pytest -m "not ultraslowtest" mne + +# a single test file / test / by keyword +pytest mne/tests/test_evoked.py::test_io_evoked --verbose +pytest mne/tests/test_evoked.py -k test_io_evoked --verbose + +# fetch datasets explicitly if needed +python -c "import mne; mne.datasets.testing.data_path(verbose=True)" +make sample_data +make testing_data + +# useful flags: -x (stop on first failure), --pdb, --durations=5, +# --cov=mne.viz --cov-report=term-missing (see which lines are covered) +``` + +Docstring / doctest checks: +```bash +pytest mne/tests/test_docstring_parameters.py +make test-doc # runs doctests across doc/ (requires sample + testing datasets) +``` + +Build the docs (Sphinx + sphinx-gallery, in `doc/`): +```bash +cd doc && make html # or `make html-noplot` for a faster build without executing examples +``` + +Other: +```bash +make nesting # import-nesting checks (mne/tests/test_import_nesting.py) +make codespell # manual spellcheck pass +make clean # remove build artifacts, __pycache__, *.pyc/*.so +``` + +There is no separate "build" step for the library itself beyond the editable install (pure +Python + hatchling/hatch-vcs for versioning from git tags). + +## Architecture + +### Lazy public API via stub files +`mne/__init__.py` uses `lazy_loader.attach_stub` against `mne/__init__.pyi` — the `.pyi` file is +the actual source of truth for what's in `mne.__all__` and lazily importable, not the `.py` file. +Many subpackages (`mne/io`, `mne/utils`, etc.) follow the same `__init__.py` + `__init__.pyi` +pattern. When adding a new public function/class, it typically needs to be added to the relevant +`__init__.pyi` (and, for docs, to `doc/python_reference.rst`) as well as implemented. + +### I/O readers: one subpackage per format +`mne/io//` (ant, array, artemis123, bci2k, besa, boxy, brainvision, bti, cnt, ctf, curry, +edf, eeglab, egi, eximia, eyelink, fieldtrip, fil, hitachi, kit, mef, nedf, neuralynx, nicolet, +nihon, nirx, nsx, persyst, snirf, ...) each implement a `read_raw_` function and a +format-specific `Raw` subclass of `BaseRaw` (`mne/io/base.py`). `mne/io/_read_raw.py` +provides the generic `read_raw()` dispatcher. New format support follows this same shape: a +subpackage with a reader function + `BaseRaw` subclass + its own `tests/` dir with small +synthetic/testing-dataset-backed fixtures. + +### FIF internals live in `mne/_fiff`, not `mne/io/fiff` +Neuromag FIF is MNE's native format and many core objects (`Info`, projections, compensators, +channel picking, tag/tree reading) depend on it, so that logic was pulled out of `mne/io/` into +`mne/_fiff/` (private) to avoid import cycles and because it's used well beyond raw I/O. +`mne/io/_fiff_wrap.py` re-exports select `mne._fiff` symbols for backward compatibility (some +were previously public under `mne.io`). + +### Core data containers and mixins +`BaseRaw` (`mne/io/base.py`), `Epochs`/`BaseEpochs` (`mne/epochs.py`), and `Evoked` +(`mne/evoked.py`) are the central objects; shared behavior (channel picking/renaming, filtering, +cropping, projections, export) lives in mixins under `mne/channels/`, `mne/filter.py`, +`mne/utils/mixin.py`, etc. and is composed via multiple inheritance rather than duplicated per +class. + +### Shared/templated docstrings +Common parameter descriptions live in a central dict in `mne/utils/docs.py` and are spliced into +function/method docstrings via the `@fill_doc` decorator + `%(param_name)s` placeholders — grep +for `docdict[` / `@fill_doc` before writing out a parameter docstring by hand, it's likely already +defined. + +### Changelog is per-PR fragment files (towncrier), not a single hand-edited file +User-facing changes need a file `doc/changes/dev/..rst` (types: `notable`, +`dependency`, `bugfix`, `apichange`, `newfeature`, `other` — see `doc/development/contributing.rst` +"Describe your changes in the changelog" section for full guidance). These get aggregated into +`doc/changes/dev.rst` at release time; don't edit `doc/changes/dev.rst` or the versioned +`doc/changes/vX.Y.rst` files directly for new changes. New contributors must also add themselves +to `doc/changes/names.inc` (build fails otherwise) and are credited with `:newcontrib:` in their +changelog entry instead of a plain name link. + +## Code conventions (beyond what ruff enforces) + +- Classes: `CamelCase`. Functions/variables: `snake_case`, no abbreviated names like `nsamples`. +- Docstrings: numpydoc style with a few local deviations — no "optional" on kwargs with defaults, + `str | None` instead of "str or None", no `Raises`/`Warns` sections, citations via + `sphinxcontrib-bibtex` (`:footcite:`/`footbibliography::`, keys defined in `doc/references.bib`). +- Cross-reference liberally in docstrings/docs using Sphinx roles (`:func:`, `:class:`, `:meth:`, + `:attr:`, `:mod:`, `:ref:`) — but note an API element must appear in `doc/python_reference.rst` + for the cross-reference to resolve. +- Imports: Use absolute imports for new code (historical relative imports are tolerated in + existing code). Optional/heavy deps (matplotlib, scipy, sklearn, pandas, ...) are imported lazily + inside the function/method that needs them, not at module level. +- Methods mutate in place and return `self`; module-level functions return copies. +- No bare `*args`/`**kwargs` in signatures; no nested functions/methods (use private + module-level functions instead). +- Visualization: add a function in `mne.viz` and have the corresponding object method + (e.g. `Epochs.plot`) call it, not the reverse. All viz functions take a `show` bool. Default + colormap is `RdBu_r` for signed/zero-centered data, `Reds` otherwise. +- Deprecations use the `@mne.utils.deprecated` decorator (functions/classes) or + `mne.utils.warn(..., FutureWarning)` (parameters); add a test asserting the warning fires, and + grep for internal call sites to update immediately rather than at end-of-cycle. +- Prefer the `testing` dataset over `sample`/other large datasets in tests (smaller, faster). From 73275c6dc809851983fba4165e881db466414b86 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Thu, 2 Jul 2026 11:45:25 +0200 Subject: [PATCH 2/7] Update CLAUDE.md Co-authored-by: Eric Larson --- CLAUDE.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index c50ded2ec22..f470546a816 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -43,8 +43,7 @@ pytest mne/tests/test_evoked.py -k test_io_evoked --verbose # fetch datasets explicitly if needed python -c "import mne; mne.datasets.testing.data_path(verbose=True)" -make sample_data -make testing_data +python -c "import mne; mne.datasets.sample.data_path(verbose=True) # useful flags: -x (stop on first failure), --pdb, --durations=5, # --cov=mne.viz --cov-report=term-missing (see which lines are covered) From 1f396a596b7b665791ad60fd974cb8f5f79a19bf Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Thu, 2 Jul 2026 11:45:41 +0200 Subject: [PATCH 3/7] Update CLAUDE.md Co-authored-by: Eric Larson --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index f470546a816..70b1889501a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,7 +52,7 @@ python -c "import mne; mne.datasets.sample.data_path(verbose=True) Docstring / doctest checks: ```bash pytest mne/tests/test_docstring_parameters.py -make test-doc # runs doctests across doc/ (requires sample + testing datasets) +make test-doc # runs doctests across doc/ (requires sample + testing datasets, generally only needed when changing example code in doc/ itself) ``` Build the docs (Sphinx + sphinx-gallery, in `doc/`): From 367a9a4c7f0ab07b94ac2e0f979ced9b168ec9bf Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Thu, 2 Jul 2026 11:45:55 +0200 Subject: [PATCH 4/7] Update CLAUDE.md Co-authored-by: Eric Larson --- CLAUDE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 70b1889501a..95c4645d096 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,7 +57,8 @@ make test-doc # runs doctests across doc/ (requires sample + testing da Build the docs (Sphinx + sphinx-gallery, in `doc/`): ```bash -cd doc && make html # or `make html-noplot` for a faster build without executing examples +PATTERN=some_regex_pattern make -C doc html-pattern # can choose some_regex_pattern to subselect relevant examples and tutorials to run +make -C doc html # full build, takes about an hour, generally only needed if changing docs extensively ``` Other: From fff2bc181ec816443843568b008e33855d0ace63 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Thu, 2 Jul 2026 11:46:08 +0200 Subject: [PATCH 5/7] Update CLAUDE.md Co-authored-by: Eric Larson --- CLAUDE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 95c4645d096..ddab7381998 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -64,7 +64,6 @@ make -C doc html # full build, takes about an hour, generally only needed Other: ```bash make nesting # import-nesting checks (mne/tests/test_import_nesting.py) -make codespell # manual spellcheck pass make clean # remove build artifacts, __pycache__, *.pyc/*.so ``` From 707fad41c23ff8878d6d616f5fbff4a4633879e2 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Thu, 2 Jul 2026 11:46:32 +0200 Subject: [PATCH 6/7] Update CLAUDE.md Co-authored-by: Eric Larson --- CLAUDE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index ddab7381998..d4ce9692c69 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -139,3 +139,5 @@ changelog entry instead of a plain name link. `mne.utils.warn(..., FutureWarning)` (parameters); add a test asserting the warning fires, and grep for internal call sites to update immediately rather than at end-of-cycle. - Prefer the `testing` dataset over `sample`/other large datasets in tests (smaller, faster). +- Prefer to keep unit tests compact and add to existing tests when possible. The full test suite takes about an hour on CIs, so minimizing test time (for CIs) and test verbosity (for reviewers) is important. +- When new functionality is added, it is good in general to add it somewhere in an example (`examples/`) or a tutorial (`tutorials/`) to help with discoverability and documentation. From 0c68789e5906318cb1a22aa72ba4b60d8b7f4921 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Thu, 2 Jul 2026 11:51:57 +0200 Subject: [PATCH 7/7] Import CONTRIBUTING.md --- CLAUDE.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d4ce9692c69..f2252d8c717 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,13 +11,10 @@ visualization. ## AI-assistance policy (read first) -This project has an explicit policy on AI-generated contributions (`CONTRIBUTING.md`): -- Fully-automated PRs/issues are not accepted; a human must review, understand, and be able to - explain every change before it's submitted under their name. -- Any PR that used AI assistance must disclose that in the PR description (tool + scope of use). -- Do not paste AI-generated text into issue/PR descriptions or comments. -- Be careful that generated code for specialized algorithms doesn't reproduce copyrighted - passages from papers/other codebases (license risk). +This project has an explicit policy on AI-generated contributions: + +@CONTRIBUTING.md + ## Common commands