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.

#!/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):
    # from https://writeonly.wordpress.com/2009/05/07/deepcopy-is-a-pig-for-simple-data
    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):
        # From https://stackoverflow.com/a/12507546
        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()

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 562 Oct-02-2023, 09:21 AM
Last Post: deanhystad
  return vs. print in nested function example Mark17 4 1,674 Jan-04-2022, 06:02 PM
Last Post: jefsummers
  Exit function from nested function based on user input Turtle 5 2,859 Oct-10-2021, 12:55 AM
Last Post: Turtle
Question Stopping a parent function from a nested function? wallgraffiti 1 3,621 May-02-2021, 12:21 PM
Last Post: Gribouillis
  Nested function problem chipx 8 3,414 Oct-21-2020, 11:56 PM
Last Post: jefsummers
  accessing a second level nested function varsh 3 2,437 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 4,333 Jun-24-2020, 04:05 AM
Last Post: deanhystad
  Nested Recursive Function not Returning Etotheitau 2 2,218 May-09-2020, 06:09 PM
Last Post: Etotheitau
  Custom Function to Return Database Values rm78 0 1,765 Sep-05-2019, 01:01 PM
Last Post: rm78
  unittest for nested function fstefanov 2 4,552 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