Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/credit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ jobs:
git checkout -b credit
git commit -am "MAINT: Update code credit"
git push origin credit
PR_NUM=$(gh pr create --base main --head credit --title "MAINT: Update code credit" --body "Created by credit [GitHub action](https://github.com/mne-tools/mne-python/actions/runs/${{ github.run_id }})." --label "no-changelog-entry-needed")
PR_NUM=$(gh pr create --base main --head credit --title "MAINT: Update code credit" --body "Created by credit [GitHub action](https://github.com/mne-tools/mne-python/actions/runs/${{ github.run_id }}). <br> <br> *Adjustments may need to be made to `doc/changes/credit_tools.py` or `.mailmap` etc. to get CircleCI to pass.*" --label "no-changelog-entry-needed")
echo "Opened https://github.com/mne-tools/mne-python/pull/${PR_NUM}" >> $GITHUB_STEP_SUMMARY
if: steps.status.outputs.dirty == 'true'
2 changes: 1 addition & 1 deletion .github/workflows/spec_zero.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ jobs:
git checkout -b spec_zero
git commit -am "MAINT: Update dependency specifiers"
git push origin spec_zero
PR_NUM=$(gh pr create --base main --head spec_zero --title "MAINT: Update dependency specifiers" --body "Created by spec_zero [GitHub action](https://github.com/mne-tools/mne-python/actions/runs/${{ github.run_id }})." --label "no-changelog-entry-needed")
PR_NUM=$(gh pr create --base main --head spec_zero --title "MAINT: Update dependency specifiers" --body "Created by spec_zero [GitHub action](https://github.com/mne-tools/mne-python/actions/runs/${{ github.run_id }}). <br> <br> *Adjustments may need to be made to shims in mne/fixes.py in this or another PR. `git grep TODO VERSION` is a good starting point for finding potential updates.*" --label "no-changelog-entry-needed")
echo "Opened https://github.com/mne-tools/mne-python/pull/${PR_NUM}" >> $GITHUB_STEP_SUMMARY
if: steps.status.outputs.dirty == 'true'
1 change: 1 addition & 0 deletions doc/changes/dev/13448.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for Nihon Kohden EEG-1200A V01.00, by `Eric Larson`_.
1 change: 1 addition & 0 deletions doc/changes/dev/13458.apichange.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ``encoding`` parameter to :func:`mne.io.read_raw_nihon` for better handling of annotation decoding, by `Tom Ma`_.
6 changes: 3 additions & 3 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ dependencies:
- PySide6 !=6.9.1
- python-neo
- python-picard
- pyvista >=0.32,!=0.35.2,!=0.38.0,!=0.38.1,!=0.38.2,!=0.38.3,!=0.38.4,!=0.38.5,!=0.38.6,!=0.42.0
- pyvistaqt >=0.4
- pyvista >=0.42.1
- pyvistaqt >=0.11
- qdarkstyle !=3.2.2
- qtpy
- scikit-learn >=1.3.0
- scikit-learn >=1.3
- scipy >=1.11
- sip
- snirf
Expand Down
2 changes: 1 addition & 1 deletion mne/io/artemis123/tests/test_artemis123.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ def _assert_trans(actual, desired, dist_tol=0.017, angle_tol=5.0):
assert angle <= angle_tol, f"{angle:0.3f} > {angle_tol:0.3f}° rotation"


@pytest.mark.timeout(60) # ~25 s on Travis Linux OpenBLAS
@testing.requires_testing_data
def test_artemis_reader():
"""Test reading raw Artemis123 files."""
_test_raw_reader(
read_raw_artemis123,
input_fname=short_hpi_1kz_fname,
pos_fname=dig_fname,
add_head_trans=False,
verbose="error",
)

