I made a class handle the preference database of my apps. The problem is that as new features are added, using the get() method on the new keys raise KeyErrors. To make this foolproof, I would like to add a repair() method, which would be called on init if the keys of the current database does not match the default keys. I was wondering if there would be an easy way to do that, and that would be reliable with various nesting levels.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
#!/usr/bin/python3 import json import os LOCAL_DIR = os.path.dirname(os.path.realpath(__file__)) + '/' PREFERENCES_FILE = LOCAL_DIR + "preferences.json" PREFERENCES_DEFAULT = \ { 'Key A' : { 'sub key' : { 'value' : 0 , } }, 'Key B' : { 'value' : 1 }, 'Key C' : 2 , } PREFERENCES_BROKEN = \ { 'Key A' : { 'sub key' : { } }, 'Key B' : { }, } def copyDict(dictionnary): out = dict ().fromkeys(dictionnary) for key, value in dictionnary.items(): try : out[key] = value.copy() # dicts, sets except AttributeError: try : out[key] = value[:] # lists, tuples, strings, unicode except TypeError: out[key] = value # ints return out class Database(): def __init__( self ): if os.path.isfile(PREFERENCES_FILE) and os.stat(PREFERENCES_FILE).st_size > 0 : self .load() if not self .verify(): self .repair() else : self .db = copyDict(PREFERENCES_DEFAULT) with open (PREFERENCES_FILE, "w" ) as f: f.write(json.dumps( self .db, indent = 2 , sort_keys = False )) print ( "Created preferences file" ) def get( self , * keys, db = None ): if not db: db = self .db for key in keys: db = db[key] return db def load( self ): with open (PREFERENCES_FILE, "r" ) as f: self .db = json.load(f) print ( "Loaded preferences database" ) def repair( self ): #self.save() pass def save( self ): with open (PREFERENCES_FILE, "w" ) as f: f.write(json.dumps( self .db, indent = 2 , sort_keys = False )) print ( "Saved preferences database" ) def verify( self ): db = [x for x in self .walk( self .db)] default = [x for x in self .walk(PREFERENCES_DEFAULT)] return db = = default def walk( self , indict, pre = None ): pre = pre[:] if pre else [] if isinstance (indict, dict ): for key, value in indict.items(): if isinstance (value, dict ): for d in self .walk(value, [key] + pre): yield d elif isinstance (value, list ) or isinstance (value, tuple ): for v in value: for d in self .walk(v, [key] + pre): yield d else : yield pre + [key] else : yield indict # Working database example if os.path.isfile(PREFERENCES_FILE): os.remove(PREFERENCES_FILE) db = Database() print ( "# GOOD DATABASE" ) print (db.get( "Key A" , "sub key" , "value" )) print (db.get( "Key B" , "value" )) print (db.get( "Key C" )) print () # Broken database example with open (PREFERENCES_FILE, "w" ) as f: f.write(json.dumps(PREFERENCES_BROKEN, indent = 2 , sort_keys = False )) db = Database() print ( "# BROKEN DATABASE" ) print (db.get( "Key A" , "sub key" , "value" )) print (db.get( "Key B" , "value" )) print (db.get( "Key C" )) |