Skip to content

Instantly share code, notes, and snippets.

@RhetTbull
Last active September 10, 2019 11:41
Show Gist options
  • Save RhetTbull/de96f9d8b6cb5209f220ca37d1f90d96 to your computer and use it in GitHub Desktop.
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
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