[build-system]
build-backend = "hatchling.build"
requires = ["hatch-vcs", "hatchling >= 1.27"]

[dependency-groups]
dev = ["pip >= 25.1", "pre-commit", "pyside6 >= 6.11.1", "rcssmin >= 1.1", {include-group = "doc"}, {include-group = "test_extra"}]
# Dependencies for building the documentation
doc = [
  # 0.22 moved normalize_options into docutils.parsers.rst.roles
  "docutils >= 0.22",
  "graphviz",
  "intersphinx_registry >= 0.2405.27",
  "ipython != 8.7.0",  # also in "full-no-qt" and "test"
  # 0.8 is the Pyodide 314 line, whose matplotlib, scipy and numpy all satisfy
  # MNE, and 0.23 raised jupyterlite-sphinx's jupyterlite-core cap to < 0.9 so
  # the two resolve together. Below either, the browser drops to Pyodide 0.29
  # and its matplotlib 3.8.4, a minor under the 3.9 MNE declares.
  "jupyterlite-pyodide-kernel >= 0.8",
  "jupyterlite-sphinx >= 0.23",
  "memory_profiler >= 0.16",
  "mne-bids",
  "mne-connectivity",
  "mne-gui-addons",
  "neo",
  "numpydoc >= 0.5",
  "openneuro-py >= 2020.1",
  "psutil",
  "pydata-sphinx-theme >= 0.15.2",
  "pygments >= 2.13",
  "pymef",  # released 2023-06-30, will become 0.12 on 2028-07-01
  "pyvistaqt >= 0.11",  # released 2023-06-30, will become 0.12 on 2028-07-01
  "pyxdf",
  "pyzmq != 24.0.0",
  "refleak >= 0.2.1",  # released 2024-05-21, will become 1.6 on 2026-12-09
  "scikit-learn >= 1.5",  # released 2024-05-21, will become 1.6 on 2026-12-09
  "seaborn >= 0.5, != 0.11.2",
  "selenium >= 4.27.1",
  "sphinx >= 6",
  "sphinx-design",
  "sphinx-gallery >= 0.16",
  "sphinx_copybutton",
  "sphinxcontrib-bibtex >= 2.5",
  "sphinxcontrib-towncrier >= 0.5.0a0",
  "sphinxcontrib-youtube",
]
doc-full = ["rpy2", {include-group = "doc"}]
# Dependencies in a separate group for uv lockfile generation, currently only used for
# the 'old' environment CI test run
lockfile_extras = [
  "pandas >= 2.2, != 3.0.4",  # released 2024-01-20, will become 2.3 on 2027-06-05
  "scikit-learn >= 1.5",  # released 2024-05-21, will become 1.6 on 2026-12-09
]
test = [
  "codespell",
  "ipython >= 8.20",  # for testing notebook backend; also in "full-no-qt" and "doc"
  "numpydoc >= 1.6",
  "pillow >= 10.2",
  "pytest >= 8.0, != 9.1.0",  # https://github.com/pytest-dev/pytest/issues/14591
  "pytest-cov >= 4.1",
  "pytest-qt >= 4.3",
  "pytest-rerunfailures",
  "pytest-timeout >= 2.2",
  "pytest-xdist >= 3.6.1",
  "refleak >= 0.2.1",
  "ruff >= 0.1",
  "threadpoolctl",
  "twine",
  "vulture",
]
# Move non-free-threaded dependencies into the "test_extra" superset
# Exclusions determined 2026/06/16
test_extra = [
  "jupyter_client",  # requires tornado, which has no ft version
  "nbclient",  # requires jupyter_client
  "nitime >= 0.7",
  "pymef",
  "statsmodels",
  {include-group = "test_extra_ft"},
]
# Dependencies for being able to run additional tests (rare/CIs/advanced devs)
# Changes here should be reflected in the mne/utils/config.py dev dependencies section
test_extra_ft = [
  "edfio >= 0.4.10",
  "eeglabio",
  "hedtools",
  "imageio >= 2.6.1",
  "imageio-ffmpeg >= 0.4.1",
  "mne-bids",
  "nbformat",
  "neo",
  "pybv",
  "snirf",
  "sphinx",
  "sphinx-gallery",
  {include-group = "test"},
]

