Source code for fridge
__all__ = ['Fridge']
import json
import errno
import functools
[docs]class Fridge(dict):
"""
Fridge is a subclass of :class:`dict` and thus fully conforms to its interface.
Fridge keeps an open file until it's closed, so you have to call :meth:`close`
when you're done using it.
Fridge implements :meth:`__enter__` and :meth:`__exit__` so you can use
`with` statement.
:param path: a path to a file that will be used to load and save the data
:param file: a file object that will be used to load and save the data.
This file object in not closed by fridge automatically.
`path` and `file` arguments are mutually exclusive
"""
def __init__(self, path=None, file=None):
if path is None and file is None:
raise ValueError('No path or file specified')
elif path is not None and file is not None:
raise ValueError('Only path or only file can be passed')
if file is not None:
self.file = file
self.close_file = False
else:
try:
self.file = open(path, 'r+')
except IOError as e:
if e.errno == errno.ENOENT:
self.file=open(path, 'w+')
else:
raise
self.close_file = True
#: True after :meth:`close` is called, False otherwise.
self.closed = False
self.load()
def check_open(self):
if self.closed:
raise ValueError('Operation on a closed fridge object')
[docs] def load(self):
"""
Force reloading the data from the file.
All data in the in-memory dictionary is lost.
This method is called automatically by the constructor, normally you
don't need to call it.
"""
self.check_open()
try:
data = json.load(self.file)
except ValueError:
data = {}
if not isinstance(data, dict):
raise ValueError('Root JSON type must be dictionary')
self.clear()
self.update(data)
[docs] def save(self):
"""
Force saving the dictionary to the file.
All data in the file is lost.
This method is called automatically by :meth:`close`.
"""
self.check_open()
self.file.truncate(0)
self.file.seek(0)
json.dump(self, self.file)
[docs] def close(self):
"""
Close the fridge.
Calls :meth:`save` and closes the underlying file object unless
an already open file was passed to the constructor.
This method has no effect if the file is already closed.
After the fridge is closed :meth:`save` and :meth:`load` will raise an exception
but you will still be able to use it as an ordinary dictionary.
"""
if not self.closed:
self.save()
if self.close_file:
self.file.close()
self.closed = True
def __enter__(self):
return self
def __exit__(self, *_):
self.close()
return False
def __del__(self):
self.close()