Last active
September 10, 2019 11:41
-
-
Save RhetTbull/de96f9d8b6cb5209f220ca37d1f90d96 to your computer and use it in GitHub Desktop.
Python dictionary class that stores its data in a JSON file for persistence--useful for storing key/value pairs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import collections | |
import traceback | |
import json | |
import sys | |
import os.path | |
class JSONDict(collections.UserDict): | |
def __init__(self, file_name, autosave=False, default=None, data=None): | |
if file_name == None: | |
raise ValueError(+f"file_name ({file_name}) must be specified") | |
self.__autosave = autosave | |
self.__fname = file_name | |
self.__dirty = True # has data been changed and not yet saved? | |
self.data = {} | |
self.__loadstate(default=default) | |
if data is not None: | |
# initialize with data that was passed to __init__ | |
self.data = data | |
self.__savestate() | |
def save(self): | |
""" force state to be saved to file""" | |
self.__savestate(True) | |
def load(self): | |
""" force state to be loaded from file """ | |
self.__loadstate() | |
def delete(self): | |
""" delete all data as well as the json file on disk """ | |
self.data = {} | |
self.__dirty = False | |
try: | |
os.remove(self.__fname) | |
except Exception as e: | |
raise Exception( | |
f"Error deleting state from {self.__fname}: {e} {traceback.print_exc}" | |
) | |
@property | |
def autosave(self): | |
return self.__autosave | |
@autosave.setter | |
def autosave(self, val): | |
if isinstance(val, bool): | |
self.__autosave = val | |
# if autosave turned on, save now | |
if self.__autosave: | |
self.save() | |
else: | |
raise ValueError("autosave must be type bool") | |
@property | |
def file_name(self): | |
return self.__fname | |
@property | |
def isdirty(self): | |
return self.__dirty | |
def __loadstate(self, default=None): | |
""" loads state from json file | |
if file does not exist, initialize data, set to default if provided """ | |
if os.path.exists(self.__fname) and os.path.isfile(self.__fname): | |
""" File exists, load it """ | |
try: | |
with open(self.__fname, "r") as infile: | |
self.data = json.load(infile) | |
except Exception as e: | |
raise IOError( | |
f"Error loading state from {self.__fname}: {e} {traceback.print_exc}" | |
) | |
elif default is not None: | |
""" File does not exist but default data provided """ | |
self.data = default | |
else: | |
""" No file, no default data: init to empty dictionary """ | |
self.data = {} | |
self.__dirty = True | |
def __savestate(self, savenow=False): | |
# Don't save unless savenow = True | |
# if autosave is True, always save | |
# logger.debug(f"save") | |
savenow = savenow or self.__autosave | |
if not savenow: | |
return | |
try: | |
with open(self.__fname, "w") as outfile: | |
json.dump(self.data, outfile) | |
self.__dirty = False | |
except: | |
raise IOError(f"Error saving state to {self.__fname}") | |
def fromkeys(self, *args): | |
newdict = JSONDict( | |
file_name=self.__fname, autosave=self.__autosave, data=dict.fromkeys(*args) | |
) | |
newdict.__dirty = True | |
return newdict | |
def __getitem__(self, key): | |
return self.data[key] | |
def __setitem__(self, key, val): | |
self.data[key] = val | |
self.__dirty = True | |
self.__savestate() | |
def __iter__(self): | |
return iter(self.data) | |
def __len__(self): | |
return len(self.data) | |
def __delitem__(self, key): | |
del self.data[key] | |
self.__dirty = True | |
self.__savestate() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment