import shelve
class InvalidationError(Exception):
pass
class ShelfProxy(Proxy):
__slots__ = ["_key", "_shelf", "_invalidated"]
def __init__(self, obj, shelf, key):
Proxy.__init__(self, obj)
object.__setattr__(self, "_shelf", shelf)
object.__setattr__(self, "_key", key)
object.__setattr__(self, "_invalidated", False)
def __del__(self):
try:
sync_proxy(self)
except InvalidationError:
pass
class ShelfWrapper(object):
def __init__(self, shelf):
self.__shelf = shelf
self.__cache = {}
def __del__(self):
self.close()
def __getattr__(self, name):
return getattr(self.__shelf, name)
def __contains__(self, key):
return key in self.__shelf
def __len__(self, key):
return len(self.__shelf)
def __delitem__(self, key):
if key in self.__cache:
object.__setattr__(self.__cache[key], "_invalidated", True)
del self.__cache[key]
del self.__shelf[key]
def __getitem__(self, key):
try:
obj = self.__cache[key]
except KeyError:
self.__cache[key] = obj = ShelfProxy(self.__shelf[key], self.__shelf, key)
return obj
def __setitem__(self, key, value):
if key in self.__cache:
object.__setattr__(self.__cache[key], "_invalidated", True)
self.__cache[key] = ShelfProxy(value, self.__shelf, key)
self.__shelf[key] = value
def sync(self):
for obj in self.__cache.itervalues():
try:
sync_proxy(obj)
except InvalidationError:
pass
def close(self):
self.sync()
self.__cache.clear()
self.__shelf.close()
def sync_proxy(proxy):
if object.__getattribute__(proxy, "_invalidated"):
raise InvalidationError("the proxy has been invalidated (the key was reassigned)")
shelf = object.__getattribute__(proxy, "_shelf")
key = object.__getattribute__(proxy, "_key")
obj = object.__getattribute__(proxy, "_obj")
shelf[key] = obj
shelf.sync()
def open(*args):
return ShelfWrapper( shelve.open(*args) )
------ example ------
>>> db = open("blah.db")
>>> db["mylist"]=[1,2,3]
>>> db["mylist"].append(4)
>>> db["mylist"]
[1, 2, 3, 4]
>>> p = db["mylist"]
>>> type(p)
>>> p.append(5)
>>> p2 = db["mylist"]
>>> p2
[1, 2, 3, 4, 5]
>>> p2 is p
True
----- invalidation -----
When we reassign a key that have been proxies earlier, the proxy
instance becomes invalidated, so it will not override the new value.
>>> db["mylist"] = 19
>>> sync_proxy(p)
Traceback (most recent call last):
File "", line 1, in ?
File "Proxy.py", line 152, in sync_proxy
raise InvalidationError("the proxy has been invalidated (the key was reassigned)")
__main__.InvalidationError: the proxy has been invalidated (the key was reassigned)
>>>
>>> db["mylist"] += 1
>>> db["mylist"]
20