From 96251a8338e0bd0d2aa4458d02b24a4d7d86bbc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20H=C3=B6chenberger?= Date: Thu, 28 Mar 2024 05:38:53 +0100 Subject: [PATCH] Drop support for Python 3.9, drop `Optional` and `Union` from type hints (#908) Co-authored-by: Eric Larson --- docs/source/examples/gen_examples.py | 3 +- docs/source/settings/gen_settings.py | 25 +-- docs/source/v1.9.md.inc | 8 +- mne_bids_pipeline/_config.py | 188 +++++++++--------- mne_bids_pipeline/_config_import.py | 15 +- mne_bids_pipeline/_config_utils.py | 26 +-- mne_bids_pipeline/_decoding.py | 4 +- mne_bids_pipeline/_import_data.py | 98 ++++----- mne_bids_pipeline/_logging.py | 23 +-- mne_bids_pipeline/_parallel.py | 3 +- mne_bids_pipeline/_reject.py | 8 +- mne_bids_pipeline/_report.py | 28 +-- mne_bids_pipeline/_run.py | 17 +- .../steps/init/_01_init_derivatives_dir.py | 3 +- .../steps/init/_02_find_empty_room.py | 7 +- .../steps/preprocessing/_01_data_quality.py | 23 +-- .../steps/preprocessing/_02_head_pos.py | 15 +- .../steps/preprocessing/_03_maxfilter.py | 21 +- .../preprocessing/_04_frequency_filter.py | 40 ++-- .../preprocessing/_05_regress_artifact.py | 9 +- .../steps/preprocessing/_06a1_fit_ica.py | 7 +- .../preprocessing/_06a2_find_ica_artifacts.py | 14 +- .../steps/preprocessing/_06b_run_ssp.py | 7 +- .../steps/preprocessing/_07_make_epochs.py | 5 +- .../steps/preprocessing/_08a_apply_ica.py | 15 +- .../steps/preprocessing/_08b_apply_ssp.py | 13 +- .../steps/preprocessing/_09_ptp_reject.py | 5 +- .../steps/sensor/_01_make_evoked.py | 5 +- .../steps/sensor/_02_decoding_full_epochs.py | 5 +- .../steps/sensor/_03_decoding_time_by_time.py | 5 +- .../steps/sensor/_04_time_frequency.py | 5 +- .../steps/sensor/_05_decoding_csp.py | 5 +- .../steps/sensor/_06_make_cov.py | 15 +- .../steps/sensor/_99_group_average.py | 25 ++- .../steps/source/_01_make_bem_surfaces.py | 7 +- .../steps/source/_04_make_forward.py | 9 +- .../steps/source/_05_make_inverse.py | 5 +- .../steps/source/_99_group_average.py | 11 +- mne_bids_pipeline/tests/test_run.py | 4 +- mne_bids_pipeline/typing.py | 4 +- pyproject.toml | 2 +- 41 files changed, 356 insertions(+), 381 deletions(-) diff --git a/docs/source/examples/gen_examples.py b/docs/source/examples/gen_examples.py index 2ae1a1b5a..24564754f 100755 --- a/docs/source/examples/gen_examples.py +++ b/docs/source/examples/gen_examples.py @@ -9,7 +9,6 @@ from collections import defaultdict from collections.abc import Iterable from pathlib import Path -from typing import Union from tqdm import tqdm @@ -24,7 +23,7 @@ logger = logging.getLogger() -def _bool_to_icon(x: Union[bool, Iterable]) -> str: +def _bool_to_icon(x: bool | Iterable) -> str: if x: return "✅" else: diff --git a/docs/source/settings/gen_settings.py b/docs/source/settings/gen_settings.py index 2b749fc10..8300245fe 100755 --- a/docs/source/settings/gen_settings.py +++ b/docs/source/settings/gen_settings.py @@ -99,18 +99,16 @@ # We cannot use ast for this because it doesn't preserve comments. We could use # something like redbaron, but our code is hopefully simple enough! assign_re = re.compile( - # Line starts with annotation syntax (name captured by the first group). - r"^(\w+): " - # Then the annotation can be ... - "(" - # ... a standard assignment ... - ".+ = .+" - # ... or ... - "|" - # ... the start of a multiline type annotation like "a: Union[" - r"(Union|Optional|Literal)\[" - # To the end of the line. - ")$", + "^" # The line starts, then is followed by + r"(\w+): " # annotation syntax (name captured by the first group), + "(?:" # then the rest of the line can be (in a non-capturing group): + ".+ = .+" # 1. a standard assignment + "|" # 2. or + r"Literal\[" # 3. the start of a multiline type annotation like "a: Literal[" + "|" # 4. or + r"\(" # 5. the start of a multiline 3.9+ type annotation like "a: (" + ")" # Then the end of our group + "$", # and immediately the end of the line. re.MULTILINE, ) @@ -186,8 +184,7 @@ def main(): match = assign_re.match(line) if match is not None: have_params = True - name, typ, desc = match.groups() - current_lines.append(f"{prefix}{name}") + current_lines.append(f"{prefix}{match.groups()[0]}") continue diff --git a/docs/source/v1.9.md.inc b/docs/source/v1.9.md.inc index 67c724e07..0ff3240cc 100644 --- a/docs/source/v1.9.md.inc +++ b/docs/source/v1.9.md.inc @@ -2,7 +2,9 @@ ### :new: New features & enhancements -- Added number of subject to sub-average report (#902 by @SophieHerbst) +- Added number of subject to `sub-average` report (#902 by @SophieHerbst) +- The type annotations in the default configuration file are now easier to read: We + replaced `Union[X, Y]` with `X | Y` and `Optional[X]` with `X | None`. (#908 by @hoechenberger) [//]: # (- Whatever (#000 by @whoever)) @@ -10,7 +12,9 @@ [//]: # (- Whatever (#000 by @whoever)) -[//]: # (### :package: Requirements) +### :package: Requirements + +- We dropped support for Python 3.9. You now need Python 3.10 or newer. [//]: # (- Whatever (#000 by @whoever)) diff --git a/mne_bids_pipeline/_config.py b/mne_bids_pipeline/_config.py index 0df4096e1..175b96cd2 100644 --- a/mne_bids_pipeline/_config.py +++ b/mne_bids_pipeline/_config.py @@ -1,7 +1,7 @@ # Default settings for data processing and analysis. -from collections.abc import Sequence -from typing import Annotated, Any, Callable, Literal, Optional, Union +from collections.abc import Callable, Sequence +from typing import Annotated, Any, Literal from annotated_types import Ge, Interval, Len, MinLen from mne import Covariance @@ -17,7 +17,7 @@ # %% # # General settings -bids_root: Optional[PathLike] = None +bids_root: PathLike | None = None """ Specify the BIDS root directory. Pass an empty string or ```None` to use the value specified in the `BIDS_ROOT` environment variable instead. @@ -30,7 +30,7 @@ ``` """ -deriv_root: Optional[PathLike] = None +deriv_root: PathLike | None = None """ The root of the derivatives directory in which the pipeline will store the processing results. If `None`, this will be @@ -41,7 +41,7 @@ set [`subjects_dir`][mne_bids_pipeline._config.subjects_dir] as well. """ -subjects_dir: Optional[PathLike] = None +subjects_dir: PathLike | None = None """ Path to the directory that contains the FreeSurfer reconstructions of all subjects. Specifically, this defines the `SUBJECTS_DIR` that is used by @@ -73,7 +73,7 @@ Enabling interactive mode deactivates parallel processing. """ -sessions: Union[list, Literal["all"]] = "all" +sessions: list | Literal["all"] = "all" """ The sessions to process. If `'all'`, will process all sessions found in the BIDS dataset. @@ -89,13 +89,13 @@ Whether the task should be treated as resting-state data. """ -runs: Union[Sequence, Literal["all"]] = "all" +runs: Sequence | Literal["all"] = "all" """ The runs to process. If `'all'`, will process all runs found in the BIDS dataset. """ -exclude_runs: Optional[dict[str, list[str]]] = None +exclude_runs: dict[str, list[str]] | None = None """ Specify runs to exclude from analysis, for each participant individually. @@ -111,34 +111,34 @@ did not understand the instructions, etc.). """ -crop_runs: Optional[tuple[float, float]] = None +crop_runs: tuple[float, float] | None = None """ Crop the raw data of each run to the specified time interval `[tmin, tmax]`, in seconds. The runs will be cropped before Maxwell or frequency filtering is applied. If `None`, do not crop the data. """ -acq: Optional[str] = None +acq: str | None = None """ The BIDS `acquisition` entity. """ -proc: Optional[str] = None +proc: str | None = None """ The BIDS `processing` entity. """ -rec: Optional[str] = None +rec: str | None = None """ The BIDS `recording` entity. """ -space: Optional[str] = None +space: str | None = None """ The BIDS `space` entity. """ -subjects: Union[Sequence[str], Literal["all"]] = "all" +subjects: Sequence[str] | Literal["all"] = "all" """ Subjects to analyze. If `'all'`, include all subjects. To only include a subset of subjects, pass a list of their identifiers. Even @@ -205,7 +205,7 @@ ``` """ -data_type: Optional[Literal["meg", "eeg"]] = None +data_type: Literal["meg", "eeg"] | None = None """ The BIDS data type. @@ -239,7 +239,7 @@ ``` """ -eog_channels: Optional[Sequence[str]] = None +eog_channels: Sequence[str] | None = None """ Specify EOG channels to use, or create virtual EOG channels. @@ -274,7 +274,7 @@ ``` """ -eeg_bipolar_channels: Optional[dict[str, tuple[str, str]]] = None +eeg_bipolar_channels: dict[str, tuple[str, str]] | None = None """ Combine two channels into a bipolar channel, whose signal is the **difference** between the two combined channels, and add it to the data. @@ -307,7 +307,7 @@ ``` """ -eeg_reference: Union[Literal["average"], str, Sequence["str"]] = "average" +eeg_reference: Literal["average"] | str | Sequence["str"] = "average" """ The EEG reference to use. If `average`, will use the average reference, i.e. the average across all channels. If a string, must be the name of a single @@ -330,7 +330,7 @@ ``` """ -eeg_template_montage: Optional[Union[str, DigMontageType]] = None +eeg_template_montage: str | DigMontageType | None = None """ In situations where you wish to process EEG data and no individual digitization points (measured channel locations) are available, you can apply @@ -371,9 +371,9 @@ ``` """ -analyze_channels: Union[ - Literal["all", "ch_types"], Annotated[Sequence["str"], MinLen(1)] -] = "ch_types" +analyze_channels: Literal["all", "ch_types"] | Annotated[Sequence["str"], MinLen(1)] = ( + "ch_types" +) """ The names of the channels to analyze during ERP/ERF and time-frequency analysis steps. For certain paradigms, e.g. EEG ERP research, it is common to constrain @@ -404,7 +404,7 @@ ``` """ -read_raw_bids_verbose: Optional[Literal["error"]] = None +read_raw_bids_verbose: Literal["error"] | None = None """ Verbosity level to pass to `read_raw_bids(..., verbose=read_raw_bids_verbose)`. If you know your dataset will contain files that are not perfectly BIDS @@ -412,7 +412,7 @@ `'error'` to suppress warnings emitted by read_raw_bids. """ -plot_psd_for_runs: Union[Literal["all"], Sequence[str]] = "all" +plot_psd_for_runs: Literal["all"] | Sequence[str] = "all" """ For which runs to add a power spectral density (PSD) plot to the generated report. This can take a considerable amount of time if you have many long @@ -420,7 +420,7 @@ plotting. """ -random_state: Optional[int] = 42 +random_state: int | None = 42 """ You can specify the seed of the random number generator (RNG). This setting is passed to the ICA algorithm and to the decoding function, @@ -574,7 +574,7 @@ before applying Maxwell filter. """ -mf_st_duration: Optional[float] = None +mf_st_duration: float | None = None """ There are two kinds of Maxwell filtering: SSS (signal space separation) and tSSS (temporal signal space separation) @@ -615,7 +615,7 @@ ``` """ -mf_head_origin: Union[Literal["auto"], FloatArrayLike] = "auto" +mf_head_origin: Literal["auto"] | FloatArrayLike = "auto" """ `mf_head_origin` : array-like, shape (3,) | 'auto' Origin of internal and external multipolar moment space in meters. @@ -630,7 +630,7 @@ ``` """ -mf_destination: Union[Literal["reference_run"], FloatArrayLike] = "reference_run" +mf_destination: Literal["reference_run"] | FloatArrayLike = "reference_run" """ Despite all possible care to avoid movements in the MEG, the participant will likely slowly drift down from the Dewar or slightly shift the head @@ -661,7 +661,7 @@ is expected. """ -mf_reference_run: Optional[str] = None +mf_reference_run: str | None = None """ Which run to take as the reference for adjusting the head position of all runs when [`mf_destination="reference_run"`][mne_bids_pipeline._config.mf_destination]. @@ -673,7 +673,7 @@ ``` """ -mf_cal_fname: Optional[str] = None +mf_cal_fname: str | None = None """ !!! warning This parameter should only be used for BIDS datasets that don't store @@ -687,7 +687,7 @@ ``` """ # noqa : E501 -mf_ctc_fname: Optional[str] = None +mf_ctc_fname: str | None = None """ Path to the Maxwell Filter cross-talk file. If `None`, the recommended location is used. @@ -707,7 +707,7 @@ Number of extended SSS (eSSS) basis projectors to use from empty-room data. """ -mf_esss_reject: Optional[dict[str, float]] = None +mf_esss_reject: dict[str, float] | None = None """ Rejection parameters to use when computing the extended SSS (eSSS) basis. """ @@ -722,7 +722,7 @@ Minimum time step to use during cHPI coil amplitude estimation. """ -mf_mc_t_window: Union[float, Literal["auto"]] = "auto" +mf_mc_t_window: float | Literal["auto"] = "auto" """ The window to use during cHPI coil amplitude estimation and in cHPI filtering. Can be "auto" to autodetect a reasonable value or a float (in seconds). @@ -738,19 +738,19 @@ Minimum distance (m) to accept for cHPI position fitting. """ -mf_mc_rotation_velocity_limit: Optional[float] = None +mf_mc_rotation_velocity_limit: float | None = None """ The rotation velocity limit (degrees/second) to use when annotating movement-compensated data. If `None`, no annotations will be added. """ -mf_mc_translation_velocity_limit: Optional[float] = None +mf_mc_translation_velocity_limit: float | None = None """ The translation velocity limit (meters/second) to use when annotating movement-compensated data. If `None`, no annotations will be added. """ -mf_filter_chpi: Optional[bool] = None +mf_filter_chpi: bool | None = None """ Use mne.chpi.filter_chpi after Maxwell filtering. Can be None to use the same value as [`mf_mc`][mne_bids_pipeline._config.mf_mc]. @@ -781,33 +781,33 @@ # If you need more fancy analysis, you are already likely past this kind # of tips! 😇 -l_freq: Optional[float] = None +l_freq: float | None = None """ The low-frequency cut-off in the highpass filtering step. Keep it `None` if no highpass filtering should be applied. """ -h_freq: Optional[float] = 40.0 +h_freq: float | None = 40.0 """ The high-frequency cut-off in the lowpass filtering step. Keep it `None` if no lowpass filtering should be applied. """ -l_trans_bandwidth: Union[float, Literal["auto"]] = "auto" +l_trans_bandwidth: float | Literal["auto"] = "auto" """ Specifies the transition bandwidth of the highpass filter. By default it's `'auto'` and uses default MNE parameters. """ -h_trans_bandwidth: Union[float, Literal["auto"]] = "auto" +h_trans_bandwidth: float | Literal["auto"] = "auto" """ Specifies the transition bandwidth of the lowpass filter. By default it's `'auto'` and uses default MNE parameters. """ -notch_freq: Optional[Union[float, Sequence[float]]] = None +notch_freq: float | Sequence[float] | None = None """ Notch filter frequency. More than one frequency can be supplied, e.g. to remove harmonics. Keep it `None` if no notch filter should be applied. @@ -831,7 +831,7 @@ Specifies the transition bandwidth of the notch filter. The default is `1.`. """ -notch_widths: Optional[Union[float, Sequence[float]]] = None +notch_widths: float | Sequence[float] | None = None """ Specifies the width of each stop band. `None` uses the MNE default. """ @@ -845,7 +845,7 @@ # resample your data down to 500 Hz without preventing reliable time-frequency # exploration of your data. -raw_resample_sfreq: Optional[float] = None +raw_resample_sfreq: float | None = None """ Specifies at which sampling frequency the data should be resampled. If `None`, then no resampling will be done. @@ -916,7 +916,7 @@ April 1st, 2021. """ -epochs_metadata_tmin: Optional[float] = None +epochs_metadata_tmin: float | None = None """ The beginning of the time window for metadata generation, in seconds, relative to the time-locked event of the respective epoch. This may be less @@ -924,13 +924,13 @@ time point of the epoch. """ -epochs_metadata_tmax: Optional[float] = None +epochs_metadata_tmax: float | None = None """ Same as `epochs_metadata_tmin`, but specifying the **end** of the time window for metadata generation. """ -epochs_metadata_keep_first: Optional[Sequence[str]] = None +epochs_metadata_keep_first: Sequence[str] | None = None """ Event groupings using hierarchical event descriptors (HEDs) for which to store the time of the **first** occurrence of any event of this group in a new column @@ -958,14 +958,14 @@ and `first_stimulus`. """ -epochs_metadata_keep_last: Optional[Sequence[str]] = None +epochs_metadata_keep_last: Sequence[str] | None = None """ Same as `epochs_metadata_keep_first`, but for keeping the **last** occurrence of matching event types. The columns indicating the event types will be named with a `last_` instead of a `first_` prefix. """ -epochs_metadata_query: Optional[str] = None +epochs_metadata_query: str | None = None """ A [metadata query][https://mne.tools/stable/auto_tutorials/epochs/30_epochs_metadata.html] specifying which epochs to keep. If the query fails because it refers to an @@ -978,7 +978,7 @@ ``` """ # noqa: E501 -conditions: Optional[Union[Sequence[str], dict[str, str]]] = None +conditions: Sequence[str] | dict[str, str] | None = None """ The time-locked events based on which to create evoked responses. This can either be name of the experimental condition as specified in the @@ -1030,18 +1030,18 @@ ``` """ -rest_epochs_duration: Optional[float] = None +rest_epochs_duration: float | None = None """ Duration of epochs in seconds. """ -rest_epochs_overlap: Optional[float] = None +rest_epochs_overlap: float | None = None """ Overlap between epochs in seconds. This is used if the task is `'rest'` and when the annotations do not contain any stimulation or behavior events. """ -baseline: Optional[tuple[Optional[float], Optional[float]]] = (None, 0) +baseline: tuple[float | None, float | None] | None = (None, 0) """ Specifies which time interval to use for baseline correction of epochs; if `None`, no baseline correction is applied. @@ -1093,7 +1093,7 @@ # ### SSP, ICA, and artifact regression -regress_artifact: Optional[dict[str, Any]] = None +regress_artifact: dict[str, Any] | None = None """ Keyword arguments to pass to the `mne.preprocessing.EOGRegression` model used in `mne.preprocessing.regress_artifact`. If `None`, no time-domain regression will @@ -1111,7 +1111,7 @@ ``` """ # noqa: E501 -spatial_filter: Optional[Literal["ssp", "ica"]] = None +spatial_filter: Literal["ssp", "ica"] | None = None """ Whether to use a spatial filter to detect and remove artifacts. The BIDS Pipeline offers the use of signal-space projection (SSP) and independent @@ -1171,7 +1171,7 @@ `'separate'` otherwise. """ -ssp_reject_ecg: Optional[Union[dict[str, float], Literal["autoreject_global"]]] = None +ssp_reject_ecg: dict[str, float] | Literal["autoreject_global"] | None = None """ Peak-to-peak amplitude limits of the ECG epochs to exclude from SSP fitting. This allows you to remove strong transient artifacts, which could negatively @@ -1189,7 +1189,7 @@ ``` """ -ssp_reject_eog: Optional[Union[dict[str, float], Literal["autoreject_global"]]] = None +ssp_reject_eog: dict[str, float] | Literal["autoreject_global"] | None = None """ Peak-to-peak amplitude limits of the EOG epochs to exclude from SSP fitting. This allows you to remove strong transient artifacts, which could negatively @@ -1207,13 +1207,13 @@ ``` """ -ssp_ecg_channel: Optional[str] = None +ssp_ecg_channel: str | None = None """ Channel to use for ECG SSP. Can be useful when the autodetected ECG channel is not reliable. """ -ica_reject: Optional[Union[dict[str, float], Literal["autoreject_local"]]] = None +ica_reject: dict[str, float] | Literal["autoreject_local"] | None = None """ Peak-to-peak amplitude limits to exclude epochs from ICA fitting. This allows you to remove strong transient artifacts from the epochs used for fitting ICA, which could @@ -1271,7 +1271,7 @@ algorithm (but may converge in less time). """ -ica_l_freq: Optional[float] = 1.0 +ica_l_freq: float | None = 1.0 """ The cutoff frequency of the high-pass filter to apply before running ICA. Using a relatively high cutoff like 1 Hz will remove slow drifts from the @@ -1305,7 +1305,7 @@ limit may be too low to achieve convergence. """ -ica_n_components: Optional[Union[float, int]] = None +ica_n_components: float | int | None = None """ MNE conducts ICA as a sort of a two-step procedure: First, a PCA is run on the data (trying to exclude zero-valued components in rank-deficient @@ -1330,7 +1330,7 @@ This setting may drastically alter the time required to compute ICA. """ -ica_decim: Optional[int] = None +ica_decim: int | None = None """ The decimation parameter to compute ICA. If 5 it means that 1 every 5 sample is used by ICA solver. The higher the faster @@ -1358,9 +1358,9 @@ # You can do a quick average of blink data and check what the amplitude looks # like. -reject: Optional[ - Union[dict[str, float], Literal["autoreject_global", "autoreject_local"]] -] = None +reject: dict[str, float] | Literal["autoreject_global", "autoreject_local"] | None = ( + None +) """ Peak-to-peak amplitude limits to mark epochs as bad. This allows you to remove epochs with strong transient artifacts. @@ -1397,7 +1397,7 @@ ``` """ # noqa: E501 -reject_tmin: Optional[float] = None +reject_tmin: float | None = None """ Start of the time window used to reject epochs. If `None`, the window will start with the first time point. Has no effect if @@ -1409,7 +1409,7 @@ ``` """ -reject_tmax: Optional[float] = None +reject_tmax: float | None = None """ End of the time window used to reject epochs. If `None`, the window will end with the last time point. Has no effect if @@ -1443,7 +1443,7 @@ # ## Condition contrasts -contrasts: Sequence[Union[tuple[str, str], ArbitraryContrast]] = [] +contrasts: Sequence[tuple[str, str] | ArbitraryContrast] = [] """ The conditions to contrast via a subtraction of ERPs / ERFs. The list elements can either be tuples or dictionaries (or a mix of both). Each element in the @@ -1539,14 +1539,14 @@ PTP-based rejection or Autoreject (epochs with the filename `*proc-clean_epo.fif`). """ -decoding_epochs_tmin: Optional[float] = 0.0 +decoding_epochs_tmin: float | None = 0.0 """ The first time sample to use for full epochs decoding. By default it starts at 0. If `None`,, it starts at the beginning of the epoch. Does not affect time-by-time decoding. """ -decoding_epochs_tmax: Optional[float] = None +decoding_epochs_tmax: float | None = None """ The last time sample to use for full epochs decoding. By default it is set to None so it ends at the end of the epoch. @@ -1605,7 +1605,7 @@ time and frequency. """ -decoding_csp_times: Optional[FloatArrayLike] = None +decoding_csp_times: FloatArrayLike | None = None """ The edges of the time bins to use for CSP decoding. Must contain at least two elements. By default, 5 equally-spaced bins are @@ -1625,7 +1625,7 @@ ``` """ -decoding_csp_freqs: Optional[dict[str, FloatArrayLike]] = None +decoding_csp_freqs: dict[str, FloatArrayLike] | None = None """ The edges of the frequency bins to use for CSP decoding. @@ -1675,7 +1675,7 @@ confidence interval of the mean decoding scores. """ -cluster_forming_t_threshold: Optional[float] = None +cluster_forming_t_threshold: float | None = None """ The t-value threshold to use for forming clusters in the cluster-based permutation test run on the the time-by-time decoding scores. @@ -1717,7 +1717,7 @@ ``` """ -time_frequency_freq_min: Optional[float] = 8 +time_frequency_freq_min: float | None = 8 """ Minimum frequency for the time frequency analysis, in Hz. ???+ example "Example" @@ -1726,7 +1726,7 @@ ``` """ -time_frequency_freq_max: Optional[float] = 40 +time_frequency_freq_max: float | None = 40 """ Maximum frequency for the time frequency analysis, in Hz. ???+ example "Example" @@ -1735,7 +1735,7 @@ ``` """ -time_frequency_cycles: Optional[Union[float, FloatArrayLike]] = None +time_frequency_cycles: float | FloatArrayLike | None = None """ The number of cycles to use in the Morlet wavelet. This can be a single number or one per frequency, where frequencies are calculated via @@ -1754,7 +1754,7 @@ This also applies to CSP analysis. """ -time_frequency_baseline: Optional[tuple[float, float]] = None +time_frequency_baseline: tuple[float, float] | None = None """ Baseline period to use for the time-frequency analysis. If `None`, no baseline. ???+ example "Example" @@ -1773,7 +1773,7 @@ ``` """ -time_frequency_crop: Optional[dict] = None +time_frequency_crop: dict | None = None """ Period and frequency range to crop the time-frequency analysis to. If `None`, no cropping. @@ -1811,7 +1811,7 @@ # ## BEM surface -use_template_mri: Optional[str] = None +use_template_mri: str | None = None """ Whether to use a template MRI subject such as FreeSurfer's `fsaverage` subject. This may come in handy if you don't have individual MR scans of your @@ -1885,7 +1885,7 @@ # ## Source space & forward solution -mri_t1_path_generator: Optional[Callable[[BIDSPath], BIDSPath]] = None +mri_t1_path_generator: Callable[[BIDSPath], BIDSPath] | None = None """ To perform source-level analyses, the Pipeline needs to generate a transformation matrix that translates coordinates from MEG and EEG sensor @@ -1945,7 +1945,7 @@ def get_t1_from_meeg(bids_path): ``` """ -mri_landmarks_kind: Optional[Callable[[BIDSPath], str]] = None +mri_landmarks_kind: Callable[[BIDSPath], str] | None = None """ This config option allows to look for specific landmarks in the json sidecar file of the T1 MRI file. This can be useful when we have different @@ -1962,7 +1962,7 @@ def mri_landmarks_kind(bids_path): ``` """ -spacing: Union[Literal["oct5", "oct6", "ico4", "ico5", "all"], int] = "oct6" +spacing: Literal["oct5", "oct6", "ico4", "ico5", "all"] | int = "oct6" """ The spacing to use. Can be `'ico#'` for a recursively subdivided icosahedron, `'oct#'` for a recursively subdivided octahedron, @@ -1979,7 +1979,7 @@ def mri_landmarks_kind(bids_path): # ## Inverse solution -loose: Union[float, Literal["auto"]] = 0.2 +loose: float | Literal["auto"] = 0.2 """ Value that weights the source variances of the dipole components that are parallel (tangential) to the cortical surface. If `0`, then the @@ -1990,7 +1990,7 @@ def mri_landmarks_kind(bids_path): unless `fixed is True` in which case the value 0. is used. """ -depth: Optional[Union[float, dict]] = 0.8 +depth: float | dict | None = 0.8 """ If float (default 0.8), it acts as the depth weighting exponent (`exp`) to use (must be between 0 and 1). None is equivalent to 0, meaning no @@ -2005,11 +2005,11 @@ def mri_landmarks_kind(bids_path): solution. """ -noise_cov: Union[ - tuple[Optional[float], Optional[float]], - Literal["emptyroom", "rest", "ad-hoc"], - Callable[[BIDSPath], Covariance], -] = (None, 0) +noise_cov: ( + tuple[float | None, float | None] + | Literal["emptyroom", "rest", "ad-hoc"] + | Callable[[BIDSPath], Covariance] +) = (None, 0) """ Specify how to estimate the noise covariance matrix, which is used in inverse modeling. @@ -2089,7 +2089,7 @@ def noise_cov(bids_path): of `mne.compute_covariance` for details. """ -source_info_path_update: Optional[dict[str, str]] = dict(suffix="ave") +source_info_path_update: dict[str, str] | None = dict(suffix="ave") """ When computing the forward and inverse solutions, by default the pipeline retrieves the `mne.Info` object from the cleaned evoked data. However, in @@ -2131,7 +2131,7 @@ def noise_cov(bids_path): # ## Report generation -report_evoked_n_time_points: Optional[int] = None +report_evoked_n_time_points: int | None = None """ Specifies the number of time points to display for each evoked in the report. If `None`, it defaults to the current default in MNE-Python. @@ -2143,7 +2143,7 @@ def noise_cov(bids_path): ``` """ -report_stc_n_time_points: Optional[int] = None +report_stc_n_time_points: int | None = None """ Specifies the number of time points to display for each source estimates in the report. If `None`, it defaults to the current default in MNE-Python. @@ -2155,7 +2155,7 @@ def noise_cov(bids_path): ``` """ -report_add_epochs_image_kwargs: Optional[dict] = None +report_add_epochs_image_kwargs: dict | None = None """ Specifies the limits for the color scales of the epochs_image in the report. If `None`, it defaults to the current default in MNE-Python. @@ -2197,7 +2197,7 @@ def noise_cov(bids_path): Ignored if `parallel_backend` is not `'dask'`. """ -dask_temp_dir: Optional[PathLike] = None +dask_temp_dir: PathLike | None = None """ The temporary directory to use by Dask. Dask places lock-files in this directory, and also uses it to "spill" RAM contents to disk if the amount of @@ -2235,7 +2235,7 @@ def noise_cov(bids_path): Enabling debug mode deactivates parallel processing. """ -memory_location: Optional[Union[PathLike, bool]] = True +memory_location: PathLike | bool | None = True """ If not None (or False), caching will be enabled and the cache files will be stored in the given directory. The default (True) will use a diff --git a/mne_bids_pipeline/_config_import.py b/mne_bids_pipeline/_config_import.py index 98286d4ba..a52c82119 100644 --- a/mne_bids_pipeline/_config_import.py +++ b/mne_bids_pipeline/_config_import.py @@ -7,7 +7,6 @@ from dataclasses import field from functools import partial from types import SimpleNamespace -from typing import Optional import matplotlib import mne @@ -20,8 +19,8 @@ def _import_config( *, - config_path: Optional[PathLike], - overrides: Optional[SimpleNamespace] = None, + config_path: PathLike | None, + overrides: SimpleNamespace | None = None, check: bool = True, log: bool = True, ) -> SimpleNamespace: @@ -118,7 +117,7 @@ def _get_default_config(): ignore_keys = { name.asname or name.name for element in tree.body - if isinstance(element, (ast.Import, ast.ImportFrom)) + if isinstance(element, ast.Import | ast.ImportFrom) for name in element.names } config = SimpleNamespace( @@ -160,8 +159,8 @@ def _update_config_from_path( def _update_with_user_config( *, config: SimpleNamespace, # modified in-place - config_path: Optional[PathLike], - overrides: Optional[SimpleNamespace], + config_path: PathLike | None, + overrides: SimpleNamespace | None, log: bool = False, ) -> list[str]: # 1. Basics and hidden vars @@ -233,7 +232,7 @@ def _update_with_user_config( return user_names -def _check_config(config: SimpleNamespace, config_path: Optional[PathLike]) -> None: +def _check_config(config: SimpleNamespace, config_path: PathLike | None) -> None: _pydantic_validate(config=config, config_path=config_path) # Eventually all of these could be pydantic-validated, but for now we'll @@ -368,7 +367,7 @@ def _default_factory(key, val): def _pydantic_validate( config: SimpleNamespace, - config_path: Optional[PathLike], + config_path: PathLike | None, ): """Create dataclass from config type hints and validate with pydantic.""" # https://docs.pydantic.dev/latest/usage/dataclasses/ diff --git a/mne_bids_pipeline/_config_utils.py b/mne_bids_pipeline/_config_utils.py index d6bcb0ce5..46990a623 100644 --- a/mne_bids_pipeline/_config_utils.py +++ b/mne_bids_pipeline/_config_utils.py @@ -5,7 +5,7 @@ import pathlib from collections.abc import Iterable from types import ModuleType, SimpleNamespace -from typing import Any, Literal, Optional, TypeVar, Union +from typing import Any, Literal, TypeVar import mne import mne_bids @@ -106,7 +106,7 @@ def get_subjects(config: SimpleNamespace) -> list[str]: return subjects -def get_sessions(config: SimpleNamespace) -> Union[list[None], list[str]]: +def get_sessions(config: SimpleNamespace) -> list[None] | list[str]: sessions = copy.deepcopy(config.sessions) _all_sessions = _get_entity_vals_cached( root=config.bids_root, @@ -124,7 +124,7 @@ def get_sessions(config: SimpleNamespace) -> Union[list[None], list[str]]: def get_runs_all_subjects( config: SimpleNamespace, -) -> dict[str, Union[list[None], list[str]]]: +) -> dict[str, list[None] | list[str]]: """Give the mapping between subjects and their runs. Returns @@ -149,7 +149,7 @@ def get_runs_all_subjects( @functools.cache def _get_runs_all_subjects_cached( **config_dict: dict[str, Any], -) -> dict[str, Union[list[None], list[str]]]: +) -> dict[str, list[None] | list[str]]: config = SimpleNamespace(**config_dict) # Sometimes we check list equivalence for ch_types, so convert it back config.ch_types = list(config.ch_types) @@ -197,7 +197,7 @@ def get_runs( config: SimpleNamespace, subject: str, verbose: bool = False, -) -> Union[list[str], list[None]]: +) -> list[str] | list[None]: """Return a list of runs in the BIDS input data. Parameters @@ -253,7 +253,7 @@ def get_runs_tasks( *, config: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, which: tuple[str] = ("runs", "noise", "rest"), ) -> list[tuple[str]]: """Get (run, task) tuples for all runs plus (maybe) rest.""" @@ -310,7 +310,7 @@ def get_mf_reference_run(config: SimpleNamespace) -> str: ) -def get_task(config: SimpleNamespace) -> Optional[str]: +def get_task(config: SimpleNamespace) -> str | None: task = config.task if task: return task @@ -416,7 +416,7 @@ def get_mf_ctc_fname( RawEpochsEvokedT = TypeVar( - "RawEpochsEvokedT", bound=Union[mne.io.BaseRaw, mne.BaseEpochs, mne.Evoked] + "RawEpochsEvokedT", bound=mne.io.BaseRaw | mne.BaseEpochs | mne.Evoked ) @@ -459,7 +459,7 @@ def _meg_in_ch_types(ch_types: str) -> bool: def get_noise_cov_bids_path( - cfg: SimpleNamespace, subject: str, session: Optional[str] + cfg: SimpleNamespace, subject: str, session: str | None ) -> BIDSPath: """Retrieve the path to the noise covariance file. @@ -553,13 +553,13 @@ def get_decoding_contrasts(config: SimpleNamespace) -> Iterable[tuple[str, str]] } -def _get_decoding_proc(config: SimpleNamespace) -> Optional[str]: +def _get_decoding_proc(config: SimpleNamespace) -> str | None: return _EPOCHS_DESCRIPTION_TO_PROC_MAP[config.decoding_which_epochs] def get_eeg_reference( config: SimpleNamespace, -) -> Union[Literal["average"], Iterable[str]]: +) -> Literal["average"] | Iterable[str]: if config.eeg_reference == "average": return config.eeg_reference elif isinstance(config.eeg_reference, str): @@ -635,7 +635,7 @@ def _do_mf_autobad(*, cfg: SimpleNamespace) -> bool: # Adapted from MNE-Python def _pl(x, *, non_pl="", pl="s"): """Determine if plural should be used.""" - len_x = x if isinstance(x, (int, np.generic)) else len(x) + len_x = x if isinstance(x, int | np.generic) else len(x) return non_pl if len_x == 1 else pl @@ -643,7 +643,7 @@ def _proj_path( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> BIDSPath: return BIDSPath( subject=subject, diff --git a/mne_bids_pipeline/_decoding.py b/mne_bids_pipeline/_decoding.py index 3968fcf3c..9c34ded27 100644 --- a/mne_bids_pipeline/_decoding.py +++ b/mne_bids_pipeline/_decoding.py @@ -1,5 +1,3 @@ -from typing import Optional - import mne import numpy as np from joblib import parallel_backend @@ -90,7 +88,7 @@ def _handle_csp_args( def _decoding_preproc_steps( subject: str, - session: Optional[str], + session: str | None, epochs: mne.Epochs, pca: bool = True, ) -> list[BaseEstimator]: diff --git a/mne_bids_pipeline/_import_data.py b/mne_bids_pipeline/_import_data.py index aaf7b56e3..1043db0d5 100644 --- a/mne_bids_pipeline/_import_data.py +++ b/mne_bids_pipeline/_import_data.py @@ -1,6 +1,6 @@ from collections.abc import Iterable from types import SimpleNamespace -from typing import Literal, Optional, Union +from typing import Literal import mne import numpy as np @@ -26,17 +26,17 @@ def make_epochs( *, task: str, subject: str, - session: Optional[str], + session: str | None, raw: mne.io.BaseRaw, - event_id: Optional[Union[dict[str, int], Literal["auto"]]], - conditions: Union[Iterable[str], dict[str, str]], + event_id: dict[str, int] | Literal["auto"] | None, + conditions: Iterable[str] | dict[str, str], tmin: float, tmax: float, - metadata_tmin: Optional[float], - metadata_tmax: Optional[float], - metadata_keep_first: Optional[Iterable[str]], - metadata_keep_last: Optional[Iterable[str]], - metadata_query: Optional[str], + metadata_tmin: float | None, + metadata_tmax: float | None, + metadata_keep_first: Iterable[str] | None, + metadata_keep_last: Iterable[str] | None, + metadata_query: str | None, event_repeated: Literal["error", "drop", "merge"], epochs_decim: int, task_is_rest: bool, @@ -173,8 +173,8 @@ def _rename_events_func( cfg: SimpleNamespace, raw: mne.io.BaseRaw, subject: str, - session: Optional[str], - run: Optional[str], + session: str | None, + run: str | None, ) -> None: """Rename events (actually, annotations descriptions) in ``raw``. @@ -256,7 +256,7 @@ def _drop_channels_func( cfg: SimpleNamespace, raw: mne.io.BaseRaw, subject: str, - session: Optional[str], + session: str | None, ) -> None: """Drop channels from the data. @@ -272,8 +272,8 @@ def _create_bipolar_channels( cfg: SimpleNamespace, raw: mne.io.BaseRaw, subject: str, - session: Optional[str], - run: Optional[str], + session: str | None, + run: str | None, ) -> None: """Create a channel from a bipolar referencing scheme.. @@ -318,8 +318,8 @@ def _set_eeg_montage( cfg: SimpleNamespace, raw: mne.io.BaseRaw, subject: str, - session: Optional[str], - run: Optional[str], + session: str | None, + run: str | None, ) -> None: """Set an EEG template montage if requested. @@ -356,8 +356,8 @@ def import_experimental_data( *, cfg: SimpleNamespace, bids_path_in: BIDSPath, - bids_path_bads_in: Optional[BIDSPath], - data_is_rest: Optional[bool], + bids_path_bads_in: BIDSPath | None, + data_is_rest: bool | None, ) -> mne.io.BaseRaw: """Run the data import. @@ -417,9 +417,9 @@ def import_er_data( *, cfg: SimpleNamespace, bids_path_er_in: BIDSPath, - bids_path_ref_in: Optional[BIDSPath], - bids_path_er_bads_in: Optional[BIDSPath], - bids_path_ref_bads_in: Optional[BIDSPath], + bids_path_ref_in: BIDSPath | None, + bids_path_er_bads_in: BIDSPath | None, + bids_path_ref_bads_in: BIDSPath | None, prepare_maxwell_filter: bool, ) -> mne.io.BaseRaw: """Import empty-room data. @@ -495,8 +495,8 @@ def _find_breaks_func( cfg, raw: mne.io.BaseRaw, subject: str, - session: Optional[str], - run: Optional[str], + session: str | None, + run: str | None, ) -> None: if not cfg.find_breaks: return @@ -527,9 +527,9 @@ def _get_bids_path_in( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, kind: Literal["orig", "sss", "filt"] = "orig", ) -> BIDSPath: # b/c can be used before this is updated @@ -563,13 +563,13 @@ def _get_run_path( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, kind: Literal["orig", "sss", "filt"], - add_bads: Optional[bool] = None, + add_bads: bool | None = None, allow_missing: bool = False, - key: Optional[str] = None, + key: str | None = None, ) -> dict: bids_path_in = _get_bids_path_in( cfg=cfg, @@ -595,9 +595,9 @@ def _get_rest_path( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, kind: Literal["orig", "sss", "filt"], - add_bads: Optional[bool] = None, + add_bads: bool | None = None, ) -> dict: if not (cfg.process_rest and not cfg.task_is_rest): return dict() @@ -617,10 +617,10 @@ def _get_noise_path( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, kind: Literal["orig", "sss", "filt"], - mf_reference_run: Optional[str], - add_bads: Optional[bool] = None, + mf_reference_run: str | None, + add_bads: bool | None = None, ) -> dict: if not (cfg.process_empty_room and get_datatype(config=cfg) == "meg"): return dict() @@ -663,12 +663,12 @@ def _get_run_rest_noise_path( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, kind: Literal["orig", "sss", "filt"], - mf_reference_run: Optional[str], - add_bads: Optional[bool] = None, + mf_reference_run: str | None, + add_bads: bool | None = None, ) -> dict: kwargs = dict( cfg=cfg, @@ -691,8 +691,8 @@ def _get_mf_reference_run_path( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], - add_bads: Optional[bool] = None, + session: str | None, + add_bads: bool | None = None, ) -> dict: return _get_run_path( cfg=cfg, @@ -716,12 +716,12 @@ def _path_dict( *, cfg: SimpleNamespace, bids_path_in: BIDSPath, - add_bads: Optional[bool] = None, + add_bads: bool | None = None, kind: Literal["orig", "sss", "filt"], allow_missing: bool, - key: Optional[str] = None, + key: str | None = None, subject: str, - session: Optional[str], + session: str | None, ) -> dict: if add_bads is None: add_bads = kind == "orig" and _do_mf_autobad(cfg=cfg) @@ -748,7 +748,7 @@ def _bads_path( cfg: SimpleNamespace, bids_path_in: BIDSPath, subject: str, - session: Optional[str], + session: str | None, ) -> BIDSPath: return bids_path_in.copy().update( suffix="bads", @@ -817,8 +817,8 @@ def _import_data_kwargs(*, config: SimpleNamespace, subject: str) -> dict: def _get_run_type( - run: Optional[str], - task: Optional[str], + run: str | None, + task: str | None, ) -> str: if run is None and task in ("noise", "rest"): run_type = dict(rest="resting-state", noise="empty-room")[task] diff --git a/mne_bids_pipeline/_logging.py b/mne_bids_pipeline/_logging.py index 2f54757a6..187930be2 100644 --- a/mne_bids_pipeline/_logging.py +++ b/mne_bids_pipeline/_logging.py @@ -4,7 +4,6 @@ import inspect import logging import os -from typing import Optional, Union import rich.console import rich.theme @@ -71,25 +70,25 @@ def level(self, level): level = int(level) self._level = level - def debug(self, msg: str, *, extra: Optional[LogKwargsT] = None) -> None: + def debug(self, msg: str, *, extra: LogKwargsT | None = None) -> None: self._log_message(kind="debug", msg=msg, **(extra or {})) - def info(self, msg: str, *, extra: Optional[LogKwargsT] = None) -> None: + def info(self, msg: str, *, extra: LogKwargsT | None = None) -> None: self._log_message(kind="info", msg=msg, **(extra or {})) - def warning(self, msg: str, *, extra: Optional[LogKwargsT] = None) -> None: + def warning(self, msg: str, *, extra: LogKwargsT | None = None) -> None: self._log_message(kind="warning", msg=msg, **(extra or {})) - def error(self, msg: str, *, extra: Optional[LogKwargsT] = None) -> None: + def error(self, msg: str, *, extra: LogKwargsT | None = None) -> None: self._log_message(kind="error", msg=msg, **(extra or {})) def _log_message( self, kind: str, msg: str, - subject: Optional[Union[str, int]] = None, - session: Optional[Union[str, int]] = None, - run: Optional[Union[str, int]] = None, + subject: str | int | None = None, + session: str | int | None = None, + run: str | int | None = None, emoji: str = "", ): this_level = getattr(logging, kind.upper()) @@ -111,10 +110,10 @@ def _log_message( def gen_log_kwargs( message: str, *, - subject: Optional[Union[str, int]] = None, - session: Optional[Union[str, int]] = None, - run: Optional[Union[str, int]] = None, - task: Optional[str] = None, + subject: str | int | None = None, + session: str | int | None = None, + run: str | int | None = None, + task: str | None = None, emoji: str = "⏳️", ) -> LogKwargsT: # Try to figure these out diff --git a/mne_bids_pipeline/_parallel.py b/mne_bids_pipeline/_parallel.py index 9c74e6474..acee195c0 100644 --- a/mne_bids_pipeline/_parallel.py +++ b/mne_bids_pipeline/_parallel.py @@ -1,7 +1,8 @@ """Parallelization.""" +from collections.abc import Callable from types import SimpleNamespace -from typing import Callable, Literal +from typing import Literal import joblib from mne.utils import logger as mne_logger diff --git a/mne_bids_pipeline/_reject.py b/mne_bids_pipeline/_reject.py index 707984732..3837daa97 100644 --- a/mne_bids_pipeline/_reject.py +++ b/mne_bids_pipeline/_reject.py @@ -1,7 +1,7 @@ """Rejection.""" from collections.abc import Iterable -from typing import Literal, Optional, Union +from typing import Literal import mne @@ -11,11 +11,11 @@ def _get_reject( *, subject: str, - session: Optional[str], - reject: Union[dict[str, float], Literal["autoreject_global"]], + session: str | None, + reject: dict[str, float] | Literal["autoreject_global"], ch_types: Iterable[Literal["meg", "mag", "grad", "eeg"]], param: str, - epochs: Optional[mne.BaseEpochs] = None, + epochs: mne.BaseEpochs | None = None, ) -> dict[str, float]: if reject is None: return dict() diff --git a/mne_bids_pipeline/_report.py b/mne_bids_pipeline/_report.py index a9f9b48a4..db14020fa 100644 --- a/mne_bids_pipeline/_report.py +++ b/mne_bids_pipeline/_report.py @@ -5,7 +5,7 @@ from io import StringIO from textwrap import indent from types import SimpleNamespace -from typing import Literal, Optional +from typing import Literal import matplotlib.transforms import mne @@ -29,10 +29,10 @@ def _open_report( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str] = None, - task: Optional[str] = None, - fname_report: Optional[BIDSPath] = None, + session: str | None, + run: str | None = None, + task: str | None = None, + fname_report: BIDSPath | None = None, name: str = "report", ): if fname_report is None: @@ -453,7 +453,7 @@ def _plot_decoding_time_generalization( def _gen_empty_report( - *, cfg: SimpleNamespace, subject: str, session: Optional[str] + *, cfg: SimpleNamespace, subject: str, session: str | None ) -> mne.Report: title = f"sub-{subject}" if session is not None: @@ -470,7 +470,7 @@ def _contrasts_to_names(contrasts: list[list[str]]) -> list[str]: def add_event_counts( - *, cfg, subject: Optional[str], session: Optional[str], report: mne.Report + *, cfg, subject: str | None, session: str | None, report: mne.Report ) -> None: try: df_events = count_events(BIDSPath(root=cfg.bids_root, session=session)) @@ -495,9 +495,9 @@ def _finalize( report: mne.Report, exec_params: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, ) -> None: """Add system information and the pipeline configuration to the report.""" # ensure they are always appended @@ -597,7 +597,7 @@ def add_csp_grand_average( cond_1: str, cond_2: str, fname_csp_freq_results: BIDSPath, - fname_csp_cluster_results: Optional[pd.DataFrame], + fname_csp_cluster_results: pd.DataFrame | None, ): """Add CSP decoding results to the grand average report.""" import matplotlib.pyplot as plt # nested import to help joblib @@ -850,8 +850,8 @@ def _add_raw( bids_path_in: BIDSPath, title: str, tags: tuple = (), - raw: Optional[BaseRaw] = None, - extra_html: Optional[str] = None, + raw: BaseRaw | None = None, + extra_html: str | None = None, ): if bids_path_in.run is not None: title += f", run {repr(bids_path_in.run)}" @@ -888,7 +888,7 @@ def _render_bem( cfg: SimpleNamespace, report: mne.report.Report, subject: str, - session: Optional[str], + session: str | None, ): logger.info(**gen_log_kwargs(message="Rendering MRI slices with BEM contours.")) report.add_bem( diff --git a/mne_bids_pipeline/_run.py b/mne_bids_pipeline/_run.py index ca3bd1faf..de1321352 100644 --- a/mne_bids_pipeline/_run.py +++ b/mne_bids_pipeline/_run.py @@ -9,8 +9,9 @@ import sys import time import traceback +from collections.abc import Callable from types import SimpleNamespace -from typing import Callable, Literal, Optional, Union +from typing import Literal import json_tricks import pandas as pd @@ -23,8 +24,8 @@ def failsafe_run( - get_input_fnames: Optional[Callable] = None, - get_output_fnames: Optional[Callable] = None, + get_input_fnames: Callable | None = None, + get_output_fnames: Callable | None = None, ) -> Callable: def failsafe_run_decorator(func): @functools.wraps(func) # Preserve "identity" of original function @@ -315,8 +316,8 @@ def save_logs(*, config: SimpleNamespace, logs: list[pd.Series]) -> None: def _update_for_splits( - files_dict: Union[dict[str, BIDSPath], BIDSPath], - key: Optional[str], + files_dict: dict[str, BIDSPath] | BIDSPath, + key: str | None, *, single: bool = False, allow_missing: bool = False, @@ -358,7 +359,7 @@ def _sanitize_callable(val): def _get_step_path( - stack: Optional[list[inspect.FrameInfo]] = None, + stack: list[inspect.FrameInfo] | None = None, ) -> pathlib.Path: if stack is None: stack = inspect.stack() @@ -385,7 +386,7 @@ def _prep_out_files( *, exec_params: SimpleNamespace, out_files: dict[str, BIDSPath], - check_relative: Optional[pathlib.Path] = None, + check_relative: pathlib.Path | None = None, bids_only: bool = True, ): if check_relative is None: @@ -415,7 +416,7 @@ def _prep_out_files( def _path_to_str_hash( k: str, - v: Union[BIDSPath, pathlib.Path], + v: BIDSPath | pathlib.Path, *, method: Literal["mtime", "hash"], kind: str = "in", diff --git a/mne_bids_pipeline/steps/init/_01_init_derivatives_dir.py b/mne_bids_pipeline/steps/init/_01_init_derivatives_dir.py index 2f17b0c77..de7a2de0d 100644 --- a/mne_bids_pipeline/steps/init/_01_init_derivatives_dir.py +++ b/mne_bids_pipeline/steps/init/_01_init_derivatives_dir.py @@ -4,7 +4,6 @@ """ from types import SimpleNamespace -from typing import Optional from mne_bids.config import BIDS_VERSION from mne_bids.utils import _write_json @@ -47,7 +46,7 @@ def init_subject_dirs( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> None: """Create processing data output directories for individual participants.""" out_dir = cfg.deriv_root / f"sub-{subject}" diff --git a/mne_bids_pipeline/steps/init/_02_find_empty_room.py b/mne_bids_pipeline/steps/init/_02_find_empty_room.py index d56318365..02515021a 100644 --- a/mne_bids_pipeline/steps/init/_02_find_empty_room.py +++ b/mne_bids_pipeline/steps/init/_02_find_empty_room.py @@ -1,7 +1,6 @@ """Find empty-room data matches.""" from types import SimpleNamespace -from typing import Optional from mne_bids import BIDSPath @@ -20,7 +19,7 @@ def get_input_fnames_find_empty_room( - *, subject: str, session: Optional[str], run: Optional[str], cfg: SimpleNamespace + *, subject: str, session: str | None, run: str | None, cfg: SimpleNamespace ) -> dict[str, BIDSPath]: """Get paths of files required by find_empty_room function.""" bids_path_in = BIDSPath( @@ -63,8 +62,8 @@ def find_empty_room( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], + session: str | None, + run: str | None, in_files: dict[str, BIDSPath], ) -> dict[str, BIDSPath]: raw_path = in_files.pop(f"raw_run-{run}") diff --git a/mne_bids_pipeline/steps/preprocessing/_01_data_quality.py b/mne_bids_pipeline/steps/preprocessing/_01_data_quality.py index 1cbeca387..0c8f81e96 100644 --- a/mne_bids_pipeline/steps/preprocessing/_01_data_quality.py +++ b/mne_bids_pipeline/steps/preprocessing/_01_data_quality.py @@ -1,7 +1,6 @@ """Assess data quality and find bad (and flat) channels.""" from types import SimpleNamespace -from typing import Optional import mne import pandas as pd @@ -36,9 +35,9 @@ def get_input_fnames_data_quality( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, ) -> dict: """Get paths of files required by assess_data_quality function.""" kwargs = dict( @@ -68,9 +67,9 @@ def assess_data_quality( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, in_files: dict, ) -> dict: """Assess data quality and find and mark bad channels.""" @@ -146,11 +145,11 @@ def _find_bads_maxwell( cfg: SimpleNamespace, exec_params: SimpleNamespace, bids_path_in: BIDSPath, - bids_path_ref_in: Optional[BIDSPath], + bids_path_ref_in: BIDSPath | None, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, out_files: dict, ): if cfg.find_flat_channels_meg and not cfg.find_noisy_channels_meg: @@ -287,7 +286,7 @@ def get_config( *, config: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> SimpleNamespace: extra_kwargs = dict() if config.find_noisy_channels_meg or config.find_flat_channels_meg: diff --git a/mne_bids_pipeline/steps/preprocessing/_02_head_pos.py b/mne_bids_pipeline/steps/preprocessing/_02_head_pos.py index d4a6a2c6b..de8996338 100644 --- a/mne_bids_pipeline/steps/preprocessing/_02_head_pos.py +++ b/mne_bids_pipeline/steps/preprocessing/_02_head_pos.py @@ -1,7 +1,6 @@ """Estimate head positions.""" from types import SimpleNamespace -from typing import Optional import mne @@ -25,9 +24,9 @@ def get_input_fnames_head_pos( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, ) -> dict: """Get paths of files required by run_head_pos function.""" return _get_run_rest_noise_path( @@ -49,9 +48,9 @@ def run_head_pos( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, in_files: dict, ) -> dict: import matplotlib.pyplot as plt @@ -148,7 +147,7 @@ def get_config( *, config: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> SimpleNamespace: cfg = SimpleNamespace( mf_mc_t_step_min=config.mf_mc_t_step_min, diff --git a/mne_bids_pipeline/steps/preprocessing/_03_maxfilter.py b/mne_bids_pipeline/steps/preprocessing/_03_maxfilter.py index 00ce5ad0a..e1e178395 100644 --- a/mne_bids_pipeline/steps/preprocessing/_03_maxfilter.py +++ b/mne_bids_pipeline/steps/preprocessing/_03_maxfilter.py @@ -17,7 +17,6 @@ import gc from copy import deepcopy from types import SimpleNamespace -from typing import Optional import mne import numpy as np @@ -50,7 +49,7 @@ def get_input_fnames_esss( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: kwargs = dict( cfg=cfg, @@ -76,7 +75,7 @@ def compute_esss_proj( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: import matplotlib.pyplot as plt @@ -182,9 +181,9 @@ def get_input_fnames_maxwell_filter( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, ) -> dict: """Get paths of files required by maxwell_filter function.""" kwargs = dict( @@ -274,9 +273,9 @@ def run_maxwell_filter( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], - run: Optional[str], - task: Optional[str], + session: str | None, + run: str | None, + task: str | None, in_files: dict, ) -> dict: if cfg.proc and "sss" in cfg.proc and cfg.use_maxwell_filter: @@ -539,7 +538,7 @@ def get_config_esss( *, config: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> SimpleNamespace: cfg = SimpleNamespace( mf_esss=config.mf_esss, @@ -553,7 +552,7 @@ def get_config_maxwell_filter( *, config: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> SimpleNamespace: cfg = SimpleNamespace( mf_cal_fname=get_mf_cal_fname( diff --git a/mne_bids_pipeline/steps/preprocessing/_04_frequency_filter.py b/mne_bids_pipeline/steps/preprocessing/_04_frequency_filter.py index fd9c6c874..330829ab8 100644 --- a/mne_bids_pipeline/steps/preprocessing/_04_frequency_filter.py +++ b/mne_bids_pipeline/steps/preprocessing/_04_frequency_filter.py @@ -16,7 +16,7 @@ from collections.abc import Iterable from types import SimpleNamespace -from typing import Literal, Optional, Union +from typing import Literal import mne import numpy as np @@ -45,9 +45,9 @@ def get_input_fnames_frequency_filter( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], + task: str | None, ) -> dict: """Get paths of files required by filter_data function.""" kind = "sss" if cfg.use_maxwell_filter else "orig" @@ -65,14 +65,14 @@ def get_input_fnames_frequency_filter( def notch_filter( raw: mne.io.BaseRaw, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], - freqs: Optional[Union[float, Iterable[float]]], - trans_bandwidth: Union[float, Literal["auto"]], - notch_widths: Optional[Union[float, Iterable[float]]], + task: str | None, + freqs: float | Iterable[float] | None, + trans_bandwidth: float | Literal["auto"], + notch_widths: float | Iterable[float] | None, run_type: Literal["experimental", "empty-room", "resting-state"], - picks: Optional[np.ndarray], + picks: np.ndarray | None, ) -> None: """Filter data channels (MEG and EEG).""" if freqs is None: @@ -97,15 +97,15 @@ def notch_filter( def bandpass_filter( raw: mne.io.BaseRaw, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], - l_freq: Optional[float], - h_freq: Optional[float], - l_trans_bandwidth: Union[float, Literal["auto"]], - h_trans_bandwidth: Union[float, Literal["auto"]], + task: str | None, + l_freq: float | None, + h_freq: float | None, + l_trans_bandwidth: float | Literal["auto"], + h_trans_bandwidth: float | Literal["auto"], run_type: Literal["experimental", "empty-room", "resting-state"], - picks: Optional[np.ndarray], + picks: np.ndarray | None, ) -> None: """Filter data channels (MEG and EEG).""" if l_freq is not None and h_freq is None: @@ -135,9 +135,9 @@ def bandpass_filter( def resample( raw: mne.io.BaseRaw, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], + task: str | None, sfreq: float, run_type: Literal["experimental", "empty-room", "resting-state"], ) -> None: @@ -157,9 +157,9 @@ def filter_data( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], + task: str | None, in_files: dict, ) -> dict: """Filter data from a single subject.""" diff --git a/mne_bids_pipeline/steps/preprocessing/_05_regress_artifact.py b/mne_bids_pipeline/steps/preprocessing/_05_regress_artifact.py index 9fce737cc..bc63acd64 100644 --- a/mne_bids_pipeline/steps/preprocessing/_05_regress_artifact.py +++ b/mne_bids_pipeline/steps/preprocessing/_05_regress_artifact.py @@ -1,7 +1,6 @@ """Temporal regression for artifact removal.""" from types import SimpleNamespace -from typing import Optional import mne from mne.io.pick import _picks_to_idx @@ -23,9 +22,9 @@ def get_input_fnames_regress_artifact( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], + task: str | None, ) -> dict: """Get paths of files required by regress_artifact function.""" out = _get_run_rest_noise_path( @@ -49,9 +48,9 @@ def run_regress_artifact( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], + task: str | None, in_files: dict, ) -> dict: model = EOGRegression(proj=False, **cfg.regress_artifact) diff --git a/mne_bids_pipeline/steps/preprocessing/_06a1_fit_ica.py b/mne_bids_pipeline/steps/preprocessing/_06a1_fit_ica.py index 79e15a235..5776f3abf 100644 --- a/mne_bids_pipeline/steps/preprocessing/_06a1_fit_ica.py +++ b/mne_bids_pipeline/steps/preprocessing/_06a1_fit_ica.py @@ -10,7 +10,6 @@ """ from types import SimpleNamespace -from typing import Optional import autoreject import mne @@ -37,7 +36,7 @@ def get_input_fnames_run_ica( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: bids_basename = BIDSPath( subject=subject, @@ -69,7 +68,7 @@ def run_ica( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: """Run ICA.""" @@ -295,7 +294,7 @@ def get_config( *, config: SimpleNamespace, subject: str, - session: Optional[str] = None, + session: str | None = None, ) -> SimpleNamespace: cfg = SimpleNamespace( conditions=config.conditions, diff --git a/mne_bids_pipeline/steps/preprocessing/_06a2_find_ica_artifacts.py b/mne_bids_pipeline/steps/preprocessing/_06a2_find_ica_artifacts.py index d85398bfc..93788eccc 100644 --- a/mne_bids_pipeline/steps/preprocessing/_06a2_find_ica_artifacts.py +++ b/mne_bids_pipeline/steps/preprocessing/_06a2_find_ica_artifacts.py @@ -9,7 +9,7 @@ import shutil from types import SimpleNamespace -from typing import Literal, Optional +from typing import Literal import mne import numpy as np @@ -34,11 +34,11 @@ def detect_bad_components( *, cfg, which: Literal["eog", "ecg"], - epochs: Optional[mne.BaseEpochs], + epochs: mne.BaseEpochs | None, ica: mne.preprocessing.ICA, - ch_names: Optional[list[str]], + ch_names: list[str] | None, subject: str, - session: Optional[str], + session: str | None, ) -> tuple[list[int], np.ndarray]: artifact = which.upper() if epochs is None: @@ -89,7 +89,7 @@ def get_input_fnames_find_ica_artifacts( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: bids_basename = BIDSPath( subject=subject, @@ -127,7 +127,7 @@ def find_ica_artifacts( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: """Run ICA.""" @@ -341,7 +341,7 @@ def get_config( *, config: SimpleNamespace, subject: str, - session: Optional[str] = None, + session: str | None = None, ) -> SimpleNamespace: cfg = SimpleNamespace( conditions=config.conditions, diff --git a/mne_bids_pipeline/steps/preprocessing/_06b_run_ssp.py b/mne_bids_pipeline/steps/preprocessing/_06b_run_ssp.py index 1580836ca..b17816a7e 100644 --- a/mne_bids_pipeline/steps/preprocessing/_06b_run_ssp.py +++ b/mne_bids_pipeline/steps/preprocessing/_06b_run_ssp.py @@ -5,7 +5,6 @@ """ from types import SimpleNamespace -from typing import Optional import mne import numpy as np @@ -28,7 +27,7 @@ from ..._run import _prep_out_files, _update_for_splits, failsafe_run, save_logs -def _find_ecg_events(raw: mne.io.Raw, ch_name: Optional[str]) -> np.ndarray: +def _find_ecg_events(raw: mne.io.Raw, ch_name: str | None) -> np.ndarray: """Wrap find_ecg_events to use the same defaults as create_ecg_events.""" return find_ecg_events(raw, ch_name=ch_name, l_freq=8, h_freq=16)[0] @@ -37,7 +36,7 @@ def get_input_fnames_run_ssp( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: bids_basename = BIDSPath( subject=subject, @@ -69,7 +68,7 @@ def run_ssp( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: import matplotlib.pyplot as plt diff --git a/mne_bids_pipeline/steps/preprocessing/_07_make_epochs.py b/mne_bids_pipeline/steps/preprocessing/_07_make_epochs.py index cc010d0b8..47f717959 100644 --- a/mne_bids_pipeline/steps/preprocessing/_07_make_epochs.py +++ b/mne_bids_pipeline/steps/preprocessing/_07_make_epochs.py @@ -9,7 +9,6 @@ import inspect from types import SimpleNamespace -from typing import Optional import mne from mne_bids import BIDSPath @@ -38,7 +37,7 @@ def get_input_fnames_epochs( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: """Get paths of files required by filter_data function.""" # Construct the basenames of the files we wish to load, and of the empty- @@ -79,7 +78,7 @@ def run_epochs( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: """Extract epochs for one subject.""" diff --git a/mne_bids_pipeline/steps/preprocessing/_08a_apply_ica.py b/mne_bids_pipeline/steps/preprocessing/_08a_apply_ica.py index b64e99f3a..4cea12ba4 100644 --- a/mne_bids_pipeline/steps/preprocessing/_08a_apply_ica.py +++ b/mne_bids_pipeline/steps/preprocessing/_08a_apply_ica.py @@ -6,7 +6,6 @@ """ from types import SimpleNamespace -from typing import Optional import mne import pandas as pd @@ -30,7 +29,7 @@ def _ica_paths( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ): bids_basename = BIDSPath( subject=subject, @@ -68,7 +67,7 @@ def get_input_fnames_apply_ica_epochs( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: in_files = _ica_paths(cfg=cfg, subject=subject, session=session) in_files["epochs"] = ( @@ -88,9 +87,9 @@ def get_input_fnames_apply_ica_raw( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], + task: str | None, ) -> dict: in_files = _get_run_rest_noise_path( cfg=cfg, @@ -114,7 +113,7 @@ def apply_ica_epochs( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: bids_basename = in_files["ica"].copy().update(processing=None) @@ -224,9 +223,9 @@ def apply_ica_raw( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], + task: str | None, in_files: dict, ) -> dict: ica = _read_ica_and_exclude(in_files) diff --git a/mne_bids_pipeline/steps/preprocessing/_08b_apply_ssp.py b/mne_bids_pipeline/steps/preprocessing/_08b_apply_ssp.py index 9a0026a78..ee2c56cb9 100644 --- a/mne_bids_pipeline/steps/preprocessing/_08b_apply_ssp.py +++ b/mne_bids_pipeline/steps/preprocessing/_08b_apply_ssp.py @@ -5,7 +5,6 @@ """ from types import SimpleNamespace -from typing import Optional import mne @@ -26,7 +25,7 @@ def get_input_fnames_apply_ssp_epochs( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: in_files = dict() in_files["proj"] = _proj_path(cfg=cfg, subject=subject, session=session) @@ -43,7 +42,7 @@ def apply_ssp_epochs( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: out_files = dict() @@ -74,9 +73,9 @@ def get_input_fnames_apply_ssp_raw( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], + task: str | None, ) -> dict: in_files = _get_run_rest_noise_path( cfg=cfg, @@ -100,9 +99,9 @@ def apply_ssp_raw( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, run: str, - task: Optional[str], + task: str | None, in_files: dict, ) -> dict: projs = mne.read_proj(in_files.pop("proj")) diff --git a/mne_bids_pipeline/steps/preprocessing/_09_ptp_reject.py b/mne_bids_pipeline/steps/preprocessing/_09_ptp_reject.py index 51ca1149b..e9de67a27 100644 --- a/mne_bids_pipeline/steps/preprocessing/_09_ptp_reject.py +++ b/mne_bids_pipeline/steps/preprocessing/_09_ptp_reject.py @@ -9,7 +9,6 @@ """ from types import SimpleNamespace -from typing import Optional import autoreject import mne @@ -33,7 +32,7 @@ def get_input_fnames_drop_ptp( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: bids_path = BIDSPath( subject=subject, @@ -63,7 +62,7 @@ def drop_ptp( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: out_files = dict() diff --git a/mne_bids_pipeline/steps/sensor/_01_make_evoked.py b/mne_bids_pipeline/steps/sensor/_01_make_evoked.py index 71835faee..879baf298 100644 --- a/mne_bids_pipeline/steps/sensor/_01_make_evoked.py +++ b/mne_bids_pipeline/steps/sensor/_01_make_evoked.py @@ -1,7 +1,6 @@ """Extract evoked data for each condition.""" from types import SimpleNamespace -from typing import Optional import mne from mne_bids import BIDSPath @@ -31,7 +30,7 @@ def get_input_fnames_evoked( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: fname_epochs = BIDSPath( subject=subject, @@ -62,7 +61,7 @@ def run_evoked( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: out_files = dict() diff --git a/mne_bids_pipeline/steps/sensor/_02_decoding_full_epochs.py b/mne_bids_pipeline/steps/sensor/_02_decoding_full_epochs.py index 4034245ff..58ccc7f2e 100644 --- a/mne_bids_pipeline/steps/sensor/_02_decoding_full_epochs.py +++ b/mne_bids_pipeline/steps/sensor/_02_decoding_full_epochs.py @@ -10,7 +10,6 @@ import os.path as op from types import SimpleNamespace -from typing import Optional import mne import numpy as np @@ -46,7 +45,7 @@ def get_input_fnames_epochs_decoding( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, condition1: str, condition2: str, ) -> dict: @@ -80,7 +79,7 @@ def run_epochs_decoding( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, condition1: str, condition2: str, in_files: dict, diff --git a/mne_bids_pipeline/steps/sensor/_03_decoding_time_by_time.py b/mne_bids_pipeline/steps/sensor/_03_decoding_time_by_time.py index 5ad221d67..8e5402d96 100644 --- a/mne_bids_pipeline/steps/sensor/_03_decoding_time_by_time.py +++ b/mne_bids_pipeline/steps/sensor/_03_decoding_time_by_time.py @@ -13,7 +13,6 @@ import os.path as op from types import SimpleNamespace -from typing import Optional import mne import numpy as np @@ -54,7 +53,7 @@ def get_input_fnames_time_decoding( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, condition1: str, condition2: str, ) -> dict: @@ -88,7 +87,7 @@ def run_time_decoding( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, condition1: str, condition2: str, in_files: dict, diff --git a/mne_bids_pipeline/steps/sensor/_04_time_frequency.py b/mne_bids_pipeline/steps/sensor/_04_time_frequency.py index 0ab3aa3ea..be04ca547 100644 --- a/mne_bids_pipeline/steps/sensor/_04_time_frequency.py +++ b/mne_bids_pipeline/steps/sensor/_04_time_frequency.py @@ -5,7 +5,6 @@ """ from types import SimpleNamespace -from typing import Optional import mne import numpy as np @@ -29,7 +28,7 @@ def get_input_fnames_time_frequency( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: fname_epochs = BIDSPath( subject=subject, @@ -60,7 +59,7 @@ def run_time_frequency( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: import matplotlib.pyplot as plt diff --git a/mne_bids_pipeline/steps/sensor/_05_decoding_csp.py b/mne_bids_pipeline/steps/sensor/_05_decoding_csp.py index ca7791fd4..368caaeb3 100644 --- a/mne_bids_pipeline/steps/sensor/_05_decoding_csp.py +++ b/mne_bids_pipeline/steps/sensor/_05_decoding_csp.py @@ -2,7 +2,6 @@ import os.path as op from types import SimpleNamespace -from typing import Optional import matplotlib.transforms import mne @@ -102,7 +101,7 @@ def get_input_fnames_csp( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, contrast: tuple[str], ) -> dict: proc = _get_decoding_proc(config=cfg) @@ -493,7 +492,7 @@ def _fmt_contrast(cond1, cond2, fmin, fmax, freq_range_name, tmin=None, tmax=Non def get_config( - *, config: SimpleNamespace, subject: str, session: Optional[str] + *, config: SimpleNamespace, subject: str, session: str | None ) -> SimpleNamespace: cfg = SimpleNamespace( # Data parameters diff --git a/mne_bids_pipeline/steps/sensor/_06_make_cov.py b/mne_bids_pipeline/steps/sensor/_06_make_cov.py index e3c8cdc9e..a9e23cc60 100644 --- a/mne_bids_pipeline/steps/sensor/_06_make_cov.py +++ b/mne_bids_pipeline/steps/sensor/_06_make_cov.py @@ -4,7 +4,6 @@ """ from types import SimpleNamespace -from typing import Optional import mne from mne_bids import BIDSPath @@ -34,7 +33,7 @@ def get_input_fnames_cov( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: cov_type = _get_cov_type(cfg) in_files = dict() @@ -94,12 +93,12 @@ def get_input_fnames_cov( def compute_cov_from_epochs( *, - tmin: Optional[float], - tmax: Optional[float], + tmin: float | None, + tmax: float | None, cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, out_files: dict, ) -> mne.Covariance: @@ -129,7 +128,7 @@ def compute_cov_from_raw( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, out_files: dict, ) -> mne.Covariance: @@ -152,7 +151,7 @@ def retrieve_custom_cov( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, out_files: dict, ) -> mne.Covariance: @@ -213,7 +212,7 @@ def run_covariance( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str] = None, + session: str | None = None, in_files: dict, ) -> dict: import matplotlib.pyplot as plt diff --git a/mne_bids_pipeline/steps/sensor/_99_group_average.py b/mne_bids_pipeline/steps/sensor/_99_group_average.py index 923b61ccb..2403bce3c 100644 --- a/mne_bids_pipeline/steps/sensor/_99_group_average.py +++ b/mne_bids_pipeline/steps/sensor/_99_group_average.py @@ -7,7 +7,6 @@ import os.path as op from functools import partial from types import SimpleNamespace -from typing import Optional import mne import numpy as np @@ -47,7 +46,7 @@ def get_input_fnames_average_evokeds( *, cfg: SimpleNamespace, subject: str, - session: Optional[dict], + session: dict | None, ) -> dict: in_files = dict() for this_subject in cfg.subjects: @@ -76,7 +75,7 @@ def average_evokeds( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: logger.info(**gen_log_kwargs(message="Creating grand averages")) @@ -185,7 +184,7 @@ class ClusterAcrossTime(TypedDict): def _decoding_cluster_permutation_test( scores: np.ndarray, times: np.ndarray, - cluster_forming_t_threshold: Optional[float], + cluster_forming_t_threshold: float | None, n_permutations: int, random_seed: int, ) -> tuple[np.ndarray, list[ClusterAcrossTime], int]: @@ -220,7 +219,7 @@ def _get_epochs_in_files( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: in_files = dict() in_files["epochs"] = BIDSPath( @@ -245,7 +244,7 @@ def _decoding_out_fname( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, cond_1: str, cond_2: str, kind: str, @@ -277,7 +276,7 @@ def _get_input_fnames_decoding( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, cond_1: str, cond_2: str, kind: str, @@ -308,7 +307,7 @@ def average_time_by_time_decoding( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, cond_1: str, cond_2: str, in_files: dict, @@ -551,7 +550,7 @@ def average_full_epochs_decoding( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, cond_1: str, cond_2: str, in_files: dict, @@ -626,7 +625,7 @@ def get_input_files_average_full_epochs_report( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, decoding_contrasts: list[list[str]], ) -> dict: in_files = dict() @@ -650,7 +649,7 @@ def average_full_epochs_report( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, decoding_contrasts: list[list[str]], in_files: dict, ) -> dict: @@ -712,7 +711,7 @@ def average_csp_decoding( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, cond_1: str, cond_2: str, in_files: dict, @@ -887,7 +886,7 @@ def _average_csp_time_freq( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, data: pd.DataFrame, ) -> pd.DataFrame: # Prepare a dataframe for storing the results. diff --git a/mne_bids_pipeline/steps/source/_01_make_bem_surfaces.py b/mne_bids_pipeline/steps/source/_01_make_bem_surfaces.py index 22c67c235..95c451327 100644 --- a/mne_bids_pipeline/steps/source/_01_make_bem_surfaces.py +++ b/mne_bids_pipeline/steps/source/_01_make_bem_surfaces.py @@ -6,7 +6,6 @@ import glob from pathlib import Path from types import SimpleNamespace -from typing import Optional import mne @@ -42,7 +41,7 @@ def get_input_fnames_make_bem_surfaces( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: in_files = dict() mri_images, mri_dir, flash_dir = _get_bem_params(cfg) @@ -59,7 +58,7 @@ def get_output_fnames_make_bem_surfaces( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: out_files = dict() conductivity, _ = _get_bem_conductivity(cfg) @@ -79,7 +78,7 @@ def make_bem_surfaces( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: mri_images, _, _ = _get_bem_params(cfg) diff --git a/mne_bids_pipeline/steps/source/_04_make_forward.py b/mne_bids_pipeline/steps/source/_04_make_forward.py index 87bcf6fd9..12342cfa6 100644 --- a/mne_bids_pipeline/steps/source/_04_make_forward.py +++ b/mne_bids_pipeline/steps/source/_04_make_forward.py @@ -4,7 +4,6 @@ """ from types import SimpleNamespace -from typing import Optional import mne import numpy as np @@ -31,7 +30,7 @@ def _prepare_trans_template( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, info: mne.Info, ) -> mne.transforms.Transform: assert isinstance(cfg.use_template_mri, str) @@ -67,7 +66,7 @@ def _prepare_trans_subject( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, bids_path: BIDSPath, ) -> mne.transforms.Transform: # Generate a head ↔ MRI transformation matrix from the @@ -119,7 +118,7 @@ def run_forward( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: bids_path = BIDSPath( @@ -218,7 +217,7 @@ def get_config( *, config: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> SimpleNamespace: if config.mri_t1_path_generator is None: t1_bids_path = None diff --git a/mne_bids_pipeline/steps/source/_05_make_inverse.py b/mne_bids_pipeline/steps/source/_05_make_inverse.py index 54f9fd0ae..c623ef7ee 100644 --- a/mne_bids_pipeline/steps/source/_05_make_inverse.py +++ b/mne_bids_pipeline/steps/source/_05_make_inverse.py @@ -4,7 +4,6 @@ """ from types import SimpleNamespace -from typing import Optional import mne from mne.minimum_norm import ( @@ -33,7 +32,7 @@ def get_input_fnames_inverse( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ): bids_path = BIDSPath( subject=subject, @@ -68,7 +67,7 @@ def run_inverse( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: # TODO: Eventually we should maybe loop over ch_types, e.g., to create diff --git a/mne_bids_pipeline/steps/source/_99_group_average.py b/mne_bids_pipeline/steps/source/_99_group_average.py index eb26c1c5f..81de0a01b 100644 --- a/mne_bids_pipeline/steps/source/_99_group_average.py +++ b/mne_bids_pipeline/steps/source/_99_group_average.py @@ -4,7 +4,6 @@ """ from types import SimpleNamespace -from typing import Optional import mne import numpy as np @@ -28,7 +27,7 @@ def _stc_path( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, condition: str, morphed: bool, ) -> BIDSPath: @@ -58,7 +57,7 @@ def get_input_fnames_morph_stc( cfg: SimpleNamespace, subject: str, fs_subject: str, - session: Optional[str], + session: str | None, ) -> dict: in_files = dict() for condition in _all_conditions(cfg=cfg): @@ -81,7 +80,7 @@ def morph_stc( exec_params: SimpleNamespace, subject: str, fs_subject: str, - session: Optional[str], + session: str | None, in_files: dict, ) -> dict: out_files = dict() @@ -113,7 +112,7 @@ def get_input_fnames_run_average( *, cfg: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, ) -> dict: in_files = dict() assert subject == "average" @@ -137,7 +136,7 @@ def run_average( cfg: SimpleNamespace, exec_params: SimpleNamespace, subject: str, - session: Optional[str], + session: str | None, in_files: dict, ): assert subject == "average" diff --git a/mne_bids_pipeline/tests/test_run.py b/mne_bids_pipeline/tests/test_run.py index cc69c9efc..952be5f13 100644 --- a/mne_bids_pipeline/tests/test_run.py +++ b/mne_bids_pipeline/tests/test_run.py @@ -5,7 +5,7 @@ import sys from collections.abc import Collection from pathlib import Path -from typing import Optional, TypedDict +from typing import TypedDict import pytest @@ -25,7 +25,7 @@ class _TestOptionsT(TypedDict, total=False): dataset: str # key.split("_")[0] config: str # f"config_{key}.py" steps: Collection[str] # ("preprocessing", "sensor") - task: Optional[str] # None + task: str | None # None env: dict[str, str] # {} requires: Collection[str] # () extra_config: str # "" diff --git a/mne_bids_pipeline/typing.py b/mne_bids_pipeline/typing.py index 61f2abdeb..8ac9ecfe4 100644 --- a/mne_bids_pipeline/typing.py +++ b/mne_bids_pipeline/typing.py @@ -2,7 +2,7 @@ import pathlib import sys -from typing import Annotated, Union +from typing import Annotated if sys.version_info < (3, 12): from typing_extensions import TypedDict @@ -14,7 +14,7 @@ from numpy.typing import ArrayLike from pydantic import PlainValidator -PathLike = Union[str, pathlib.Path] +PathLike = str | pathlib.Path class ArbitraryContrast(TypedDict): diff --git a/pyproject.toml b/pyproject.toml index 7c4928f35..fae794214 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "mne-bids-pipeline" # Keep in sync with README.md: description = "A full-flegded processing pipeline for your MEG and EEG data" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" license = { file = "LICENSE.txt" } keywords = ["science", "neuroscience", "psychology"] authors = [