Source code for jgdv.logging.decorators

  1#!/usr/bin/env python3
  2"""
  3
  4
  5
  6"""
  7# Import:
  8from __future__ import annotations
  9
 10# ##-- stdlib imports
 11import datetime
 12import enum
 13import functools as ftz
 14import itertools as itz
 15import logging as logmod
 16import pathlib as pl
 17import re
 18import time
 19import weakref
 20from uuid import UUID, uuid1
 21# ##-- end stdlib imports
 22
 23from jgdv import Maybe
 24from jgdv.decorators import Decorator
 25
 26from ._interface import Logger, LOGDEC_PRE
 27
 28# ##-- types
 29# isort: off
 30# General
 31import abc
 32import collections.abc
 33import typing
 34import types
 35from typing import cast, assert_type, assert_never
 36from typing import Generic, NewType, Never
 37from typing import no_type_check, final, override, overload
 38# Protocols and Interfaces:
 39from typing import Protocol, runtime_checkable
 40# isort: on
 41# ##-- end types
 42
 43# ##-- type checking
 44# isort: off
 45if typing.TYPE_CHECKING:
 46    from typing import Final, ClassVar, Any, Self
 47    from typing import Literal, LiteralString
 48    from typing import TypeGuard, Concatenate
 49    from collections.abc import Iterable, Iterator, Callable, Generator
 50    from collections.abc import Sequence, Mapping, MutableMapping, Hashable
 51
 52    from jgdv import Maybe, Lambda
 53## isort: on
 54# ##-- end type checking
 55
 56##-- logging
 57logging = logmod.getLogger(__name__)
 58##-- end logging
 59
 60# Global Vars:
 61
 62# Body:
 63
[docs] 64class LogCall(Decorator): 65 """ A Decorator for announcing the entry/exit of a function call 66 67 eg: 68 @LogCall(enter="Entering", exit="Exiting", level=logmod.INFO) 69 def a_func()... 70 """ 71 72 def __init__(self, enter:Maybe[str|Lambda]=None, exit:Maybe[str|Lambda]=None, level:int|str=logmod.INFO, logger:Maybe[Logger]=None) -> None: # noqa: A002 73 super().__init__(prefix=LOGDEC_PRE) 74 self._logger = logger or logging 75 self._enter_msg = enter 76 self._exit_msg = exit 77 match level: 78 case str(): 79 self._level = logmod.getLevelNamesMapping().get(level, logmod.INFO) 80 case int(): 81 self._level = level 82 case _: 83 raise ValueError(level) 84
[docs] 85 def _log_msg(self, msg:Maybe[str|Lambda], fn:Callable, args:Iterable, **kwargs:Any) -> None: # noqa: ANN401 86 match msg: 87 case None: 88 return None 89 case types.FunctionType(): 90 msg = msg(fn, *args, **kwargs) 91 case str(): 92 pass 93 case _: 94 raise TypeError(msg) 95 96 self._logger.log(self._level, msg)
97
[docs] 98 def _wrap_method[X, **I, O](self, fn:Callable[Concatenate[X, I],O]) -> Callable[Concatenate[X, I],O]: 99 100 def basic_wrapper(_self:X, *args:I.args, **kwargs:I.kwargs) -> O: 101 self._log_msg(self._enter_msg, fn, args, obj=_self, **kwargs) 102 ret_val = fn(_self, *args, **kwargs) 103 self._log_msg(self._exit_msg, fn, args, obj=_self, returned=ret_val, **kwargs) 104 return ret_val 105 106 return basic_wrapper
107
[docs] 108 def _wrap_fn[**I, O](self, fn:Callable[I, O]) -> Callable[I, O]: 109 110 def basic_wrapper(*args:I.args, **kwargs:I.kwargs) -> O: 111 self._log_msg(self._enter_msg, fn, args, obj=None, **kwargs) 112 ret_val = fn(*args, **kwargs) 113 self._log_msg(self._exit_msg, fn, args, obj=None, returned=ret_val, **kwargs) 114 return ret_val 115 116 return basic_wrapper
117
[docs] 118 def _wrap_class[T](self, cls:type[T]) -> type[T]: 119 raise NotImplementedError()