Source code for mne_rt._logging

"""Logging and verbosity utilities for MNE-RT.

Integrates with MNE's logging infrastructure so that setting ``verbose``
on any MNE-RT function also silences or expands MNE's own output consistently.

Usage
-----
Apply ``@verbose`` to any public function or method that accepts a
``verbose`` keyword argument::

    from mne_rt._logging import verbose, logger

    @verbose
    def my_function(a, b, verbose=None):
        logger.info("Computing …")
        ...

The ``verbose`` parameter accepts the same values as ``mne.set_log_level``:

* ``None``    — leave current level unchanged
* ``True``    — ``"INFO"``
* ``False``   — ``"WARNING"``
* ``"DEBUG"``, ``"INFO"``, ``"WARNING"``, ``"ERROR"``, ``"CRITICAL"``
* An integer MNE/Python logging level
"""
from __future__ import annotations

import functools
import inspect
import logging
from typing import Union


# ---------------------------------------------------------------------------
# Module-level logger
# ---------------------------------------------------------------------------

logger = logging.getLogger("ant")

if not logger.handlers:
    _handler = logging.StreamHandler()
    _handler.setFormatter(logging.Formatter("[ANT] %(levelname)s %(message)s"))
    logger.addHandler(_handler)
    logger.setLevel(logging.WARNING)

logger.propagate = False


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

_LEVEL_MAP: dict = {
    None: None,
    True: logging.INFO,
    False: logging.WARNING,
    "DEBUG": logging.DEBUG,
    "INFO": logging.INFO,
    "WARNING": logging.WARNING,
    "ERROR": logging.ERROR,
    "CRITICAL": logging.CRITICAL,
}


[docs] def set_log_level(verbose: Union[bool, str, int, None]) -> None: """Set the ANT (and MNE) logging level. Parameters ---------- verbose : bool | str | int | None Desired verbosity level. * ``None`` — no change * ``True`` — ``"INFO"`` * ``False`` — ``"WARNING"`` * ``"DEBUG"``, ``"INFO"``, ``"WARNING"``, ``"ERROR"`` * An integer :mod:`logging` level See Also -------- mne.set_log_level : Set MNE's own logging level. """ if verbose is None: return if isinstance(verbose, str): verbose = verbose.upper() level = _LEVEL_MAP.get(verbose, verbose) if not isinstance(level, int): raise ValueError( f"verbose must be None, bool, an int, or one of " f"'DEBUG','INFO','WARNING','ERROR','CRITICAL'; got {verbose!r}." ) logger.setLevel(level) # Mirror to MNE so MNE-internal calls respect the same level try: import mne mne_level = { logging.DEBUG: "DEBUG", logging.INFO: "INFO", logging.WARNING: "WARNING", logging.ERROR: "ERROR", }.get(level, "WARNING") mne.set_log_level(mne_level, add_frames=0) except Exception: pass
# --------------------------------------------------------------------------- # Decorator # --------------------------------------------------------------------------- def verbose(func): """Decorator that applies the ``verbose`` kwarg to ANT and MNE logging. Wrap any function that has a ``verbose=None`` parameter:: @verbose def compute(data, verbose=None): ... The decorator sets the ANT log level for the duration of the call, then restores the previous level. Parameters ---------- func : callable Function to wrap. Must accept ``verbose`` as a keyword (or positional) argument. Returns ------- wrapper : callable """ sig = inspect.signature(func) param_names = list(sig.parameters.keys()) verbose_idx = param_names.index("verbose") if "verbose" in param_names else None @functools.wraps(func) def wrapper(*args, **kwargs): # Resolve verbose value from kwargs or positional args v = kwargs.get("verbose", None) if v is None and verbose_idx is not None and verbose_idx < len(args): v = args[verbose_idx] if v is None: return func(*args, **kwargs) old_level = logger.level try: set_log_level(v) return func(*args, **kwargs) finally: logger.setLevel(old_level) return wrapper