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()