Source code for debug_this

"""Python debug logging helpers."""
from __future__ import annotations

import functools
import inspect
import logging
from typing import Any
from typing import Callable

module_logger = logging.getLogger(__name__)

__version__ = "1.0.2"


[docs]def fucking_function(*args_d: Any, **kwargs_d: Any) -> Any: """Log the execution of an unfriendly function. Parameters ---------- logger: logging.Logger, optional Specify a logger instead of the default one. print_parent: bool, optional Print which function has called the decorated function. Examples -------- >>> import logging >>> import debug_this >>> >>> logging.basicConfig(level=logging.DEBUG) >>> >>> logger = logging.getLogger(__name__) >>> >>> @debug_this.fucking_function(logger) >>> def example_function(): ... logger.info("This is an example function") >>> >>> example_function() DEBUG:__main__: >>> example_function INFO:__main__:This is an example function DEBUG:__main__: <<< example_function """ logger: logging.Logger | None = kwargs_d.get("logger", None) print_parent: bool | None = kwargs_d.get("print_parent", None) if len(args_d) >= 1 and isinstance(args_d[0], logging.Logger): logger = args_d[0] if len(args_d) >= 2 and isinstance(args_d[1], bool): print_parent = args_d[1] if logger is None: logger = module_logger def fucking_function_decorator(func: Callable[..., Any]) -> Callable[..., Any]: if not inspect.isfunction(func): raise TypeError("This decorator must be used on a function") @functools.wraps(func) def debug_this_fucking_function(*args_f: Any, **kwargs_f: Any) -> Any: stack = [ x.function for x in inspect.stack() if x[3] != "debug_this_fucking_function" ] stack_level = len(stack) prefix = " " * stack_level # makes mypy happy assert isinstance(logger, logging.Logger) # nosec assert_used if print_parent is True: logger.debug( "%s>>> %s (parent: %s)", prefix, func.__qualname__, stack[0] ) else: logger.debug("%s>>> %s", prefix, func.__qualname__) value = func(*args_f, **kwargs_f) logger.debug("%s<<< %s", prefix, func.__qualname__) return value return debug_this_fucking_function if len(args_d) == 1 and callable(args_d[0]): return fucking_function_decorator(args_d[0]) return fucking_function_decorator
[docs]def fucking_class(*args_d: Any, **kwargs_d: Any) -> Any: """Log the execution of an unfriendly class. This decorator configures the :obj:`fucking_function` decorator on each method of the specified class. All available parameters for the :obj:`fucking_function` decorator can be used. See Also -------- fucking_function Examples -------- >>> import logging >>> import debug_this >>> >>> logging.basicConfig(level=logging.DEBUG) >>> >>> logger = logging.getLogger(__name__) >>> >>> @debug_this.fucking_class(logger) >>> class ExampleClass: ... def __init__(self): ... logger.info("Example constructor") ... ... def example_method(self): ... logger.info("Example method") >>> >>> e = ExampleClass() >>> e.example_method() DEBUG:__main__: >>> ExampleClass.__init__ INFO:__main__:Example constructor DEBUG:__main__: <<< ExampleClass.__init__ DEBUG:__main__: >>> ExampleClass.example_method INFO:__main__:Example method DEBUG:__main__: <<< ExampleClass.example_method """ def fucking_class_decorator(cls: type) -> type: if not inspect.isclass(cls): raise TypeError("This decorator must be used on a class") for name, method in inspect.getmembers(cls, inspect.isfunction): setattr(cls, name, fucking_function(*args_d, **kwargs_d)(method)) return cls if len(args_d) == 1 and callable(args_d[0]): cls = args_d[0] args_d = args_d[1:] return fucking_class_decorator(cls) return fucking_class_decorator