[project]
authors = [
  {email = "alexandre.gramfort@inria.fr", name = "Alexandre Gramfort"},
]
classifiers = [
  "Intended Audience :: Developers",
  "Intended Audience :: Science/Research",
  "License :: OSI Approved",
  "Operating System :: MacOS",
  "Operating System :: Microsoft :: Windows",
  "Operating System :: POSIX :: Linux",
  "Operating System :: POSIX",
  "Operating System :: Unix",
  "Programming Language :: Python :: 3",
  "Programming Language :: Python",
  "Topic :: Scientific/Engineering",
  "Topic :: Software Development",
]
dependencies = [
  "decorator >= 5.1",
  "jinja2 >= 3.1",
  "lazy_loader >= 0.3",
  "matplotlib >= 3.9",  # released 2024-05-15, will become 3.10 on 2026-12-14
  "numpy >= 2.1, < 3",  # released 2024-08-18, will become 2.2 on 2026-12-08
  "packaging",
  "pooch >= 1.5",
  "scipy >= 1.14",  # released 2024-06-24, will become 1.15 on 2027-01-03
  "tqdm >= 4.66",
]
description = "MNE-Python is an open-source Python package for exploring, visualizing, and analyzing human neurophysiological data. It provides methods for data input/output, preprocessing, visualization, source estimation, time-frequency analysis, machine learning, and statistics."
dynamic = ["version"]
keywords = [
  "brain",
  "DBS",
  "deep brain stimulation",
  "ECoG",
  "EEG",
  "electrocorticography",
  "electroencephalography",
  "fNIRS",
  "functional near-infrared spectroscopy",
  "iEEG",
  "intracranial EEG",
  "magnetoencephalography",
  "MEG",
  "neuroimaging",
  "neuroscience",
]
license = "BSD-3-Clause"
license-files = ["LICENSE.txt"]
maintainers = [{email = "dan@mccloy.info", name = "Dan McCloy"}]
name = "mne"
readme = {content-type = "text/x-rst", file = "README.rst"}
requires-python = ">= 3.11"
# ↑↑↑↑↑↑↑↑↑↑↑ when this changes, bump the `--python-version` in the `uv pip compile ...`
# command in `.github/workflows/autofix.yml` (astral-sh/uv/#16333), and also
# the `python` version for the `kind: old` matrix entry in `.github/workflows/tests.yml`
scripts = {mne = "mne.commands.utils:main"}

[project.optional-dependencies]
# Leave this one here for backward-compat
data = []
full = ["mne[full-no-qt]", "PySide6 != 6.7.0, != 6.8.0, != 6.8.0.1, != 6.9.1"]
# Dependencies for full MNE-Python functionality (other than raw/epochs export)
# We first define a variant without any Qt bindings. The "complete" variant, mne[full],
# makes an opinionated choice and installs PySide6.
# We also offer mne[full-pyqt6], which will install PyQt6 instead of PySide6.
full-no-qt = [
  "antio >= 0.5.0",
  "curryreader >= 0.1.2",
  "darkdetect",
  "defusedxml",
  "dipy >= 1.9",  # released 2024-03-08, will become 1.10 on 2026-12-12
  "edfio >= 0.4.10",
  "eeglabio",
  "filelock >= 3.18.0",
  "h5py >= 2.4",
  "imageio >= 2.6.1",
  "imageio-ffmpeg >= 0.4.1",
  "ipyevents",
  "ipympl",
  "ipython >= 2.0, != 8.7.0",  # for notebook backend; also in "doc" and "test"
  "ipywidgets",
  "joblib >= 0.8",
  "jupyter",
  "mffpy >= 0.11.0",
  "mne-qt-browser >= 0.6",  # released 2023-10-11, will become 0.7 on 2027-04-08
  "mne[hdf5]",
  "neo",
  "nest-asyncio2",
  "nibabel >= 5.2",  # released 2023-12-11, will become 5.3 on 2026-10-08
  "nilearn >= 0.10",  # released 2023-01-06, will become 0.11 on 2026-12-02
  "numba >= 0.60",  # released 2024-06-13, will become 0.61 on 2027-01-20
  "openmeeg >= 2.5.7",
  "pandas >= 2.2, != 3.0.4",  # released 2024-01-20, will become 2.3 on 2027-06-05
  "pillow",  # for `Brain.save_image` and `mne.Report`
  "pyarrow",  # only needed to avoid a deprecation warning in pandas
  "pybv",
  "pymef",
  "pyobjc-framework-Cocoa >= 5.2.0; platform_system == 'Darwin'",
  "python-picard >= 0.4",
  "pyvista >= 0.44",  # released 2024-07-07, will become 0.45 on 2027-04-19
  "pyvistaqt >= 0.11",  # released 2023-06-30, will become 0.12 on 2028-07-01
  "qdarkstyle >= 3.2.2",  # released 2023-11-27, no newer version available
  "qtpy",
  "scikit-learn >= 1.5",  # released 2024-05-21, will become 1.6 on 2026-12-09
  "sip",
  "snirf",
  "statsmodels >= 0.6",
  "threadpoolctl",
  "traitlets",
  "trame",
  "trame-pyvista",
  "trame-vtk",
  "trame-vuetify != 3.2.3",
  "vtk >= 9.2",
  "xlrd",
]
full-pyqt6 = ["mne[full-no-qt]", "PyQt6 != 6.6.0", "PyQt6-Qt6 != 6.6.0, != 6.7.0"]
full-pyside6 = ["mne[full]"]
# Dependencies for MNE-Python functions that use HDF5 I/O
hdf5 = ["h5io >= 0.2.4", "pymatreader"]

