internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: mne-tools/mne-bids-pipeline

Issue:
Print heartbeat log messages
Some steps take quite a while to complete without printing log messages. This causes issues with CircleCI, which may believe the job is stuck; but it's also an issue for users, who cannot be sure that we're still operational either.

The least we should do is periodically print some heartbeat messages.

Better yet would be some useful status info.

Affected steps are e.g. the time-by-time decoding with our ds000246 configuration.

cc @larsoner 


Discussion:

Resolving patch:
diff --git a/mne_bids_pipeline/scripts/sensor/_03_decoding_time_by_time.py b/mne_bids_pipeline/scripts/sensor/_03_decoding_time_by_time.py
--- a/mne_bids_pipeline/scripts/sensor/_03_decoding_time_by_time.py
+++ b/mne_bids_pipeline/scripts/sensor/_03_decoding_time_by_time.py
@@ -37,7 +37,8 @@
 from ..._decoding import LogReg
 from ..._logging import gen_log_kwargs, logger
 from ..._run import failsafe_run, save_logs
-from ..._parallel import get_parallel_backend, get_n_jobs
+from ..._parallel import (
+    get_parallel_backend, get_n_jobs, get_parallel_backend_name)
 
 
 def get_input_fnames_time_decoding(**kwargs):
@@ -71,7 +72,11 @@ def get_input_fnames_time_decoding(**kwargs):
               get_input_fnames=get_input_fnames_time_decoding)
 def run_time_decoding(*, cfg, subject, condition1, condition2, session,
                       in_files):
-    msg = f'Contrasting conditions: {condition1} – {condition2}'
+    if cfg.decoding_time_generalization:
+        kind = 'time generalization'
+    else:
+        kind = 'sliding estimator'
+    msg = f'Contrasting conditions ({kind}): {condition1} – {condition2}'
     logger.info(**gen_log_kwargs(message=msg, subject=subject,
                                  session=session))
     out_files = dict()
