Like other things posted here without notices to the contrary, this
code is in the public domain.
#!/usr/bin/python
# I'd use the MetaPy dict mixin if I had one handy
"""A crude dictionary backed up by log-structured file storage.
This hack is just a proof of concept. The concept is that it's really
easy to store data structures as the sequence of updates to the data
structure --- "logical logging" in OLTP/RDBMS-speak. In theory this
could be the fastest possible way to store things, because your
storing is all sequential --- only your retrieval needs to be random.
In this implementation, it's far from fast, because it flushes the
file every time it writes a record. It's also far from robust,
because it doesn't fsync().
"""
from __future__ import generators, nested_scopes
import os, urllib
quote = urllib.quote_plus
unquote = urllib.unquote_plus
def ok(a, b): assert a == b, (a, b)
class wrong_exception(AssertionError): pass
def assert_raises(subr, exc):
try:
subr()
except exc:
return 1
except:
exc_type, exc_value, tb = sys.exc_info()
raise wrong_exception, wrong_exception(exc_value), tb
else:
raise wrong_exception("no %s exception" % exc)
class logdict:
def __init__(self, filename):
self.file = file(filename, 'a+')
self.contents = {}
self.file.seek(0)
deltoken = 'del '
for line in self.file:
assert line.endswith('\n'), line
line = line[:-1]
if '=' in line:
(name, value) = line.split('=')
self.contents[unquote(name)] = unquote(value)
elif line.startswith(deltoken):
del self.contents[unquote(line[len(deltoken):])]
else:
assert 0, line
def log(self, msg):
self.file.write(msg)
self.file.flush()
def __getitem__(self, name): return self.contents[name]
def __setitem__(self, name, value):
self.contents[name] = value
self.log('%s=%s\n' % (quote(name), quote(value)))
def __delitem__(self, name):
del self.contents[name]
self.log('del %s\n' % quote(name))
def iterkeys(self): return self.contents.iterkeys()
def test_basics(dicttype):
os.unlink('tmp.hash')
foo = dicttype('tmp.hash')
assert_raises(lambda: foo['bar'], KeyError)
foo['bar'] = 'baz'
ok(foo['bar'], 'baz')
foo['glork'] = 'glick'
ok(foo['glork'], 'glick')
ok(foo['bar'], 'baz')
del foo['bar']
ok(foo['glork'], 'glick')
assert_raises(lambda: foo['bar'], KeyError)
foo['del \n='] = 'x=y\nz'
ok(foo['del \n='], 'x=y\nz')
def test_persistence(dicttype):
foo = dicttype('tmp.hash')
ok(foo['glork'], 'glick')
ok(foo['del \n='], 'x=y\nz')
def test_dicttype(dicttype):
test_basics(dicttype)
test_persistence(dicttype)
def test():
test_dicttype(logdict)
test()