Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
sappelhoff authored Aug 23, 2024
2 parents a25800c + 471bd0c commit 83c7d6e
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 72 deletions.
38 changes: 14 additions & 24 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.9", "3.12"] # Oldest and newest supported versions
python-version: ["3.10", "3.12"] # Oldest and newest supported versions
steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
Expand Down Expand Up @@ -113,40 +113,29 @@ jobs:
test:
# For GitHub "required" CI checks, add in branch protection:
#
# 6 checks:
# for each OS (ubuntu, macos, windows):
# 3.9 / mne-stable / full / validator-stable
# 8 checks:
# for each machine type (ubuntu, macos, macos-13, windows):
# (NOTE: macos-13=x86_64, macos>13=arm64)
# 3.10 / mne-stable / full / validator-stable
# 3.12 / mne-stable / full / validator-stable
#
# 1 additional check for Apple Silicon (doesn't support Python 3.9):
# 3.12 / mne-stable / full / validator-stable
#
# 5 additional checks with alternative MNE-Python and BIDS validator versions:
# 3 additional checks with alternative MNE-Python and BIDS validator versions:
# ubuntu / 3.12 / mne-main / full / validator-main
# ubuntu / 3.9 / mne-prev / full / validator-stable
# ubuntu / 3.10 / mne-prev / full / validator-stable
# ubuntu / 3.12 / mne-stable / minimal / validator-stable
timeout-minutes: 60
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.9", "3.12"] # Oldest and newest supported versions
os: [ubuntu-latest, macos-latest, macos-13, windows-latest]
python-version: ["3.10", "3.12"] # Oldest and newest supported versions
mne-version: [mne-stable]
mne-bids-install: [full]
bids-validator-version: [validator-stable]

include:
# special test runs running only on single CI systems to save resources
#
# macOS-14 (Apple Silicon) only works with Python 3.10+
# Once we drop support for Python 3.9, move it to the "proper" matrix above.
- os: macos-14
python-version: "3.12"
mne-version: mne-stable
mne-bids-install: full
bids-validator-version: validator-stable