@@ -103,6 +108,8 @@ def run_time_decoding(*, cfg, subject, condition1, condition2, session,
 
     X = epochs.get_data()
     y = np.r_[np.ones(n_cond1), np.zeros(n_cond2)]
+    # ProgressBar does not work on dask, so only enable it if not using dask
+    verbose = get_parallel_backend_name(cfg) != "dask"
     with get_parallel_backend(cfg):
         clf = make_pipeline(
             StandardScaler(),
@@ -134,7 +141,8 @@ def run_time_decoding(*, cfg, subject, condition1, condition2, session,
             cv_scoring_n_jobs = cfg.n_jobs
 
         scores = cross_val_multiscore(
-            estimator, X=X, y=y, cv=cv, n_jobs=cv_scoring_n_jobs
+            estimator, X=X, y=y, cv=cv, n_jobs=cv_scoring_n_jobs,
+            verbose=verbose,  # ensure ProgressBar is shown (can be slow)
         )
 
         # let's save the scores now


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
BUG: Contrasts missing from report
> I believe there’s a bug in the pipeline and some of the outputs regarding contrasts are actually missing from the reports. For example for the grand mean N400 report for ERP CORE:

> [https://mne.tools/mne-bids-pipeline/examples/ERP_CORE/sub-average_ses-N400_task-N400_report.html 1](https://mne.tools/mne-bids-pipeline/examples/ERP_CORE/sub-average_ses-N400_task-N400_report.html)

*Originally posted by @hoechenberger in https://mne.discourse.group/t/mne-bids-pipeline-erf-erp-sensor-space-analysis/5979/2*

I think we should standardize our use of the private all-contrast function while fixing this bug...


Discussion:

Resolving patch:
diff --git a/mne_bids_pipeline/_report.py b/mne_bids_pipeline/_report.py
--- a/mne_bids_pipeline/_report.py
+++ b/mne_bids_pipeline/_report.py
@@ -547,11 +547,16 @@ def run_report_average_sensor(
     if cfg.task is not None:
         title += f', task-{cfg.task}'
 
-    evokeds = mne.read_evokeds(evoked_fname)
-    for evoked in evokeds:
+    all_evokeds = mne.read_evokeds(evoked_fname)
+    for evoked in all_evokeds:
         _restrict_analyze_channels(evoked, cfg)
-
     conditions = _all_conditions(cfg=cfg)
+    assert len(conditions) == len(all_evokeds)
+    all_evokeds = {
+        cond: evoked
+        for cond, evoked in zip(conditions, all_evokeds)
+    }
+
     with _open_report(
             cfg=cfg,
             exec_params=exec_params,
@@ -573,16 +578,13 @@ def run_report_average_sensor(
         #
         # Visualize evoked responses.
         #
-        for condition, evoked in zip(conditions, evokeds):
+        for condition, evoked in all_evokeds.items():
+            tags = ('evoked', _sanitize_cond_tag(condition))
             if condition in cfg.conditions:
-                title = f'Average: {condition}'
-                tags = (
-                    'evoked',
-                    _sanitize_cond_tag(condition)
-                )
+                title = f'Condition: {condition}'
             else:  # It's a contrast of two conditions.
-                # XXX Will change once we process contrasts here too
-                continue
+                title = f'Contrast: {condition}'
+                tags = tags + ('contrast',)
 
             report.add_evokeds(
                 evokeds=evoked,
diff --git a/mne_bids_pipeline/steps/sensor/_99_group_average.py b/mne_bids_pipeline/steps/sensor/_99_group_average.py
--- a/mne_bids_pipeline/steps/sensor/_99_group_average.py
+++ b/mne_bids_pipeline/steps/sensor/_99_group_average.py
@@ -596,7 +596,7 @@ def get_config(
         proc=config.proc,
         deriv_root=config.deriv_root,
         conditions=config.conditions,
-        contrasts=config.contrasts,
+        contrasts=get_all_contrasts(config),
         decode=config.decode,
         decoding_metric=config.decoding_metric,
         decoding_n_splits=config.decoding_n_splits,


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
Doc deployment step failing
The latest CI run failed to execute documentation deployment:
https://app.circleci.com/pipelines/github/mne-tools/mne-bids-pipeline/3557/workflows/3458e5cc-c471-4664-8d0a-b0cc4961f9eb/jobs/41986/parallel-runs/0/steps/0-107

```shell
#!/bin/bash -eo pipefail
./.circleci/setup_bash.sh
CIRCLE_JOB=deploy_docs
COMMIT_MESSAGE=68c63d6878992fb7c298f24420f1d349c6811079 MAINT: Use mike for doc deployment (#676)
COMMIT_MESSAGE_ESCAPED=68c63d6878992fb7c298f24420f1d349c6811079 MAINT: Use mike for doc deployment (#676)
CIRCLE_REQUESTED_JOB=
Running job deploy_docs for main branch
./.circleci/setup_bash.sh: line 35: sudo: command not found

Exited with code exit status 127
CircleCI received exit code 127
```


Discussion:

Resolving patch:
diff --git a/docs/source/features/gen_steps.py b/docs/source/features/gen_steps.py
--- a/docs/source/features/gen_steps.py
+++ b/docs/source/features/gen_steps.py
@@ -12,6 +12,7 @@
 Template. All steps exist in the `steps`/ directory.
 """
 
+print('Generating steps …')
 step_modules = _get_step_modules()
 
 # Construct the lines of steps.md


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
plot_psd() is legacy, we should replace it with compute_psd.plot()
cc @SophieHerbst 


Discussion:

Resolving patch:
diff --git a/mne_bids_pipeline/steps/preprocessing/_03_frequency_filter.py b/mne_bids_pipeline/steps/preprocessing/_03_frequency_filter.py
--- a/mne_bids_pipeline/steps/preprocessing/_03_frequency_filter.py
+++ b/mne_bids_pipeline/steps/preprocessing/_03_frequency_filter.py
@@ -218,7 +218,7 @@ def filter_data(
     if exec_params.interactive:
         # Plot raw data and power spectral density.
         raw.plot(n_channels=50, butterfly=True)
-        raw.plot_psd(fmax=fmax)
+        raw.compute_psd(fmax=fmax).plot()
 
     del raw
 
@@ -302,7 +302,7 @@ def filter_data(
         if exec_params.interactive:
             # Plot raw data and power spectral density.
             raw_noise.plot(n_channels=50, butterfly=True)
-            raw_noise.plot_psd(fmax=fmax)
+            raw_noise.compute_psd(fmax=fmax).plot()
 
     assert len(in_files) == 0, in_files.keys()
 


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
Add version info to mne-bids-pipeline
> it would be helpful if the version of the bids-pipeline used per step was to be found in the output for reporting in a paper and replicability

_Originally posted by @SophieHerbst in https://github.com/mne-tools/mne-bids-pipeline/issues/609#issuecomment-1482981862_
            


Discussion:
... I thought at some point we had `mne.sys_info()` output in there, but looking online I don't see it e.g. here

https://mne.tools/mne-bids-pipeline/1.2/examples/ds004229/sub-102_task-amnoise_report.html

We should `lru_cache` the output string
> ... I thought at some point we had `mne.sys_info()` output in there, but looking online I don't see it e.g. here

It probably got lost somewhere during a refactoring, it used to be included.

But I don't think mne-bids-pipeline is listed in mne.sys_info(), or is it? If it isn't, we should add it upstream

Resolving patch:
diff --git a/mne_bids_pipeline/_report.py b/mne_bids_pipeline/_report.py
--- a/mne_bids_pipeline/_report.py
+++ b/mne_bids_pipeline/_report.py
@@ -78,8 +78,8 @@ def _open_report(
                     session=session,
                     run=run,
                 )
-            except Exception:
-                pass
+            except Exception as exc:
+                logger.warning(f"Failed: {exc}")
             fname_report_html = fname_report.with_suffix(".html")
             msg = f"Saving report: {fname_report_html}"
             logger.info(**gen_log_kwargs(message=msg))
@@ -536,7 +536,7 @@ def _finalize(
 # We make a lot of calls to this function and it takes > 1 sec generally
 # to run, so run it just once (it shouldn't meaningfully change anyway)
 @lru_cache(maxsize=1)
-def _cached_sys_info(f):
+def _cached_sys_info():
     with StringIO() as f:
         mne.sys_info(f)
         return f.getvalue()
diff --git a/mne_bids_pipeline/_run.py b/mne_bids_pipeline/_run.py
--- a/mne_bids_pipeline/_run.py
+++ b/mne_bids_pipeline/_run.py
@@ -12,7 +12,6 @@
 import time
 from typing import Callable, Optional, Dict, List
 from types import SimpleNamespace
-import warnings
 
 from joblib import Memory
 import json_tricks
@@ -320,13 +319,8 @@ def save_logs(*, config: SimpleNamespace, logs) -> None:  # TODO add type
     else:
         writer = pd.ExcelWriter(fname, engine="openpyxl")
 
-    df.to_excel(writer, sheet_name=sheet_name, index=False)
-    # TODO: "Future!!! warning save is not part of the public API, usage can give
-    # in unexpected results and will be removed in a future version"
-    with warnings.catch_warnings(record=True):
-        warnings.simplefilter("ignore")
-        writer.save()
-    writer.close()
+    with writer:
+        df.to_excel(writer, sheet_name=sheet_name, index=False)
 
 
 def _update_for_splits(


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
Setting decode_csp does run CSP, but doesn't create grand average CSP results when running the group average step
The reason is that we decide to run CSP if `decoding_csp is True`, but when generating the grand average, we check for the combination of **two** configuration values:

https://github.com/mne-tools/mne-bids-pipeline/blob/05b72e82d4c36f1a81bd6424a4301974cd6d7f3b/mne_bids_pipeline/steps/sensor/_99_group_average.py#L668

This should probably be changed to just
```python
 if cfg.decoding_csp: 
```

cc @SophieHerbst 


Discussion:

Resolving patch:
diff --git a/mne_bids_pipeline/steps/sensor/_99_group_average.py b/mne_bids_pipeline/steps/sensor/_99_group_average.py
--- a/mne_bids_pipeline/steps/sensor/_99_group_average.py
+++ b/mne_bids_pipeline/steps/sensor/_99_group_average.py
@@ -665,7 +665,7 @@ def run_group_average_sensor(
             if cfg.decode:
                 average_full_epochs_decoding(cfg, session)
                 average_time_by_time_decoding(cfg, session)
-        if cfg.decode and cfg.decoding_csp:
+        if cfg.decoding_csp:
             parallel, run_func = parallel_func(
                 average_csp_decoding, exec_params=exec_params
             )


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
When CSP is skipped, we don't provide the correct log message
We **do** prepare a helpful log message but don't actually use it:
https://github.com/mne-tools/mne-bids-pipeline/blob/05b72e82d4c36f1a81bd6424a4301974cd6d7f3b/mne_bids_pipeline/steps/sensor/_05_decoding_csp.py#L541-L549

The bug is in line 547, which should simply be deleted


cc @SophieHerbst 


Discussion:

Resolving patch:
diff --git a/mne_bids_pipeline/steps/sensor/_05_decoding_csp.py b/mne_bids_pipeline/steps/sensor/_05_decoding_csp.py
--- a/mne_bids_pipeline/steps/sensor/_05_decoding_csp.py
+++ b/mne_bids_pipeline/steps/sensor/_05_decoding_csp.py
@@ -544,7 +544,7 @@ def main(*, config: SimpleNamespace) -> None:
         else:
             msg = "No CSP analysis requested. "
 
-        msg = "Skipping …"
+        msg += "Skipping …"
         logger.info(**gen_log_kwargs(message=msg, emoji="skip"))
         return
 


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
mne_bids_pipeline/_run.py:317: FutureWarning: Setting the `book` attribute is not part of the public API
I'm receiving a `FutureWarning`, probably related to Pandas? In any case, we should mask it – it currently appears in the output on the console, which is not nice.
```
mne_bids_pipeline/_run.py:317: FutureWarning: Setting the `book` attribute is not part of the public API, usage can give unexpected or corrupted results and will be removed in a future version
  writer.book = book
```


Discussion:
@larsoner Do you know if this has been fixed through #732?
We still have a `writer.book = book` in that file but it's in a `try/except`. We should probably figure out the right way to set the book... or do whatever that code is trying to do.

Resolving patch:
diff --git a/mne_bids_pipeline/_run.py b/mne_bids_pipeline/_run.py
--- a/mne_bids_pipeline/_run.py
+++ b/mne_bids_pipeline/_run.py
@@ -13,9 +13,9 @@
 from typing import Callable, Optional, Dict, List
 from types import SimpleNamespace
 
+from filelock import FileLock
 from joblib import Memory
 import json_tricks
-from openpyxl import load_workbook
 import pandas as pd
 from mne_bids import BIDSPath
 
@@ -301,26 +301,16 @@ def save_logs(*, config: SimpleNamespace, logs) -> None:  # TODO add type
 
     df = df[columns]
 
-    if fname.exists():
-        book = None
-        try:
-            book = load_workbook(fname)
-        except Exception:  # bad file
-            pass
-        else:
-            if sheet_name in book:
-                book.remove(book[sheet_name])
-        writer = pd.ExcelWriter(fname, engine="openpyxl")
-        if book is not None:
-            try:
-                writer.book = book
-            except Exception:
-                pass  # AttributeError: can't set attribute 'book' (?)
-    else:
-        writer = pd.ExcelWriter(fname, engine="openpyxl")
-
-    with writer:
-        df.to_excel(writer, sheet_name=sheet_name, index=False)
+    with FileLock(fname.with_suffix(fname.suffix + ".lock")):
+        append = fname.exists()
+        writer = pd.ExcelWriter(
+            fname,
+            engine="openpyxl",
+            mode="a" if append else "w",
+            if_sheet_exists="replace" if append else None,
+        )
+        with writer:
+            df.to_excel(writer, sheet_name=sheet_name, index=False)
 
 
 def _update_for_splits(


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
Make some dependencies explicit
We depend on

  - numpy
  - scipy
  - matplotlib
  - nibabel

(we import these packages explicitly) but they're not specified in `pyproject.toml`


Discussion:

Resolving patch:
diff --git a/mne_bids_pipeline/_config.py b/mne_bids_pipeline/_config.py
--- a/mne_bids_pipeline/_config.py
+++ b/mne_bids_pipeline/_config.py
@@ -618,7 +618,7 @@
 
 1. Choose a reference run. Often one from the middle of the recording session
    is a good choice. Set `mf_destination = "reference_run" and then set
-   [`config.mf_reference_run`](mne_bids_pipeline._config.mf_reference_run).
+   [`config.mf_reference_run`][mne_bids_pipeline._config.mf_reference_run].
    This will result in a device-to-head transformation that differs between
    subjects.
 2. Choose a standard position in the MEG coordinate frame. For this, pass
@@ -643,7 +643,7 @@
 mf_reference_run: Optional[str] = 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).
+runs when [`mf_destination="reference_run"`][mne_bids_pipeline._config.mf_destination].
 If `None`, pick the first run.
 
 ???+ example "Example"


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
Add command line option for deriv_root
As I develop a Docker App using mne-bids-pipeline, I want a straightforward way to overwrite the bids_root and deriv_root values defined in the configuration file from the command line. This enhancement aims to simplify Docker volume mapping.

Currently, the option to override `bids_root `is already available ([source](https://github.com/mne-tools/mne-bids-pipeline/blob/ea95979fdb0ef807b3cd262da409cf010ada3da3/mne_bids_pipeline/_main.py#L56)), but there is no similar option for `deriv_root`.

I suggest adding a command-line option to the mne-bids-pipeline tool to allow users to specify the deriv_root directory. This behavior is similar to other BidsApps such as `FMRIprep `and `MRIQC`, where command line arguments overwrite configuration files.

If you are interested, I would be happy to contribute by taking care of the pull request.


Discussion:
Hello! 👋 Thanks for opening your first issue here! ❤️ We will try to get back to you soon. 🚴🏽‍♂️

Makes sense to me, PR welcome!

Resolving patch:
diff --git a/mne_bids_pipeline/_main.py b/mne_bids_pipeline/_main.py
--- a/mne_bids_pipeline/_main.py
+++ b/mne_bids_pipeline/_main.py
@@ -59,6 +59,18 @@ def main():
         default=None,
         help="BIDS root directory of the data to process.",
     )
+    parser.add_argument(
+        "--deriv_root",
+        dest="deriv_root",
+        default=None,
+        help=dedent(
+            """\
+        The root of the derivatives directory
+        in which the pipeline will store the processing results.
+        If unspecified, this will be derivatives/mne-bids-pipeline
+        inside the BIDS root."""
+        ),
+    ),
     parser.add_argument(
         "--subject", dest="subject", default=None, help="The subject to process."
     )
@@ -115,6 +127,7 @@ def main():
         )
     steps = options.steps
     root_dir = options.root_dir
+    deriv_root = options.deriv_root
     subject, session = options.subject, options.session
     task, run = options.task, options.run
     n_jobs = options.n_jobs
@@ -148,6 +161,10 @@ def main():
     overrides = SimpleNamespace()
     if root_dir:
         overrides.bids_root = pathlib.Path(root_dir).expanduser().resolve(strict=True)
+    if deriv_root:
+        overrides.deriv_root = (
+            pathlib.Path(deriv_root).expanduser().resolve(strict=False)
+        )
     if subject:
         overrides.subjects = [subject]
     if session:


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
"joblib" directory in output folder
I'm getting a "joblib" folder in my derivatives directory. Is this intended behavior? if so, could we make it "hidden" (prefix with a `.`)?

PS wanted to upload a screenshot but somehow it's not working


Discussion:
yes and I would say no

it can fill up your disk and you won't see it easily if you hide the folder

now we could call it something more meaninful in our context

> Message ID: ***@***.***>
>

How about calling it `_cache`?
no strong feeling.

> Message ID: ***@***.***>
>


Resolving patch:
diff --git a/mne_bids_pipeline/_config.py b/mne_bids_pipeline/_config.py
--- a/mne_bids_pipeline/_config.py
+++ b/mne_bids_pipeline/_config.py
@@ -2107,7 +2107,14 @@ def noise_cov(bids_path):
 """
 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
-`'joblib'` subdirectory in the BIDS derivative root of the dataset.
+`"_cache"` subdirectory (name configurable via the
+[`memory_subdir`][mne_bids_pipeline._config.memory_subdir]
+variable) in the BIDS derivative root of the dataset.
+"""
+
+memory_subdir: str = "_cache"
+"""
+The caching directory name to use if `memory_location` is `True`.
 """
 
 memory_file_method: Literal["mtime", "hash"] = "mtime"
diff --git a/mne_bids_pipeline/_config_import.py b/mne_bids_pipeline/_config_import.py
--- a/mne_bids_pipeline/_config_import.py
+++ b/mne_bids_pipeline/_config_import.py
@@ -75,6 +75,7 @@ def _import_config(
         "interactive",
         # Caching
         "memory_location",
+        "memory_subdir",
         "memory_verbose",
         "memory_file_method",
         # Misc
diff --git a/mne_bids_pipeline/_run.py b/mne_bids_pipeline/_run.py
--- a/mne_bids_pipeline/_run.py
+++ b/mne_bids_pipeline/_run.py
@@ -123,7 +123,7 @@ class ConditionalStepMemory:
     def __init__(self, *, exec_params, get_input_fnames, get_output_fnames):
         memory_location = exec_params.memory_location
         if memory_location is True:
-            use_location = exec_params.deriv_root / "joblib"
+            use_location = exec_params.deriv_root / exec_params.memory_subdir
         elif not memory_location:
             use_location = None
         else:


Test patch:
Repository: mne-tools/mne-bids-pipeline

Issue:
Caching seems not to be working on macOS
_I cannot provide sufficient information now – this issue is supposed to serve as a reminder._

I'm under the impression that caching is not working on my macOS machine – it seems that all steps are always run again, regardless of the caching setting. I also tried the `"hash"` method, but I'm not seeing any change. Needs to be investigated.


Discussion:
This makes me think that we should take our smallest dataset and set up a GitHub actions version of the tests that just run on that dataset.
So a rough check (ignore my cruft):
```
$ du -s ~/mne_data/ds00* | sort -rnk1
3883408	/home/larsoner/mne_data/ds001810
3474416	/home/larsoner/mne_data/ds000246
2407716	/home/larsoner/mne_data/ds003104
2286128	/home/larsoner/mne_data/ds000248
1864280	/home/larsoner/mne_data/ds004229
1766884	/home/larsoner/mne_data/ds000117
1690760	/home/larsoner/mne_data/ds000247
992692	/home/larsoner/mne_data/ds004107
891284	/home/larsoner/mne_data/ds000248_ica
851544	/home/larsoner/mne_data/ds003392
181484	/home/larsoner/mne_data/ds001971
30796	/home/larsoner/mne_data/ds003775
```
Looks like we could use 1971, 3775, or 3392. 3392 is our smallest MEG dataset and 1971 runs decoding so I'm inclined toward testing those.

Resolving patch:
diff --git a/mne_bids_pipeline/_logging.py b/mne_bids_pipeline/_logging.py
--- a/mne_bids_pipeline/_logging.py
+++ b/mne_bids_pipeline/_logging.py
@@ -27,7 +27,14 @@ def _console(self):
         force_terminal = os.getenv("MNE_BIDS_PIPELINE_FORCE_TERMINAL", None)
         if force_terminal is not None:
             force_terminal = force_terminal.lower() in ("true", "1")
-        kwargs = dict(soft_wrap=True, force_terminal=force_terminal)
+        legacy_windows = os.getenv("MNE_BIDS_PIPELINE_LEGACY_WINDOWS", None)
+        if legacy_windows is not None:
+            legacy_windows = legacy_windows.lower() in ("true", "1")
+        kwargs = dict(
+            soft_wrap=True,
+            force_terminal=force_terminal,
+            legacy_windows=legacy_windows,
+        )
         kwargs["theme"] = rich.theme.Theme(
             dict(
                 default="white",


Test patch:
diff --git a/mne_bids_pipeline/tests/conftest.py b/mne_bids_pipeline/tests/conftest.py
--- a/mne_bids_pipeline/tests/conftest.py
+++ b/mne_bids_pipeline/tests/conftest.py
@@ -55,6 +55,8 @@ def pytest_configure(config):
     #../python_env/lib/python3.10/site-packages/mne/report/report.py:1713: in _add_ica_artifact_sources
     #    self._add_figure(
     always:constrained_layout not applied.*:UserWarning
+    ignore:datetime\.datetime\.utcfromtimestamp.*:DeprecationWarning
+    ignore:datetime\.datetime\.utcnow.*:DeprecationWarning
     """
     for warning_line in warning_lines.split("\n"):
         warning_line = warning_line.strip()