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

74 statements  

« prev     ^ index     » next       coverage.py v7.3.4, created at 2023-12-20 14:17 -0500

1from dataclasses import dataclass, field 

2from typing import Literal, Callable 

3import wrapt 

4 

5 

6@dataclass 

7class 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 

31class 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 

45class DictStorage(dict): 

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

47 __getattr__ = dict.get 

48 __setattr__ = dict.__setitem__ 

49 __delattr__ = dict.__delitem__ 

50 

51 

52class 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 

88class 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