Logging
Thin wrapper around the shared application logger.
The class provides a simple console_print method used by the GUI to
write messages to the configured logger identified by constants.LOG_NAME.
Source code in utils/logging.py
class ConsoleLogging:
"""Thin wrapper around the shared application logger.
The class provides a simple ``console_print`` method used by the GUI to
write messages to the configured logger identified by ``constants.LOG_NAME``.
"""
def __init__(self):
self.logger = logging.getLogger(constants.LOG_NAME)
def console_print(self, level=10, message=None):
self.logger.log(level, message)
Logging decorator usable as
@with_logging @with_logging() @with_logging(log_level=...)
Source code in utils/logging.py
def with_logging(func=None, *, log_level: int = 10):
"""
Logging decorator usable as:
@with_logging
@with_logging()
@with_logging(log_level=...)
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
logger = logging.getLogger(constants.LOG_NAME)
logger.log(log_level, f"Calling {func.__qualname__}")
value = func(*args, **kwargs)
logger.log(log_level, f"Finished calling {func.__qualname__}")
return value
return wrapper
# CASE 1: @with_logging → func is the decorated function
if callable(func):
return decorator(func)
# CASE 2: @with_logging(...) → func is None, return real decorator
return decorator
Class decorator that wraps explicitly defined methods on a class
with with_logging, without touching Qt signals or other descriptors.
- Only items in
cls.__dict__that are real functions (or class/staticmethods) are wrapped. - By default, public methods (no leading underscore) are wrapped.
- You can narrow/adjust behaviour with
include/exclude.
Source code in utils/logging.py
def decorate_class_with_logging(
log_level: int = 10,
include: set[str] | None = None,
exclude: set[str] | None = None,
):
"""
Class decorator that wraps explicitly defined *methods* on a class
with `with_logging`, without touching Qt signals or other descriptors.
- Only items in `cls.__dict__` that are real functions (or class/staticmethods)
are wrapped.
- By default, public methods (no leading underscore) are wrapped.
- You can narrow/adjust behaviour with `include` / `exclude`.
"""
def decorator(cls):
for name, attr in cls.__dict__.items():
# unwrap classmethod / staticmethod
is_classmethod = isinstance(attr, classmethod)
is_staticmethod = isinstance(attr, staticmethod)
func = attr.__func__ if (is_classmethod or is_staticmethod) else attr
# only wrap *real* functions, not pyqtSignal, properties, etc.
if not isinstance(func, FunctionType):
continue
# skip dunder + private-ish names
if name.startswith("__") and name.endswith("__"):
continue
if name.startswith("_"):
continue
# optional filters
if include is not None and name not in include:
continue
if exclude is not None and name in exclude:
continue
# your existing with_logging(func, log_level=...)
wrapped_func = with_logging(func, log_level=log_level)
# re-wrap as classmethod/staticmethod if needed
if is_classmethod:
wrapped_attr = classmethod(wrapped_func)
elif is_staticmethod:
wrapped_attr = staticmethod(wrapped_func)
else:
wrapped_attr = wrapped_func
setattr(cls, name, wrapped_attr)
return cls
return decorator