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
1 change: 1 addition & 0 deletions doc/changes/dev/13497.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a bug where ``mne.io.read_raw_gdf`` failed with NumPy ≥1.24 due to the removal of ``np.fromstring`` binary mode. Replaced with ``np.frombuffer`` for compatibility, by :newcontrib:`Dev Parikh`.
1 change: 1 addition & 0 deletions doc/changes/names.inc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
.. _David Sabbagh: https://github.com/DavidSabbagh
.. _Demetres Kostas: https://github.com/kostasde
.. _Denis Engemann: https://denis-engemann.de
.. _Dev Parikh: https://github.com/devparikh0506
.. _Dinara Issagaliyeva: https://github.com/dissagaliyeva
.. _Diptyajit Das: https://github.com/dasdiptyajit
.. _Dirk Gütlin: https://github.com/DiGyt
Expand Down
55 changes: 15 additions & 40 deletions doc/sphinxext/related_software.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

# If it's not available on PyPI, add it to this dict:
MANUAL_PACKAGES = {
# TODO: These packages are not pip-installable as of 2024/07/17, so we have to
# TODO: These packages are not pip-installable as of 2025/11/19, so we have to
# manually populate them -- should open issues on their package repos.
"best-python": {
"Home-page": "https://github.com/multifunkim/best-python",
Expand All @@ -54,68 +54,34 @@
# This package does not provide wheels, so don't force CircleCI to build it.
# If it eventually provides binary wheels we could add it to
# `tools/circleci_dependencies.sh` and remove from here.
# https://github.com/Eelbrain/Eelbrain/issues/130
"eelbrain": {
"Home-page": "https://eelbrain.readthedocs.io/en/stable/",
"Summary": "Open-source Python toolkit for MEG and EEG data analysis.",
},
# mne-kit-gui requires mayavi (ugh)
"mne-kit-gui": {
"Home-page": "https://github.com/mne-tools/mne-kit-gui",
"Summary": "A module for KIT MEG coregistration.",
},
# fsleyes requires wxpython, which needs to build
"fsleyes": {
"Home-page": "https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FSLeyes",
"Summary": "FSLeyes is the FSL image viewer.",
},
# dcm2niix must be built from source
"dcm2niix": {
"Home-page": "https://github.com/rordenlab/dcm2niix",
"Summary": "DICOM to NIfTI converter",
},
# TODO: these do not set a valid homepage or documentation page on PyPI
"python-picard": { # https://github.com/mind-inria/picard/issues/60
"Home-page": "https://github.com/mind-inria/picard",
"Summary": "Preconditioned ICA for Real Data",
},
"eeg_positions": {
"Home-page": "https://eeg-positions.readthedocs.io",
"Summary": "Compute and plot standard EEG electrode positions.",
},
"mne-faster": {
"Home-page": "https://github.com/wmvanvliet/mne-faster",
"Summary": "MNE-FASTER: automatic bad channel/epoch/component detection.", # noqa: E501
},
"mne-features": {
"Home-page": "https://mne.tools/mne-features",
"Summary": "MNE-Features software for extracting features from multivariate time series", # noqa: E501
},
"mne-rsa": {
"Home-page": "https://users.aalto.fi/~vanvlm1/mne-rsa",
"Summary": "Code for performing Representational Similarity Analysis on MNE-Python data structures.", # noqa: E501
},
"mffpy": {
"Home-page": "https://github.com/BEL-Public/mffpy",
"Summary": "Reader and Writer for Philips' MFF file format.",
},
"emd": {
"Home-page": "https://emd.readthedocs.io/en/stable",
"Summary": "Empirical Mode Decomposition in Python.",
},
# Needs a release with homepage set properly
"meegkit": {
"Home-page": "https://nbara.github.io/python-meegkit",
"Summary": "Denoising tools for M/EEG processing.",
},
# not on PyPI
"conpy": {
"Home-page": "https://github.com/aaltoimaginglanguage/conpy",
"Summary": "Functions and classes for performing connectivity analysis on MEG data.", # noqa: E501
},
}

REQUIRE_METADATA = os.getenv("MNE_REQUIRE_RELATED_SOFTWARE_INSTALLED", "false").lower()
REQUIRE_METADATA = REQUIRE_METADATA in ("true", "1")
REQUIRE_INSTALLED = os.getenv("MNE_REQUIRE_RELATED_SOFTWARE_INSTALLED", "false").lower()
REQUIRE_INSTALLED = REQUIRE_INSTALLED in ("true", "1")
REQUIRE_METADATA = REQUIRE_INSTALLED

# These packages pip-install with a different name than the package name
RENAMES = {
Expand Down Expand Up @@ -201,7 +167,7 @@ def _get_packages() -> dict[str, str]:
if "Home-page" in md:
url = md["Home-page"]
else:
for prefix in ("homepage", "documentation"):
for prefix in ("homepage", "documentation", "user documentation"):
for key, val in md.items():
if key == "Project-URL" and val.lower().startswith(
f"{prefix}, "
Expand All @@ -217,6 +183,12 @@ def _get_packages() -> dict[str, str]:
continue
out[package]["url"] = url
out[package]["description"] = md["Summary"].replace("\n", "")
if not REQUIRE_INSTALLED:
reasons = [
reason
for reason in reasons
if "not found, needs to be installed" not in reason
]
reason_str = "\n".join(reasons)
if reason_str and REQUIRE_METADATA:
raise ExtensionError(
Expand Down Expand Up @@ -263,6 +235,9 @@ def setup(app):


if __name__ == "__main__": # pragma: no cover
# running `python doc/sphinxext/related_software.py` for testing
# require metadata for any installed packages (for debugging)
REQUIRE_METADATA = True
items = list(RelatedSoftwareDirective.run(None)[0].children)
print(f"Got {len(items)} related software packages:")
for item in items:
Expand Down
1 change: 1 addition & 0 deletions doc/sphinxext/related_software.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ autoreject
bycycle
conpy
curryreader
dcm2niix
eeg_positions
emd
fooof
Expand Down
4 changes: 4 additions & 0 deletions doc/sphinxext/related_software_nodeps.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# deps with onerous requirements (tensorflow, wxPython, mayavi)
cross-domain-saliency-maps
fsleyes
mne-kit-gui
6 changes: 3 additions & 3 deletions mne/io/edf/edf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1734,9 +1734,9 @@ def _read_gdf_header(fname, exclude, include=None):
edf_info["data_offset"] + edf_info["n_records"] * edf_info["bytes_tot"]
)
fid.seek(etp) # skip data to go to event table
etmode = fid.read(1).decode()
if etmode != "":
etmode = np.fromstring(etmode, UINT8).tolist()[0]
etmode = fid.read(1)
if isinstance(etmode, (bytes, bytearray)) and len(etmode) == 1:
etmode = np.frombuffer(etmode, dtype=UINT8).tolist()[0]

if edf_info["number"] < 1.94:
sr = read_from_file_or_buffer(fid, UINT8, 3)
Expand Down
10 changes: 10 additions & 0 deletions mne/io/edf/tests/test_gdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,13 @@ def test_gdf_read_from_file_like():

with pytest.raises(Exception, match="Bad GDF file provided."):
read_raw_gdf(BytesIO(), preload=True)


@testing.requires_testing_data
@pytest.mark.filterwarnings("ignore:Highpass cutoff frequency")
def test_gh_13414():
"""Test handling bytes objects when reading GDF events."""
fpath = data_path / "GDF" / "test_gdf_1.99.gdf"
raw = read_raw_gdf(fpath)
# Should be 1 event in this GDF file.
assert len(raw.annotations) == 1
3 changes: 2 additions & 1 deletion tools/circleci_dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ python -m pip install --upgrade --progress-bar off \
"git+https://github.com/mne-tools/mne-qt-browser.git" \
"git+https://github.com/pyvista/pyvista.git" \
"git+https://github.com/sphinx-gallery/sphinx-gallery.git"
python -m pip install --upgrade --progress-bar off --no-deps cross-domain-saliency-maps
python -m pip install --upgrade --progress-bar off --no-deps \
-r doc/sphinxext/related_software_nodeps.txt
Loading