[project.urls]
"Bug Tracker" = "https://github.com/mne-tools/mne-python/issues/"
Documentation = "https://mne.tools/"
Download = "https://pypi.org/project/mne/#files"
Forum = "https://mne.discourse.group/"
Homepage = "https://mne.tools/"
"Source Code" = "https://github.com/mne-tools/mne-python/"

[tool.bandit.assert_used]
skips = ["*/test_*.py"]  # assert statements are good practice with pytest

[tool.changelog-bot]

[tool.changelog-bot.towncrier_changelog]
changelog_skip_label = "no-changelog-entry-needed"
enabled = true
verify_pr_number = true

[tool.codespell]
builtin = "clear,rare,informal,names,usage"
ignore-words = "ignore_words.txt"
skip = "doc/references.bib"

[tool.hatch.build]
exclude = [
  "/*.toml",
  "/*.txt",
  "/*.yaml",
  "/*.yml",
  "/.*",
  "/CITATION.cff",
  "/codemeta.json",
  "/CONTRIBUTING.md",
  "/doc",
  "/examples",
  "/ignore_words.txt",
  "/logo",
  "/Makefile",
  "/mne/**/tests",
  "/tools",
  "/tutorials",
]  # tracked by git, but we don't want to ship those files

[tool.hatch.version]
raw-options = {version_scheme = "release-branch-semver"}
source = "vcs"

[tool.numpydoc_validation]
checks = [
  "all",  # report on all checks, except the below
  "ES01",  # no extended summary
  "EX01",  # no examples
  "EX02",  # examples failed (we test them separately)
  "GL01",  # Docstring should start in the line immediately after the quotes
  "PR04",  # Parameter "shape (n_channels" has no type
  "RT02",  # The first line of the Returns section should contain only the type, unless multiple values are being returned  # noqa
  "SA01",  # no see also
  "SA04",  # no description in See Also
]
exclude = [
  # dict subclasses
  '\.clear',
  '\.get$',
  '\.copy$',
  '\.fromkeys',
  '\.items',
  '\.keys',
  '\.move_to_end',
  '\.pop',
  '\.popitem',
  '\.setdefault',
  '\.update',
  '\.values',
  # list subclasses
  '\.append',
  '\.count',
  '\.extend',
  '\.index',
  '\.insert',
  '\.remove',
  '\.sort',
  # we currently don't document these properly (probably okay)
  '\.__getitem__',
  '\.__contains__',
  '\.__hash__',
  '\.__mul__',
  '\.__sub__',
  '\.__add__',
  '\.__iter__',
  '\.__div__',
  '\.__neg__',
  # copied from sklearn
  'mne\.utils\.deprecated',
]

[tool.pytest.ini_options]
# -r f (failed), E (error), s (skipped), x (xfail), X (xpassed), w (warnings)
# don't put in xfail for pytest 8.0+ because then it prints the tracebacks,
# which look like real errors
addopts = [
  "--capture=sys",
  "--color=yes",
  "--cov-branch",
  "--cov-report=",
  "--doctest-ignore-import-errors",
  "--doctest-modules",
  "--durations=20",
  "--ignore=mne/gui/_*.py",
  "--ignore=mne/icons",
  "--ignore=mne/report/js_and_css",
  "--junit-xml=junit-results.xml",
  "--tb=short",
  "-ra",
]
testpaths = [
  "mne",
]

