Source code for tracmass.verbose
"""Verbose debug output: timestamped progress messages for each run stage.
tracmass logs what it is doing (adapter calls, flux reads with their size,
seeding, kernel compilation, each window's phases) to the standard
:mod:`logging` logger ``"tracmass"`` at ``DEBUG`` level. Nothing is shown
unless verbose output is turned on, in any of these ways:
- ``tracmass.run(..., verbose=True)`` or ``verbose = true`` in the active
``tracmass.toml`` environment (also honoured by the integrators);
- :func:`set_verbose` for the rest of the session;
- standard logging config, e.g.
``logging.getLogger("tracmass").setLevel("DEBUG")`` plus a handler.
Messages go to ``stderr``, one line each, with the wall-clock time, so a run
that seems stuck shows which step it is in and how long the previous steps
took.
"""
from __future__ import annotations
import contextlib
import logging
import sys
import time
logger = logging.getLogger("tracmass")
_handler: logging.Handler | None = None
_saved_level: int = logging.NOTSET
[docs]
def set_verbose(verbose: bool = True) -> None:
"""Turn verbose debug output on or off for the session.
Parameters
----------
verbose : bool, optional
True attaches a ``stderr`` handler to the ``"tracmass"`` logger and
sets it to ``DEBUG``; False removes the handler again and restores
the previous level. Default True.
Examples
--------
>>> tracmass.set_verbose()
>>> result = tracmass.run(env="gog")
"""
global _handler, _saved_level
if verbose and _handler is None:
_handler = logging.StreamHandler(sys.stderr)
_handler.setFormatter(
logging.Formatter("%(asctime)s %(name)s: %(message)s", "%H:%M:%S")
)
_saved_level = logger.level
logger.addHandler(_handler)
logger.setLevel(logging.DEBUG)
elif not verbose and _handler is not None:
logger.removeHandler(_handler)
logger.setLevel(_saved_level)
_handler = None
[docs]
def is_verbose() -> bool:
"""``True`` while :func:`set_verbose` output is on."""
return _handler is not None
[docs]
def resolve_verbose(verbose: bool | None = None) -> bool:
"""*verbose* if given, else the ``verbose`` setting (default False)."""
if verbose is not None:
return bool(verbose)
try:
from tracmass.config import settings
return bool(settings.get("verbose", False))
except Exception: # pragma: no cover - config is optional
return False
[docs]
@contextlib.contextmanager
def verbose_output(verbose: bool | None = None):
"""Turn verbose output on for a ``with`` block, if *verbose* resolves True.
Output that was already on (from :func:`set_verbose` or an outer block)
stays on afterwards; output turned on here is turned off on exit.
"""
turn_on = resolve_verbose(verbose) and not is_verbose()
if turn_on:
set_verbose(True)
try:
yield
finally:
if turn_on:
set_verbose(False)
[docs]
@contextlib.contextmanager
def timed(log: logging.Logger, msg: str, *args):
"""Log ``msg ...`` at entry and ``msg: done in N s`` at exit (DEBUG)."""
if not log.isEnabledFor(logging.DEBUG):
yield
return
log.debug(msg + " ...", *args)
t0 = time.perf_counter()
yield
log.debug(msg + ": done in %.1f s", *args, time.perf_counter() - t0)
[docs]
def gb(nbytes: float) -> str:
"""*nbytes* formatted as ``"1.23 GB"``."""
return f"{nbytes / 1e9:.2f} GB"