@@ -89,23 +89,37 @@ class StorageEncryptionVersion(IntEnum):
8989
9090
9191def json_default (obj ):
92+ """Convert object to simple python types.
93+ The result must be json serializable.
94+ """
9295 if isinstance (obj , (set , frozenset )):
93- return list (obj )
96+ return [ json_default ( x ) for x in list (obj )]
9497 if isinstance (obj , bytes ):
9598 return obj .hex ()
9699 if hasattr (obj , 'as_str' ) and callable (obj .as_str ):
97100 return obj .as_str ()
98101 if hasattr (obj , 'as_dict' ) and callable (obj .as_dict ):
99- return obj .as_dict ()
102+ obj = obj .as_dict ()
103+ return dict ([(key_to_str (k ), json_default (v )) for k , v in obj .items ()])
100104 if hasattr (obj , 'as_tuple' ) and callable (obj .as_tuple ):
101- return obj .as_tuple ()
105+ obj = obj .as_tuple ()
106+ return tuple ([json_default (x ) for x in list (obj )])
107+ if hasattr (obj , 'to_json' ) and callable (obj .to_json ): # TxOutpoint
108+ return obj .to_json ()
109+ if isinstance (obj , dict ):
110+ return dict ([(key_to_str (k ), json_default (v )) for k , v in obj .items ()])
111+ if isinstance (obj , list ):
112+ return [json_default (x ) for x in obj ]
113+ if isinstance (obj , tuple ):
114+ return tuple ([json_default (x ) for x in list (obj )])
102115 if isinstance (obj , StoredDict ):
103- return dict ( obj )
116+ raise Exception ( 'trying to convert StoredDict' )
104117 if isinstance (obj , StoredList ):
105- return list ( obj )
118+ raise Exception ( 'trying to convert StoredList' )
106119 return obj
107120
108121
122+
109123class BaseDB (Logger ):
110124
111125 def __init__ (self , path ):
@@ -136,6 +150,7 @@ def _convert_dict_key(path: List[str], key: str) -> _FLEX_KEY:
136150 assert isinstance (key , _FLEX_KEY ), f"unexpected type for { key = !r} at { path = } "
137151 return key
138152
153+
139154def _convert_dict_value (path : List [str ], v ) -> Any :
140155 assert all (isinstance (x , _FLEX_KEY ) for x in path )
141156 n = len (path )
@@ -145,8 +160,13 @@ def _convert_dict_value(path: List[str], v) -> Any:
145160 if reg :
146161 _type , constructor = reg
147162 if _type == dict :
163+ if isinstance (v , StoredDict ):
164+ v = v ._dump ()
165+ #v = {k:_convert_dict_value(path + [k], x) for k, x in v.items()}
148166 v = constructor (** v )
149167 elif _type == tuple :
168+ if isinstance (v , StoredList ):
169+ v = v ._dump ()
150170 v = constructor (* v )
151171 else :
152172 v = constructor (v )
@@ -207,7 +227,7 @@ def __setattr__(self, key: str, value):
207227 assert isinstance (key , str ), repr (key )
208228 if not key .startswith ('_' ) and self .path :
209229 if value != getattr (self , key ):
210- self ._db .replace (self .path , key , value )
230+ self ._db .replace (self .path , key , json_default ( value ) )
211231 object .__setattr__ (self , key , value )
212232
213233 def as_dict (self ):
@@ -218,7 +238,6 @@ def as_dict(self):
218238 return d
219239
220240
221-
222241class StoredDict (BaseStoredObject ):
223242 """
224243 dict-like object that queries the DB
@@ -255,14 +274,15 @@ def _dump(self) -> dict:
255274 def __getitem__ (self , key : _FLEX_KEY ) -> Any :
256275 key = key_to_str (key ) # fixme: assumes that db keys are str
257276 value = self ._db .get (self .path , key )
277+ value = self ._to_stored_dict_or_list (key , value )
258278 if not self .should_convert ():
259- return self . _to_stored_dict_or_list ( key , value )
279+ return value
260280 value = _convert_dict_value (self .path + [key ], value )
261281 # set db for StoredObject, because it is not set in the constructor
262282 if isinstance (value , StoredObject ):
263283 value .set_db (self ._db )
264284 value .set_parent (key = key , parent = self )
265- return self . _to_stored_dict_or_list ( key , value )
285+ return value
266286
267287 def __setitem__ (self , key : _FLEX_KEY , value : Any ) -> None :
268288 key = key_to_str (key )
@@ -273,6 +293,8 @@ def __setitem__(self, key: _FLEX_KEY, value: Any) -> None:
273293 if isinstance (value , StoredDict ):
274294 value = value ._dump ()
275295 assert isinstance (value , dict )
296+ # convert here
297+ value = json_default (value )
276298 self ._db .put (self .path , key , value )
277299
278300 def __delitem__ (self , key : _FLEX_KEY ) -> None :
@@ -365,9 +387,9 @@ def should_convert(self):
365387 def _get_list_item (self , key : int ):
366388 key = int (key )
367389 value = self ._db .get (self .path , key )
390+ value = self ._to_stored_dict_or_list (key , value )
368391 if self .should_convert ():
369392 value = _convert_dict_value (self .path + [key ], value )
370- value = self ._to_stored_dict_or_list (key , value )
371393 return value
372394
373395 def __getitem__ (self , s : slice ) -> Any :
@@ -390,17 +412,20 @@ def __iter__(self) -> Iterator[str]:
390412 for i in range (self ._db .list_len (self .path )):
391413 yield self ._get_list_item (i )
392414
393- def append (self , item ):
394- self ._db .list_append (self .path , item )
415+ def append (self , value ):
416+ value = json_default (value )
417+ self ._db .list_append (self .path , value )
395418
396419 def clear (self ):
397420 self ._db .list_clear (self .path )
398421 assert len (self ) == 0
399422
400423 def index (self , item ) -> int :
424+ item = json_default (item )
401425 return self ._db .list_index (self .path , item )
402426
403427 def remove (self , item ):
428+ item = json_default (item )
404429 self ._db .list_remove (self .path , item )
405430
406431 def _dump (self ) -> list :
0 commit comments