Python Forum
Repair function for a nested database
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Repair function for a nested database
#1
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"))
Reply
#2
I solved it with a recursive function and setdefault()

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
class Database():
    def __init__(self):
        if os.path.isfile(PREFERENCES_FILE) and os.stat(PREFERENCES_FILE).st_size > 0:
            self.load()
            self.validate()
        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, db, default=PREFERENCES_DEFAULT):
        for key in default:
            if isinstance(default[key], dict):
                self.repair(db[key], default[key])
            else:
                db.setdefault(key, default[key])
 
    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 validate(self):
        old = dict(self.db)
        self.repair(self.db)
        if not self.db == old:
            print("Repaired missing keys in database")
            self.save()
 
 
with open(PREFERENCES_FILE, "w") as f:
    f.write(json.dumps(PREFERENCES_BROKEN, indent=2, sort_keys=False))
    #f.write(json.dumps(PREFERENCES_DEFAULT, indent=2, sort_keys=False))
 
db = Database()
print(db.get("Key A", "sub key", "value"))
print(db.get("Key B", "value"))
print(db.get("Key C"))
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  nested function return MHGhonaim 2 1,693 Oct-02-2023, 09:21 AM
Last Post: deanhystad
  return vs. print in nested function example Mark17 4 2,836 Jan-04-2022, 06:02 PM
Last Post: jefsummers
  Exit function from nested function based on user input Turtle 5 4,698 Oct-10-2021, 12:55 AM
Last Post: Turtle
Question Stopping a parent function from a nested function? wallgraffiti 1 4,748 May-02-2021, 12:21 PM
Last Post: Gribouillis
  Nested function problem chipx 8 5,331 Oct-21-2020, 11:56 PM
Last Post: jefsummers
  accessing a second level nested function varsh 3 3,274 Aug-13-2020, 06:57 AM
Last Post: varsh
  How to make this function general to create binary numbers? (many nested for loops) dospina 4 6,498 Jun-24-2020, 04:05 AM
Last Post: deanhystad
  Nested Recursive Function not Returning Etotheitau 2 3,259 May-09-2020, 06:09 PM
Last Post: Etotheitau
  Custom Function to Return Database Values rm78 0 2,208 Sep-05-2019, 01:01 PM
Last Post: rm78
  unittest for nested function fstefanov 2 5,811 Oct-15-2018, 01:43 PM
Last Post: jdjeffers

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020