-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add CLAUDE.md #14012
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add CLAUDE.md #14012
Changes from all commits
cd051ac
73275c6
1f396a5
367a9a4
fff2bc1
707fad4
0c68789
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| # 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 | ||
|
|
||
|
|
||
| ## 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)" | ||
| 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) | ||
| ``` | ||
|
|
||
| Docstring / doctest checks: | ||
| ```bash | ||
| pytest mne/tests/test_docstring_parameters.py | ||
| 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/`): | ||
| ```bash | ||
| 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: | ||
| ```bash | ||
| make nesting # import-nesting checks (mne/tests/test_import_nesting.py) | ||
| 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/<format>/` (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_<format>` function and a | ||
| format-specific `Raw<Format>` 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/<PR-number>.<type>.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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this surprised me; can you point to where a decision was made to use absolute imports for new code?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, how quickly we forget 😅 |
||
| 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). | ||
|
cbrnr marked this conversation as resolved.
|
||
| - 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. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing closing quote
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in #14044, let's use that PR to update/add/modify whatever we missed in the first iteration!