22from typing import Any , Optional , Tuple , Union , Iterator , Iterable , List , Sequence
33from .logging import Logger
44
5+ from .lrucache import LRUCache
56
67FLEX_KEY = str | int | None
78_RaiseKeyError = object () # singleton for no-default behavior
@@ -99,6 +100,7 @@ class BaseStoredObject:
99100 _key : FLEX_KEY = None
100101 _parent : Optional ['BaseStoredObject' ] = None
101102 _lock : threading .RLock = None
103+ _path = None
102104
103105 def set_db (self , db ):
104106 self ._db = db
@@ -116,24 +118,29 @@ def lock(self):
116118
117119 @property
118120 def path (self ) -> Sequence [FLEX_KEY ] | None :
121+ if self ._path is not None :
122+ return self ._path
119123 # return None iff we are pruned from root
120124 x = self
121125 s = [x ._key ]
122126 while x ._parent is not None :
123127 x = x ._parent
124128 s = [x ._key ] + s
125129 if x ._key != '' :
126- return None
127- assert self ._db is not None
128- return s
130+ s = []
131+ #return None
132+ else :
133+ assert self ._db is not None
134+ self ._path = s
135+ return self ._path
129136
130137
131138class StoredObject (BaseStoredObject ):
132139 """for attr.s objects """
133140
134141 def __setattr__ (self , key : str , value ):
135142 assert isinstance (key , str ), repr (key )
136- if self . path and not key .startswith ('_' ):
143+ if not key .startswith ('_' ) and self . path :
137144 if value != getattr (self , key ):
138145 self ._db .replace (self .path , key , value )
139146 object .__setattr__ (self , key , value )
@@ -163,6 +170,7 @@ def __init__(self, db: BaseDB, key: FLEX_KEY, parent):
163170 self ._parent = parent
164171 self ._key = key_to_str (key )
165172 self ._should_convert = True
173+ self ._cache = LRUCache (maxsize = 100 )
166174
167175 def should_convert (self ):
168176 return self ._parent ._should_convert if self ._parent is not None else self ._should_convert
@@ -176,6 +184,8 @@ def get_dict(self, key) -> 'StoredDict':
176184
177185 def __getitem__ (self , key : FLEX_KEY ) -> Any :
178186 key = key_to_str (key )
187+ if cached := self ._cache .get (key ):
188+ return cached
179189 value = self ._db .get (self .path , key )
180190 if not self .should_convert ():
181191 if isinstance (value , dict ):
@@ -187,9 +197,10 @@ def __getitem__(self, key: FLEX_KEY) -> Any:
187197 value .set_db (self ._db )
188198 value .set_parent (key = key , parent = self )
189199 elif isinstance (value , list ):
190- return StoredList (self ._db , key = key , parent = self )
200+ value = StoredList (self ._db , key = key , parent = self )
191201 elif isinstance (value , dict ):
192- return StoredDict (self ._db , key = key , parent = self )
202+ value = StoredDict (self ._db , key = key , parent = self )
203+ self ._cache [key ] = value
193204 return value
194205
195206 def __setitem__ (self , key : FLEX_KEY , value : Any ) -> None :
@@ -201,10 +212,14 @@ def __setitem__(self, key: FLEX_KEY, value: Any) -> None:
201212 if isinstance (value , StoredDict ):
202213 raise Exception ('trying to set StoredDict' )
203214 self ._db .put (self .path , key , value )
215+ if key in self ._cache :
216+ del self ._cache [key ]
204217
205218 def __delitem__ (self , key : FLEX_KEY ) -> None :
206219 key = key_to_str (key )
207220 self ._db .remove (self .path , key )
221+ if key in self ._cache :
222+ del self ._cache [key ]
208223
209224 def __iter__ (self ) -> Iterator [str ]:
210225 return self ._db .iter_keys (self .path )
@@ -239,6 +254,7 @@ def get(self, key: FLEX_KEY, default: Any = None) -> Any:
239254
240255 def clear (self ) -> None :
241256 self ._db .clear (self .path )
257+ self ._cache .clear ()
242258
243259 def pop (self , key : FLEX_KEY , default : Any = _RaiseKeyError ) -> Any :
244260 try :
0 commit comments