# Test development versions
- os: ubuntu-latest
python-version: "3.12"
Expand All @@ -155,7 +144,7 @@ jobs:
bids-validator-version: validator-main
# Test previous MNE stable version
- os: ubuntu-latest
python-version: "3.9"
python-version: "3.10"
mne-version: mne-prev-stable
mne-bids-install: full
bids-validator-version: validator-stable
Expand Down Expand Up @@ -198,14 +187,15 @@ jobs:
- name: Install MNE (stable)
if: matrix.mne-version == 'mne-stable'
run: |
git clone --single-branch --branch maint/1.6 https://github.com/mne-tools/mne-python.git
git clone --single-branch --branch maint/1.8 https://github.com/mne-tools/mne-python.git
python -m pip install -e ./mne-python
- name: Install MNE (previous stable)
if: matrix.mne-version == 'mne-prev-stable'
# Have to install NumPy<2.1 here because of a change in positional arg handling + MNE 1.7
run: |
git clone --single-branch --branch maint/1.6 https://github.com/mne-tools/mne-python.git
python -m pip install -e ./mne-python
git clone --single-branch --branch maint/1.7 https://github.com/mne-tools/mne-python.git
python -m pip install -e ./mne-python "numpy<2.1"
- name: Install MNE (main)
if: matrix.mne-version == 'mne-main'
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.7
rev: v0.6.1
hooks:
- id: ruff
name: ruff mne_bids/
Expand Down
4 changes: 4 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ authors:
family-names: Benitez
affiliation: 'Magnetoencephalography Core, National Institutes of Health, Bethesda, Maryland, USA'
orcid: 'https://orcid.org/0000-0001-6364-7272'
- given-names: Thomas
family-names: Hartmann
affiliation: 'Paris-Lodron-University Salzburg, Centre for Cogntitive Neuroscience, Department of Psychology, Salzburg, Austria'
orcid: 'https://orcid.org/0000-0002-8298-8125'
- given-names: Alexandre
family-names: Gramfort
affiliation: 'Université Paris-Saclay, Inria, CEA, Palaiseau, France'
Expand Down
1 change: 1 addition & 0 deletions doc/authors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@
.. _Julius Welzel: https://github.com/JuliusWelzel
.. _Kaare Mikkelsen: https://github.com/kaare-mikkelsen
.. _Amaia Benitez: https://github.com/AmaiaBA
.. _Thomas Hartmann: https://github.com/thht
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
"use_edit_page_button": False,
"navigation_with_keys": False,
"show_toc_level": 1,
"header_links_before_dropdown": 6,
"navbar_end": ["theme-switcher", "version-switcher", "navbar-icon-links"],
"analytics": dict(google_analytics_id="G-C8SH9E98QC"),
"switcher": {
Expand Down
4 changes: 4 additions & 0 deletions doc/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The following authors contributed for the first time. Thank you so much! 🤩

* `Kaare Mikkelsen`_
* `Amaia Benitez`_
* `Thomas Hartmann`_

The following authors had contributed before. Thank you for sticking around! 🤘

Expand All @@ -33,17 +34,20 @@ Detailed list of changes
^^^^^^^^^^^^^^^

- :meth:`mne_bids.BIDSPath.match()` and :func:`mne_bids.find_matching_paths` now have additional parameters ``ignore_json`` and ``ignore_nosub``, to give users more control over which type of files are matched, by `Kaare Mikkelsen`_ (:gh:`1281`)
- :func:`mne_bids.write_raw_bids()` can now handle event metadata as a pandas DataFrame, by `Thomas Hartmann`_ (:gh:`1285`)

🧐 API and behavior changes
^^^^^^^^^^^^^^^^^^^^^^^^^^^

- :func:`mne_bids.read_raw_bids` no longer warns about unit changes in channels upon reading, as that information is taken from ``channels.tsv`` and judged authorative, by `Stefan Appelhoff`_ (:gh:`1282`)
- MEG OPM channels are now experimentally included, by `Amaia Benitez`_ (:gh:`1222`)
- :func:`mne_bids.mark_channels` will no longer create a ``status_description`` column filled with ``n/a`` in the ``channels.tsv`` file, by `Stefan Appelhoff`_ (:gh:`1293`)

🛠 Requirements
^^^^^^^^^^^^^^^

- MNE-BIDS now requires MNE-Python 1.6.0 or higher.
- MNE-BIDS now requires Python 3.10 or higher.

🪲 Bug fixes
^^^^^^^^^^^^
Expand Down
36 changes: 17 additions & 19 deletions mne_bids/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from os import path as op
from pathlib import Path
from textwrap import indent
from typing import Optional

import numpy as np
from mne.utils import _check_fname, _validate_type, logger, verbose
Expand Down Expand Up @@ -448,7 +447,7 @@ def directory(self):
return Path(data_path)

@property
def subject(self) -> Optional[str]:
def subject(self) -> str | None:
"""The subject ID."""
return self._subject

Expand All @@ -457,7 +456,7 @@ def subject(self, value):
self.update(subject=value)

@property
def session(self) -> Optional[str]:
def session(self) -> str | None:
"""The acquisition session."""
return self._session

Expand All @@ -466,7 +465,7 @@ def session(self, value):
self.update(session=value)

@property
def task(self) -> Optional[str]:
def task(self) -> str | None:
"""The experimental task."""
return self._task

Expand All @@ -475,7 +474,7 @@ def task(self, value):
self.update(task=value)

@property
def run(self) -> Optional[str]:
def run(self) -> str | None:
"""The run number."""
return self._run

Expand All @@ -484,7 +483,7 @@ def run(self, value):
self.update(run=value)

@property
def acquisition(self) -> Optional[str]:
def acquisition(self) -> str | None:
"""The acquisition parameters."""
return self._acquisition

Expand All @@ -493,7 +492,7 @@ def acquisition(self, value):
self.update(acquisition=value)

@property
def processing(self) -> Optional[str]:
def processing(self) -> str | None:
"""The processing label."""
return self._processing

Expand All @@ -502,7 +501,7 @@ def processing(self, value):
self.update(processing=value)

@property
def recording(self) -> Optional[str]:
def recording(self) -> str | None:
"""The recording name."""
return self._recording

Expand All @@ -511,7 +510,7 @@ def recording(self, value):
self.update(recording=value)

@property
def space(self) -> Optional[str]:
def space(self) -> str | None:
"""The coordinate space for an anatomical or sensor position file."""
return self._space

Expand All @@ -520,7 +519,7 @@ def space(self, value):
self.update(space=value)

@property
def description(self) -> Optional[str]:
def description(self) -> str | None:
"""The description entity."""
return self._description

Expand All @@ -529,7 +528,7 @@ def description(self, value):
self.update(description=value)

@property
def suffix(self) -> Optional[str]:
def suffix(self) -> str | None:
"""The filename suffix."""
return self._suffix

Expand All @@ -538,7 +537,7 @@ def suffix(self, value):
self.update(suffix=value)

@property
def root(self) -> Optional[Path]:
def root(self) -> Path | None:
"""The root directory of the BIDS dataset."""
return self._root

Expand All @@ -547,7 +546,7 @@ def root(self, value):
self.update(root=value)

@property
def datatype(self) -> Optional[str]:
def datatype(self) -> str | None:
"""The BIDS data type, e.g. ``'anat'``, ``'meg'``, ``'eeg'``."""
return self._datatype

Expand All @@ -556,7 +555,7 @@ def datatype(self, value):
self.update(datatype=value)

@property
def split(self) -> Optional[str]:
def split(self) -> str | None:
"""The split of the continuous recording file for ``.fif`` data."""
return self._split

Expand All @@ -565,7 +564,7 @@ def split(self, value):
self.update(split=value)

@property
def extension(self) -> Optional[str]:
def extension(self) -> str | None:
"""The extension of the filename, including a leading period."""
return self._extension

Expand Down Expand Up @@ -1474,7 +1473,7 @@ def search_folder_for_text(
def _check_max_depth(max_depth):
"""Check that max depth is a proper input."""
msg = "`max_depth` must be a positive integer or None"
if not isinstance(max_depth, (int, type(None))):
if not isinstance(max_depth, int | type(None)):
raise ValueError(msg)
if max_depth is None:
max_depth = float("inf")
Expand Down Expand Up @@ -2057,9 +2056,8 @@ def get_entity_vals(

for filename in filenames:
# Skip ignored directories
# XXX In Python 3.9, we can use Path.is_relative_to() here
if any(
[str(filename).startswith(str(ignore_dir)) for ignore_dir in ignore_dirs]
[Path(filename).is_relative_to(ignore_dir) for ignore_dir in ignore_dirs]
):
continue

Expand Down Expand Up @@ -2219,7 +2217,7 @@ def _infer_datatype(*, root, sub, ses):

def _path_to_str(var):
"""Make sure var is a string or Path, return string representation."""
if not isinstance(var, (Path, str)):
if not isinstance(var, Path | str):
raise ValueError(
f"All path parameters must be either strings or "
f"pathlib.Path objects. Found type {type(var)}."
Expand Down
18 changes: 17 additions & 1 deletion mne_bids/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def _read_events(events, event_id, raw, bids_path=None):

# If we have events, convert them to Annotations so they can be easily
# merged with existing Annotations.
if events.size > 0:
if events.size > 0 and event_id is not None:
ids_without_desc = set(events[:, 2]) - set(event_id.values())
if ids_without_desc:
raise ValueError(
Expand Down Expand Up @@ -200,6 +200,22 @@ def _read_events(events, event_id, raw, bids_path=None):
raw.set_annotations(annotations)
del id_to_desc_map, annotations, new_annotations

if events.size > 0 and event_id is None:
new_annotations = mne.annotations_from_events(
events=events,
sfreq=raw.info["sfreq"],
orig_time=raw.annotations.orig_time,
)

raw = raw.copy() # Don't alter the original.
annotations = raw.annotations.copy()

# We use `+=` here because `Annotations.__iadd__()` does the right
# thing and also performs a sanity check on `Annotations.orig_time`.
annotations += new_annotations
raw.set_annotations(annotations)
del annotations, new_annotations

# Now convert the Annotations to events.
all_events, all_desc = events_from_annotations(
raw,
Expand Down
Loading

0 comments on commit 83c7d6e

Please sign in to comment.