1#!/usr/bin/env python3
2"""
3
4See EOF for license/metadata/notes as applicable
5"""
6
7##-- builtin imports
8from __future__ import annotations
9
10import datetime
11import enum
12import functools as ftz
13import itertools as itz
14import logging as logmod
15import pathlib as pl
16import re
17import time
18import weakref
19from uuid import UUID, uuid1
20
21##-- end builtin imports
22
23# ##-- types
24# isort: off
25# General
26import abc
27import collections.abc
28import typing
29import types
30from typing import cast, assert_type, assert_never
31from typing import Generic, NewType, Never
32from typing import no_type_check, final, override, overload
33# Protocols and Interfaces:
34from typing import Protocol, runtime_checkable
35# isort: on
36# ##-- end types
37
38# ##-- type checking
39# isort: off
40if typing.TYPE_CHECKING:
41 from typing import Final, ClassVar, Any, Self
42 from typing import Literal, LiteralString
43 from typing import TypeGuard
44 from collections.abc import Iterable, Iterator, Callable, Generator
45 from collections.abc import Sequence, Mapping, MutableMapping, Hashable
46
47 from jgdv import Maybe, Func
48## isort: on
49# ##-- end type checking
50
51##-- logging
52logging = logmod.getLogger(__name__)
53##-- end logging
54
55from ._interface import DEL_LOG_K
56from jgdv.decorators._interface import ClsDecorator_p
57
58DEBUG_DESTRUCT_ON = False
59
[docs]
60def _log_del(self:Any) -> None: # noqa: ANN401
61 """ standalone del logging """
62 logging.warning("Deleting: %s", self)
63
[docs]
64def _decorate_del(fn:Func[..., None]) -> Func[..., None]:
65 """ wraps existing del method """
66 @ftz.wraps(fn)
67 def _wrapped(self, *args:Any) -> None: # noqa: ANN001, ANN401
68 logging.warning("Deleting: %s", self)
69 fn(*args)
70
71 return _wrapped
72
[docs]
73def LogDel(cls:type) -> type: # noqa: N802
74 """
75 A Class Decorator, attaches a debugging statement to the object destructor
76 To activate, add classvar of {jgdv.debugging._interface.DEL_LOG_K} = True
77 to the class.
78 """
79 match (getattr(cls, DEL_LOG_K, default=False), # type: ignore[call-overload]
80 hasattr(cls, "__del__")):
81 case (False, _):
82 pass
83 case (True, True):
84 assert(hasattr(cls, "__del__"))
85 setattr(cls, "__del__", _decorate_del(cls.__del__)) # noqa: B010
86 case (True, False):
87 setattr(cls, "__del__", _log_del) # noqa: B010
88 return cls
89##--|
[docs]
90class LogDestruction(ClsDecorator_p):
91 """
92 A Decorator to log when instances of a class are deleted
93 """
94
[docs]
95 def _debug_del(self) -> None:
96 """ standalone del logging """
97 logging.warning("Deleting: %s", self)
98
[docs]
99 def _debug_del_dec(self, fn:Func) -> Callable:
100 """ wraps existing del method """
101
102 def _wrapped(_self:object, *args, **kwargs) -> None:
103 logging.warning("Deleting: %s", _self)
104 fn(_self, *args, **kwargs)
105
106 return _wrapped
107
108 @override
109 def __call__[T](self, cls:type[T]) -> type[T]:
110 """
111 A Class Decorator, attaches a debugging statement to the object destructor
112 """
113 match (DEBUG_DESTRUCT_ON, hasattr(cls, "__del__")):
114 case (False, _):
115 pass
116 case (True, True):
117 setattr(cls, "__del__", self._debug_del_dec(cls.__del__)) # type: ignore[attr-defined] # noqa: B010
118 case (True, False):
119 setattr(cls, "__del__", self._debug_del) # noqa: B010
120 return cls