[tool.rstcheck]
ignore_directives = [
  "autoclass",
  "autofunction",
  "automodule",
  "autosummary",
  "bibliography",
  "card",
  "cssclass",
  "currentmodule",
  "dropdown",
  "footbibliography",
  "glossary",
  "graphviz",
  "grid",
  "highlight",
  "minigallery",
  "related-software",
  "rst-class",
  "tab-set",
  "tabularcolumns",
  "toctree",
  "towncrier-draft-entries",
]
ignore_messages = "^.*(Unknown target name|Undefined substitution referenced)[^`]*$"
ignore_roles = [
  "attr",
  "bdg-info-line",
  "bdg-primary-line",
  "class",
  "doc",
  "eq",
  "exc",
  "file",
  "footcite",
  "footcite:t",
  "func",
  "gh",
  "kbd",
  "meth",
  "mod",
  "newcontrib",
  "py:mod",
  "ref",
  "samp",
  "term",
]
report_level = "WARNING"

[tool.ruff]
exclude = ["__init__.py", "constants.py", "resources.py"]

[tool.ruff.lint]
ignore = [
  "D100",  # Missing docstring in public module
  "D104",  # Missing docstring in public package
  "D413",  # Missing blank line after last section
]
select = ["A", "B006", "D", "E", "F", "I", "UP", "UP031", "W"]

[tool.ruff.lint.per-file-ignores]
"examples/*/*.py" = [
  "D205",  # 1 blank line required between summary line and description
  "D400",  # First line should end with a period
]
"examples/preprocessing/eeg_bridging.py" = [
  "E501",  # line too long
]
"mne/__init__.pyi" = [
  "A004",  # Import *** is shadowing a Python builtin
]
"mne/datasets/*/*.py" = [
  "D103",  # Missing docstring in public function
]
"mne/decoding/tests/test_*.py" = [
  "E402",  # Module level import not at top of file
]
"mne/utils/tests/test_docs.py" = [
  "D101",  # Missing docstring in public class
  "D410",  # Missing blank line after section
  "D411",  # Missing blank line before section
  "D414",  # Section has no content
]
"tutorials/*/*.py" = [
  "D400",  # First line should end with a period
]
"tutorials/time-freq/10_spectrum_class.py" = [
  "E501",  # line too long
]

[tool.ruff.lint.pydocstyle]
convention = "numpy"
ignore-decorators = [
  "mne.utils.copy_doc",
  "mne.utils.copy_function_doc_to_method_doc",
  "mne.utils.deprecated",
  "property",
  "setter",
]

[tool.tomlsort]
all = true
ignore_case = true
spaces_before_inline_comment = 2
trailing_comma_inline_array = true

[tool.tomlsort.overrides."tool.numpydoc_validation.exclude"]
inline_arrays = false

[tool.towncrier]
directory = "doc/changes/dev/"
filename = "doc/changes/dev.rst"
issue_format = "`#{issue} <https://github.com/mne-tools/mne-python/pull/{issue}>`__"
package = "mne"
title_format = "{version} ({project_date})"

[[tool.towncrier.type]]
directory = "notable"
name = "Notable changes"
showcontent = true

[[tool.towncrier.type]]
directory = "dependency"
name = "Dependencies"
showcontent = true

[[tool.towncrier.type]]
directory = "bugfix"
name = "Bugfixes"
showcontent = true

[[tool.towncrier.type]]
directory = "apichange"
name = "API changes by deprecation"
showcontent = true

[[tool.towncrier.type]]
directory = "newfeature"
name = "New features"
showcontent = true

[[tool.towncrier.type]]
directory = "other"
name = "Other changes"
showcontent = true

[tool.ty.environment]
python-version = "3.11"  # minimum supported, keep in sync with requires-python

# Files that are strictly type-checked with no rule overrides. A file is added
# here only once it passes ty cleanly; grow this list as more of the codebase is
# annotated and cleaned up.
[tool.ty.src]
exclude = ["mne/io/**/tests"]
include = [
  "mne/annotations.py",
  "mne/epochs.py",
  "mne/evoked.py",
  "mne/io",
]

[tool.vulture]
exclude = [
  'conftest.py',
  'constants.py',
  'mne/viz/backends/_abstract.py',
  'mne/viz/backends/_notebook.py',
  'mne/viz/backends/_qt.py',
]
ignore_decorators = ['@observe']
min_confidence = 60
paths = ['mne', 'tools/vulture_allowlist.py']
verbose = false