Expand Down
25 changes: 10 additions & 15 deletions mne/io/eeglab/tests/test_eeglab.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import numpy as np
import pytest
from flaky import flaky
from numpy.testing import (
assert_allclose,
assert_array_almost_equal,
Expand All @@ -23,7 +24,6 @@
from mne.channels import read_custom_montage
from mne.datasets import testing
from mne.io import read_raw_eeglab
from mne.io.eeglab import _eeglab as eeglab_mod
from mne.io.eeglab._eeglab import _readmat
from mne.io.eeglab.eeglab import _dol_to_lod, _get_montage_information
from mne.io.tests.test_raw import _test_raw_reader
Expand Down Expand Up @@ -769,25 +769,20 @@ def test_eeglab_drop_nan_annotations(tmp_path):
raw = read_raw_eeglab(file_path, preload=True)


@flaky
@testing.requires_testing_data
@pytest.mark.timeout(10)
def test_io_set_preload_false_is_faster(monkeypatch):
@pytest.mark.slowtest # has the advantage of not running on macOS where it errs a lot
def test_io_set_preload_false_is_faster():
"""Using preload=False should skip the expensive data read branch."""
real_loadmat = eeglab_mod.loadmat
call_counts = {"n": 0}

def counting_loadmat(*args, **kwargs):
call_counts["n"] += 1
return real_loadmat(*args, **kwargs)

monkeypatch.setattr(eeglab_mod, "loadmat", counting_loadmat)
# warm start
read_raw_eeglab(raw_fname_mat, preload=False)

durations = {}
with _record_warnings():
for preload in (False, True):
start = time.perf_counter()
_ = read_raw_eeglab(raw_fname_mat, preload=preload)
durations[preload] = time.perf_counter() - start
for preload in (True, False):
start = time.perf_counter()
_ = read_raw_eeglab(raw_fname_mat, preload=preload)
durations[preload] = time.perf_counter() - start

# preload=True should not be faster than preload=False (timings may vary
# across systems, so avoid strict thresholds)
Expand Down
39 changes: 22 additions & 17 deletions mne/io/nihon/nihon.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ def _ensure_path(fname):


@fill_doc
def read_raw_nihon(fname, preload=False, verbose=None) -> "RawNihon":
def read_raw_nihon(
fname, preload=False, *, encoding="utf-8", verbose=None
) -> "RawNihon":
"""Reader for an Nihon Kohden EEG file.

Parameters
Expand All @@ -32,6 +34,9 @@ def read_raw_nihon(fname, preload=False, verbose=None) -> "RawNihon":
Path to the Nihon Kohden data file (``.EEG``).
preload : bool
If True, all data are loaded at initialization.
%(encoding_nihon)s

.. versionadded:: 1.11
%(verbose)s

Returns
Expand All @@ -44,7 +49,7 @@ def read_raw_nihon(fname, preload=False, verbose=None) -> "RawNihon":
--------
mne.io.Raw : Documentation of attributes and methods of RawNihon.
"""
return RawNihon(fname, preload, verbose)
return RawNihon(fname, preload, encoding=encoding, verbose=verbose)


_valid_headers = [
Expand All @@ -57,7 +62,7 @@ def read_raw_nihon(fname, preload=False, verbose=None) -> "RawNihon":
"EEG-2100 V02.00",
"DAE-2100D V01.30",
"DAE-2100D V02.00",
# 'EEG-1200A V01.00', # Not working for the moment.
"EEG-1200A V01.00",
]


Expand All @@ -66,8 +71,10 @@ def _read_nihon_metadata(fname):
fname = _ensure_path(fname)
pnt_fname = fname.with_suffix(".PNT")
if not pnt_fname.exists():
warn("No PNT file exists. Metadata will be blank")
return metadata
pnt_fname = fname.with_suffix(".pnt")
if not pnt_fname.exists():
warn("No PNT file exists. Metadata will be blank")
return metadata
logger.info("Found PNT file, reading metadata.")
with open(pnt_fname) as fid:
version = np.fromfile(fid, "|S16", 1).astype("U16")[0]
Expand Down Expand Up @@ -315,7 +322,7 @@ def _parse_sub_event_log(sub_event_log):
return t_sub_desc, t_sub_onset


def _read_nihon_annotations(fname):
def _read_nihon_annotations(fname, encoding="utf-8"):
fname = _ensure_path(fname)
log_fname = fname.with_suffix(".LOG")
if not log_fname.exists():
Expand Down Expand Up @@ -346,15 +353,10 @@ def _read_nihon_annotations(fname):
t_onset += t_sub_onset

t_desc = t_desc.rstrip(b"\x00")
for enc in _encodings:
try:
t_desc = t_desc.decode(enc)
except UnicodeDecodeError:
pass
else:
break
else:
warn(f"Could not decode log as one of {_encodings}")
try:
t_desc = t_desc.decode(encoding)
except UnicodeDecodeError:
warn(f"Could not decode log as {encoding}")
continue

all_onsets.append(t_onset)
Expand Down Expand Up @@ -414,6 +416,9 @@ class RawNihon(BaseRaw):
Path to the Nihon Kohden data ``.eeg`` file.
preload : bool
If True, all data are loaded at initialization.
%(encoding_nihon)s

.. versionadded:: 1.11
%(verbose)s

See Also
Expand All @@ -422,7 +427,7 @@ class RawNihon(BaseRaw):
"""

@verbose
def __init__(self, fname, preload=False, verbose=None):
def __init__(self, fname, preload=False, *, encoding="utf-8", verbose=None):
fname = _check_fname(fname, "read", True, "fname")
data_name = fname.name
logger.info(f"Loading {data_name}")
Expand Down Expand Up @@ -468,7 +473,7 @@ def __init__(self, fname, preload=False, verbose=None):
)

# Get annotations from LOG file
annots = _read_nihon_annotations(fname)
annots = _read_nihon_annotations(fname, encoding)

# Annotate acquisition skips
controlblock = header["controlblocks"][0]
Expand Down
1 change: 1 addition & 0 deletions mne/time_frequency/tests/test_tfr.py
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ def test_tfr_decim_and_shift_time(epochs, method, freqs, decim):
assert_array_equal(freqs, tfr.freqs)


@pytest.mark.slowtest
@pytest.mark.parametrize("inst", ("raw_tfr", "epochs_tfr", "average_tfr"))
def test_tfr_io(inst, average_tfr, request, tmp_path):
"""Test TFR I/O."""
Expand Down
5 changes: 5 additions & 0 deletions mne/utils/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,11 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75):
encoding according to the EDF+ standard).
"""

docdict["encoding_nihon"] = """
encoding : str
Text encoding of Nihon Kohden annotations. See :ref:`standard-encodings`.
"""

docdict["encoding_nirx"] = """
encoding : str
Text encoding of the NIRX header file. See :ref:`standard-encodings`.
Expand Down
3 changes: 2 additions & 1 deletion mne/viz/backends/_pyvista.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ def __init__(
):
from .._3d import _get_3d_option

_require_version("pyvista", "use 3D rendering", "0.32")
# TODO VERSION change whenever PyVista min gets updated:
_require_version("pyvista", "use 3D rendering", "0.42")
multi_samples = _get_3d_option("multi_samples")
# multi_samples > 1 is broken on macOS + Intel Iris + volume rendering
if platform.system() == "Darwin":
Expand Down
18 changes: 9 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ dependencies = [
"decorator",
"jinja2",
"lazy_loader >= 0.3",
"matplotlib >= 3.8", # released: 2023/09/15
"numpy >= 1.26,<3", # released: 2023/09/16
"matplotlib >= 3.8", # released 2023-09-15, will become 3.9 on 2026-05-15
"numpy >= 1.26, < 3", # released 2023-09-16, will become 2.0 on 2026-06-16
"packaging",
"pooch >= 1.5",
"scipy >= 1.11", # released: 2023/06/25
"scipy >= 1.11", # released 2023-06-28, will become 1.12 on 2026-01-19
"tqdm",
]
description = "MNE-Python project for MEG and EEG data analysis."
Expand Down Expand Up @@ -78,7 +78,7 @@ doc = [
"sphinx-gallery >= 0.16",
"sphinx_copybutton",
"sphinxcontrib-bibtex >= 2.5",
"sphinxcontrib-towncrier >=0.5.0a0",
"sphinxcontrib-towncrier >= 0.5.0a0",
"sphinxcontrib-youtube",
]
full = ["mne[full-no-qt]", "PyQt6 != 6.6.0", "PyQt6-Qt6 != 6.6.0, != 6.7.0"]
Expand Down Expand Up @@ -112,17 +112,17 @@ full-no-qt = [
"nilearn",
"numba",
"openmeeg >= 2.5.7",
"pandas >= 2.1", # released: 2023/08/30
"pandas >= 2.1", # released 2023-08-30, will become 2.2 on 2026-01-19
"pillow", # for `Brain.save_image` and `mne.Report`
"pyarrow", # only needed to avoid a deprecation warning in pandas
"pybv",
"pyobjc-framework-Cocoa >= 5.2.0; platform_system == 'Darwin'",
"python-picard",
"pyvista >= 0.32, != 0.35.2, != 0.38.0, != 0.38.1, != 0.38.2, != 0.38.3, != 0.38.4, != 0.38.5, != 0.38.6, != 0.42.0",
"pyvistaqt >= 0.4",
"pyvista >= 0.42.1", # released 2023-09-06, will become 0.43 on 2025-12-06
"pyvistaqt >= 0.11", # released 2023-06-30, no newer version available
"qdarkstyle != 3.2.2",
"qtpy",
"scikit-learn >=1.3.0", # released: 2023/06/30
"scikit-learn >= 1.3", # released 2023-06-30, will become 1.4 on 2026-01-17
"sip",
"snirf",
"statsmodels",
Expand Down Expand Up @@ -152,7 +152,7 @@ test = [
"pytest-timeout",
"ruff",
"toml-sort",
"tomli; python_version<'3.11'",
"tomli; python_version < '3.11'",
"twine",
"vulture",
"wheel",
Expand Down
Loading