diff --git a/doc/install.rst b/doc/install.rst index ab1bbae48..c88b62357 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -20,7 +20,7 @@ Required: Optional: * ``nibabel`` (>=2.5, for processing MRI data) -* ``pybv`` (>=0.6, for writing BrainVision data) +* ``pybv`` (>=0.7.3, for writing BrainVision data) * ``pymatreader`` (>=0.0.29 , for operations with EEGLAB data) * ``matplotlib`` (>=3.1.0, for using the interactive data inspector) * ``pandas`` (>=0.24.0, for generating event statistics) diff --git a/doc/whats_new.rst b/doc/whats_new.rst index abbee9c6b..54df44230 100644 --- a/doc/whats_new.rst +++ b/doc/whats_new.rst @@ -50,12 +50,12 @@ Detailed list of changes - :func:`mne_bids.make_dataset_description` now accepts keyword arguments only, and can now also write the following metadata: ``HEDVersion``, ``EthicsApprovals``, ``GeneratedBy``, and ``SourceDatasets``, by `Stefan Appelhoff`_ (:gh:`406`) -- The deprecated function ```mne_bids.mark_bad_channels`` has been removed in favor of :func:`mne_bids.mark_channels`, by `Richard Höchenberger`_ (:gh:`1009`) +- The deprecated function ``mne_bids.mark_bad_channels`` has been removed in favor of :func:`mne_bids.mark_channels`, by `Richard Höchenberger`_ (:gh:`1009`) 🛠 Requirements ^^^^^^^^^^^^^^^ -- ... +- Writing BrainVision files now requires ``pybv`` version ``0.7.3``, by `Stefan Appelhoff`_ (:gh:`1011`) 🪲 Bug fixes ^^^^^^^^^^^^ diff --git a/examples/anonymize_dataset.py b/examples/anonymize_dataset.py index 0065f82b6..30a300e95 100644 --- a/examples/anonymize_dataset.py +++ b/examples/anonymize_dataset.py @@ -16,7 +16,7 @@ MNE-BIDS provides a dedicated function, :func:`mne_bids.anonymize_dataset`, to do the heavy lifting for you, automatically. -""" +""" # noqa: D400 D205 # Authors: Richard Höchenberger # License: BSD-3-Clause diff --git a/examples/convert_mne_sample.py b/examples/convert_mne_sample.py index 6d91265b2..ddb8efee9 100644 --- a/examples/convert_mne_sample.py +++ b/examples/convert_mne_sample.py @@ -10,8 +10,7 @@ In this example we will use MNE-BIDS to organize the MNE sample data according to the BIDS standard. In a second step we will read the organized dataset using MNE-BIDS. - -""" +""" # noqa: D400 D205 # Authors: Mainak Jas # Alexandre Gramfort diff --git a/examples/mark_bad_channels.py b/examples/mark_bad_channels.py index 84f373741..02860d0a5 100644 --- a/examples/mark_bad_channels.py +++ b/examples/mark_bad_channels.py @@ -13,7 +13,7 @@ segments as "bad". .. _MNE-Python Annotations tutorial: https://mne.tools/stable/auto_tutorials/raw/30_annotate_raw.html#annotating-raw-objects-interactively -""" # noqa:E501 +""" # noqa: E501 D400 D205 # Authors: Richard Höchenberger # diff --git a/examples/rename_brainvision_files.py b/examples/rename_brainvision_files.py index dcbe15bbe..229e4ecf1 100644 --- a/examples/rename_brainvision_files.py +++ b/examples/rename_brainvision_files.py @@ -93,7 +93,7 @@ # ------------------- # # For converting data files, or writing new data to the BrainVision format, you -# can use the `pybv`_ Python package. +# can use :mod:`mne.export` or have a look at the `pybv`_ Python package. # # There is node JS tool to check the integrity of your BrainVision files. # For that, see the `BrainVision Validator `_ diff --git a/mne_bids/commands/tests/test_cli.py b/mne_bids/commands/tests/test_cli.py index 5b6e4a16d..3e60c4763 100644 --- a/mne_bids/commands/tests/test_cli.py +++ b/mne_bids/commands/tests/test_cli.py @@ -109,7 +109,6 @@ def test_cp(tmp_path): def test_mark_bad_chanels_single_file(tmp_path): """Test mne_bids mark_channels.""" - # Check that help is printed check_usage(mne_bids_mark_channels) @@ -162,7 +161,6 @@ def test_mark_bad_chanels_single_file(tmp_path): def test_mark_bad_chanels_multiple_files(tmp_path): """Test mne_bids mark_channels.""" - # Check that help is printed check_usage(mne_bids_mark_channels) @@ -204,7 +202,6 @@ def test_mark_bad_chanels_multiple_files(tmp_path): def test_calibration_to_bids(tmp_path): """Test mne_bids calibration_to_bids.""" - # Check that help is printed check_usage(mne_bids_calibration_to_bids) @@ -224,7 +221,6 @@ def test_calibration_to_bids(tmp_path): def test_crosstalk_to_bids(tmp_path): """Test mne_bids crosstalk_to_bids.""" - # Check that help is printed check_usage(mne_bids_crosstalk_to_bids) @@ -245,7 +241,6 @@ def test_crosstalk_to_bids(tmp_path): @requires_pandas def test_count_events(tmp_path): """Test mne_bids count_events.""" - # Check that help is printed check_usage(mne_bids_count_events) @@ -290,7 +285,6 @@ def test_count_events(tmp_path): @requires_version('mne', '0.22') def test_inspect(tmp_path): """Test mne_bids inspect.""" - # Check that help is printed check_usage(mne_bids_inspect) diff --git a/mne_bids/config.py b/mne_bids/config.py index 357f57dbe..0bd489d41 100644 --- a/mne_bids/config.py +++ b/mne_bids/config.py @@ -5,6 +5,8 @@ BIDS_VERSION = "1.7.0" +PYBV_VERSION = "0.7.3" + DOI = """https://doi.org/10.21105/joss.01896""" EPHY_ALLOWED_DATATYPES = ['meg', 'eeg', 'ieeg', 'nirs'] diff --git a/mne_bids/tests/conftest.py b/mne_bids/tests/conftest.py index 67fc1d8f9..e8dd09466 100644 --- a/mne_bids/tests/conftest.py +++ b/mne_bids/tests/conftest.py @@ -1,3 +1,4 @@ +"""Configure tests.""" import os import platform diff --git a/mne_bids/tests/test_write.py b/mne_bids/tests/test_write.py index f5d0333e7..6e79a059f 100644 --- a/mne_bids/tests/test_write.py +++ b/mne_bids/tests/test_write.py @@ -48,7 +48,8 @@ from mne_bids.sidecar_updates import _update_sidecar, update_sidecar_json from mne_bids.path import _find_matching_sidecar, _parse_ext from mne_bids.pick import coil_type -from mne_bids.config import REFERENCES, BIDS_COORD_FRAME_DESCRIPTIONS +from mne_bids.config import (REFERENCES, BIDS_COORD_FRAME_DESCRIPTIONS, + PYBV_VERSION) base_path = op.join(op.dirname(mne.__file__), 'io') subject_id = '01' @@ -456,7 +457,7 @@ def test_line_freq(line_freq, _bids_validate, tmp_path): assert eeg_json['PowerLineFrequency'] == 'n/a' -@requires_version('pybv', '0.6') +@requires_version('pybv', PYBV_VERSION) @pytest.mark.filterwarnings(warning_str['channel_unit_changed']) @pytest.mark.filterwarnings(warning_str['maxshield']) def test_fif(_bids_validate, tmp_path): @@ -515,12 +516,6 @@ def test_fif(_bids_validate, tmp_path): events = mne.find_events(raw2) event_id = {'auditory/left': 1, 'auditory/right': 2, 'visual/left': 3, 'visual/right': 4, 'smiley': 5, 'button': 32} - # XXX: Need to remove "Status" channel until pybv supports - # channels that are non-Volt - idxs = mne.pick_types(raw.info, meg=False, stim=True) - stim_ch_names = np.array(raw.ch_names)[idxs] - raw2.drop_channels(stim_ch_names) - raw.drop_channels(stim_ch_names) epochs = mne.Epochs(raw2, events, event_id=event_id, tmin=-0.2, tmax=0.5, preload=True) @@ -1136,7 +1131,7 @@ def test_vhdr(_bids_validate, tmp_path): assert op.exists(events_tsv_fname) # test anonymize and convert - if check_version('pybv', '0.6'): + if check_version('pybv', PYBV_VERSION): raw = _read_raw_brainvision(raw_fname) output_path = _test_anonymize(tmp_path / 'tmp', raw, bids_path) _bids_validate(output_path) @@ -1404,7 +1399,7 @@ def test_eegieeg(dir_name, fname, reader, _bids_validate, tmp_path): assert len(list(data.values())[0]) == 2 # check that scans list is properly converted to brainvision - if check_version('pybv', '0.6') or dir_name == 'EDF': + if check_version('pybv', PYBV_VERSION) or dir_name == 'EDF': if raw.info['meas_date'] is not None: daysback_min, daysback_max = _get_anonymization_daysback(raw) daysback = (daysback_min + daysback_max) // 2 @@ -1597,7 +1592,7 @@ def test_eegieeg(dir_name, fname, reader, _bids_validate, tmp_path): write_raw_bids(**kwargs) # test anonymize and convert - if check_version('pybv', '0.6') or dir_name == 'EDF': + if check_version('pybv', PYBV_VERSION) or dir_name == 'EDF': raw = reader(raw_fname) bids_path.update(root=bids_root, datatype='eeg') kwargs = dict(raw=raw, bids_path=bids_path, overwrite=True) @@ -1635,7 +1630,6 @@ def test_eegieeg(dir_name, fname, reader, _bids_validate, tmp_path): ) def test_snirf(_bids_validate, tmp_path): """Test write_raw_bids conversion for SNIRF data.""" - raw_fname = op.join(testing.data_path(), 'SNIRF', 'MNE-NIRS', '20220217', '20220217_nirx_15_3_recording.snirf') bids_path = _bids_path.copy().update(root=tmp_path, datatype='nirs') @@ -1790,7 +1784,7 @@ def test_set(_bids_validate, tmp_path): _bids_validate(bids_root) # test anonymize and convert - if check_version('pybv', '0.6'): + if check_version('pybv', PYBV_VERSION): with pytest.warns(RuntimeWarning, match='Encountered data in "double" format'): output_path = _test_anonymize(tmp_path / 'tmp', raw, bids_path) @@ -2078,6 +2072,7 @@ def test_write_anat(_bids_validate, tmp_path): def test_write_raw_pathlike(tmp_path): + """Ensure writing pathlib.Path works.""" data_path = Path(testing.data_path()) raw_fname = op.join(data_path, 'MEG', 'sample', 'sample_audvis_trunc_raw.fif') @@ -2100,6 +2095,7 @@ def test_write_raw_pathlike(tmp_path): def test_write_raw_no_dig(tmp_path): + """Test writing without dig.""" data_path = testing.data_path() raw_fname = op.join(data_path, 'MEG', 'sample', 'sample_audvis_trunc_raw.fif') @@ -2955,7 +2951,7 @@ def test_sidecar_encoding(_bids_validate, tmp_path): @requires_version('mne', '0.24') -@requires_version('pybv', '0.6') +@requires_version('pybv', PYBV_VERSION) @pytest.mark.parametrize( 'dir_name, format, fname, reader', test_converteeg_data) @pytest.mark.filterwarnings( @@ -3031,7 +3027,7 @@ def test_convert_eeg_formats(dir_name, format, fname, reader, tmp_path): @requires_version('mne', '0.24') -@requires_version('pybv', '0.6') +@requires_version('pybv', PYBV_VERSION) @pytest.mark.parametrize( 'dir_name, format, fname, reader', test_converteeg_data) @pytest.mark.filterwarnings( @@ -3325,7 +3321,7 @@ def test_write_associated_emptyroom( def test_preload(_bids_validate, tmp_path): - """Test writing custom preloaded raw objects""" + """Test writing custom preloaded raw objects.""" bids_root = tmp_path / 'bids' bids_path = _bids_path.copy().update(root=bids_root) sfreq, n_points = 1024., int(1e6) diff --git a/mne_bids/write.py b/mne_bids/write.py index 96d312bca..4bc51eca0 100644 --- a/mne_bids/write.py +++ b/mne_bids/write.py @@ -55,7 +55,7 @@ IGNORED_CHANNELS, ALLOWED_DATATYPE_EXTENSIONS, BIDS_VERSION, REFERENCES, _map_options, reader, ALLOWED_INPUT_EXTENSIONS, CONVERT_FORMATS, - ANONYMIZED_JSON_KEY_WHITELIST, + ANONYMIZED_JSON_KEY_WHITELIST, PYBV_VERSION, BIDS_STANDARD_TEMPLATE_COORDINATE_SYSTEMS) @@ -975,9 +975,9 @@ def _write_raw_brainvision(raw, bids_fname, events, overwrite): overwrite : bool Whether or not to overwrite existing files. """ - if not check_version('pybv', '0.6'): # pragma: no cover - raise ImportError('pybv >=0.6 is required for converting ' - 'file to BrainVision format') + if not check_version('pybv', PYBV_VERSION): # pragma: no cover + raise ImportError(f'pybv >= {PYBV_VERSION} is required for converting' + ' file to BrainVision format') from pybv import write_brainvision # Subtract raw.first_samp because brainvision marks events starting from # the first available data point and ignores the raw.first_samp diff --git a/setup.cfg b/setup.cfg index 12286b993..46e14ad46 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,7 +45,7 @@ include_package_data = True [options.extras_require] full = nibabel >= 2.5 - pybv >= 0.6 + pybv >= 0.7.3 matplotlib >= 3.1.0 pandas >= 0.24.0 EDFlib-Python >= 1.0.6 @@ -110,3 +110,6 @@ exclude_lines = # Don't complain if non-runnable code isn't run: if __name__ == .__main__.: + +[isort] +skip_glob = */* diff --git a/test_requirements.txt b/test_requirements.txt index 8b4a4fc1e..4227e243b 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -4,7 +4,7 @@ mne>=0.24 matplotlib>=3.1 pandas>=0.24.0 nibabel>=2.5 -pybv>=0.6 +pybv>=0.7.3 openneuro-py>=2022.1 EDFlib-Python>=1.0.6 pymatreader>=0.0.29