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
« 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
6@dataclass
7class StorageLog:
8 type: Literal['set', 'get']
9 instance: 'Proxy'
10 name: str
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)
26 def __repr__(self) -> str:
27 return f'{self.type}: {".".join(self.name_chain)}'
30@dataclass
31class Logger:
32 modifier: str
33 tags: list | None = field(default_factory=list)
34 _subscribers: list[Callable] = field(default_factory=list)
36 def log(self, type, instance, name):
37 log = StorageLog(type, instance, name)
38 for sub in self._subscribers:
39 sub(log)
41 def on_log(self, something: Callable):
42 self._subscribers.append(something)
45class DictStorage(dict):
46 """dot.notation access to dictionary attributes"""
47 __getattr__ = dict.get
48 __setattr__ = dict.__setitem__
49 __delattr__ = dict.__delitem__
52class Proxy(wrapt.ObjectProxy):
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
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)
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)
74 def __setitem__(self, key, value):
75 logger = self._self_logger
76 logger.log('set', self, key)
77 return super().__setitem__(key, value)
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)
88class Store(DictStorage):
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)