Skip to content
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

MAINT: Bump to 3.10 min req #1297

Merged
merged 6 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 10 additions & 21 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 @@ -115,38 +115,26 @@ jobs:
#
# 6 checks:
# for each OS (ubuntu, macos, windows):
larsoner marked this conversation as resolved.
Show resolved Hide resolved
# 3.9 / 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.10 / mne-stable / full / validator-stable
# 3.12 / mne-stable / full / validator-stable
#
# 5 additional checks with alternative MNE-Python and BIDS validator versions:
larsoner marked this conversation as resolved.
Show resolved Hide resolved
# 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]
larsoner marked this conversation as resolved.
Show resolved Hide resolved
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 +143,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 +186,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
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
2 changes: 1 addition & 1 deletion mne_bids/tsv_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def _from_tsv(fname, dtypes=None):
data_dict = OrderedDict()
if dtypes is None:
dtypes = [str] * info.shape[1]
if not isinstance(dtypes, (list, tuple)):
if not isinstance(dtypes, list | tuple):
dtypes = [dtypes] * info.shape[1]
if not len(dtypes) == info.shape[1]:
raise ValueError(
Expand Down
4 changes: 2 additions & 2 deletions mne_bids/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def _age_on_date(bday, exp_date):
def _check_types(variables):
"""Make sure all vars are str or None."""
for var in variables:
if not isinstance(var, (str, type(None))):
if not isinstance(var, str | type(None)):
raise ValueError(
f"You supplied a value ({var}) of type "
f"{type(var)}, where a string or None was "
Expand Down Expand Up @@ -271,7 +271,7 @@ def _get_mrk_meas_date(mrk):
"""Find the measurement date from a KIT marker file."""
info = get_kit_info(mrk, False)[0]
meas_date = info.get("meas_date", None)
if isinstance(meas_date, (tuple, list, np.ndarray)):
if isinstance(meas_date, tuple | list | np.ndarray):
meas_date = meas_date[0]
if isinstance(meas_date, datetime):
meas_datetime = meas_date
Expand Down
6 changes: 3 additions & 3 deletions mne_bids/write.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@


def _is_numeric(n):
return isinstance(n, (np.integer, np.floating, int, float))
return isinstance(n, np.integer | np.floating | int | float)


def _channels_tsv(raw, fname, overwrite=False):
Expand Down Expand Up @@ -459,7 +459,7 @@ def _participants_tsv(raw, subject_id, fname, overwrite=False):
if isinstance(age, tuple): # can be removed once MNE >= 1.8 is required
age = date(*age)
meas_date = raw.info.get("meas_date", None)
if isinstance(meas_date, (tuple, list, np.ndarray)):
if isinstance(meas_date, tuple | list | np.ndarray):
meas_date = meas_date[0]

if meas_date is not None and age is not None:
Expand Down Expand Up @@ -2267,7 +2267,7 @@ def _get_t1w_mgh(fs_subject, fs_subjects_dir):
def _get_landmarks(landmarks, image_nii, kind=""):
import nibabel as nib

if isinstance(landmarks, (str, Path)):
if isinstance(landmarks, str | Path):
landmarks, coord_frame = read_fiducials(landmarks)
landmarks = np.array(
[landmark["r"] for landmark in landmarks], dtype=float
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ maintainers = [
]
license = { text = "BSD-3-Clause" }
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.9"
requires-python = ">=3.10"
keywords = [
"meg",
"eeg",
Expand Down Expand Up @@ -151,4 +151,6 @@ filterwarnings = [
"ignore:datetime\\.datetime\\.utcfromtimestamp.* is deprecated and scheduled for removal in a future version.*:DeprecationWarning",
# matplotlib
"ignore:Figure.*is non-interactive.*cannot be shown:UserWarning",
# NumPy 2.1 bug (probably)
"ignore:__array__ implementation doesn.*:DeprecationWarning",
]