Coverage for src\zapy\store\manager.py: 38%

1 from dataclasses import dataclass, field

2 from typing import Literal, Callable

3 import wrapt

4

5

6@dataclass

7 class StorageLog:

8 type: Literal['set', 'get']

9 instance: 'Proxy'

10 name: str

11

12 @property

13 def name_chain(self):

14 count = 0

15 name_stack = [self.name]

16 instance = self.instance

17 while instance is not None:

18 if instance._self_field_name:

19 name_stack.append(instance._self_field_name)

20 instance = instance._self_parent

21 count += 1

22 if count > 100:

23 raise RecursionError

24 return reversed(name_stack)

25

26 def __repr__(self) -> str:

27 return f'{self.type}: {".".join(self.name_chain)}'

28

29

30@dataclass

31 class Logger:

32 modifier: str

33 tags: list | None = field(default_factory=list)

34 _subscribers: list[Callable] = field(default_factory=list)

35

36 def log(self, type, instance, name):

37 log = StorageLog(type, instance, name)

38 for sub in self._subscribers:

39 sub(log)

40

41 def on_log(self, something: Callable):

42 self._subscribers.append(something)

43

44

45 class DictStorage(dict):

46 """dot.notation access to dictionary attributes"""

47 __getattr__ = dict.get

48 __setattr__ = dict.__setitem__

49 __delattr__ = dict.__delitem__

50

51

52 class Proxy(wrapt.ObjectProxy):

53

54 def __init__(self, value, logger: Logger, parent=None, field_name=None):

55 super().__init__(value)

56 self._self_logger = logger

57 self._self_parent = parent

58 self._self_field_name = field_name

59

60 def __setattr__(self, name, value):

61 if not name.startswith('_self_'):

62 logger = self._self_logger

63 logger.log('set', self, name)

64 return super().__setattr__(name, value)

65

66 def __getattr__(self, name: str):

67 val = super().__getattr__(name)

68 if isinstance(val, Proxy) or name.startswith('_self_'):

69 return val

70 logger = self._self_logger

71 logger.log('get', self, name)

72 return Proxy(val, logger, self, name)

73

74 def __setitem__(self, key, value):

75 logger = self._self_logger

76 logger.log('set', self, key)

77 return super().__setitem__(key, value)

78

79 def __getitem__(self, key):

80 val = super().__getitem__(key)

81 if isinstance(val, Proxy):

82 return val

83 logger = self._self_logger

84 logger.log('get', self, key)

85 return Proxy(val, logger, self, key)

86

87

88 class Store(DictStorage):

89

90 def create_usage(self, modifier, tags=None) -> 'Store':

91 logger = Logger(modifier, tags=tags)

92 def catch_log(log: StorageLog):

93 print(logger.modifier, logger.tags, log)

94 logger.on_log(catch_log)

95 return Proxy(self, logger)

96