"""Loging utilities."""
import logging
from contextlib import contextmanager
from fnmatch import fnmatch
[docs]@contextmanager
def logs(logger, level=None, handler=None, close=True):
"""Emit logs at or above `level` in the encapsulated context, optionally using the
specified `handler`.
See `the Python logging cookbook
<https://docs.python.org/3/howto/logging-cookbook.html#using-a-context-manager-for-selective-logging>`__
for more information.
Parameters
----------
logger : :class:`logging.Logger`
The logger to use for the encapsulated context.
level : str or int, optional
The minimum log levels to emit. The standard log levels "debug", "info",
"warning", "error" and "critical" are supported, as are their corresponding
level numbers (see :mod:`logging`).
handler : :class:`logging.LogHandler`, optional
The handler to add to `logger` for the encapsulated context.
close : bool, optional
Close `handler` once finished. Defaults to `True`.
Examples
--------
Print debug logs during parsing, excluding compilation messages.
>>> import logging
>>> from finesse import Model
>>> from finesse.utilities import logs
>>> from finesse.utilities.logging import FinesseStreamHandler
>>> handler = FinesseStreamHandler()
>>> handler.exclude("finesse.script.compiler")
>>> model = Model()
>>> with logs(logging.getLogger(), level="debug", handler=handler):
>>> model.parse("laser l1 P=1")
"""
old_level = None
if level is not None:
try:
# Convert level name to uppercase.
level = level.upper()
except AttributeError:
# Probably a number.
pass
old_level = logger.level
logger.setLevel(level)
if handler:
logger.addHandler(handler)
yield
if level is not None:
logger.setLevel(old_level)
if handler:
logger.removeHandler(handler)
if close:
handler.close()
[docs]@contextmanager
def tracebacks(tracebacks=True):
"""Show or hide tracebacks in the encapsulated context.
Some environments, such as Jupyterlab, hide tracebacks by default. This context
allows tracebacks to be forcefully shown or hidden temporarily.
Parameters
----------
tracebacks : bool, optional
Show tracebacks. Defaults to True.
Examples
--------
Print tracebacks during a run.
>>> from finesse import Model
>>> from finesse.utilities import tracebacks
>>> model = Model()
>>> with tracebacks():
>>> model.parse("laser l1 L=1")
"""
from .. import show_tracebacks
old_tracebacks = show_tracebacks(tracebacks)
yield
show_tracebacks(old_tracebacks)
[docs]class FinesseStreamHandler(logging.StreamHandler):
"""Finesse stream handler.
This class provides a mechanism to exclude displayed log channels by wildcard. It is
otherwise identical to :class:`logging.StreamHandler`.
"""
[docs] def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__excluded_channels = None
self.reset_exclude_patterns()
[docs] def exclude(self, pattern):
self.__excluded_channels.add(pattern)
[docs] def reset_exclude_patterns(self):
"""Empty the configured log channel exclude patterns, and return what was
there."""
old_excludes = set(self.__excluded_channels or [])
self.__excluded_channels = set()
return old_excludes
[docs] def filter(self, record):
for pattern in self.__excluded_channels:
if fnmatch(record.name, pattern):
# Skip the record.
return
return record