[Zodb-checkins] SVN: ZODB/trunk/src/ZODB/DemoStorage. Added blob support to DemoStorage.
Jim Fulton
jim at zope.com
Mon Oct 27 15:20:54 EDT 2008
Log message for revision 92627:
Added blob support to DemoStorage.
Changed:
U ZODB/trunk/src/ZODB/DemoStorage.py
U ZODB/trunk/src/ZODB/DemoStorage.test
-=-
Modified: ZODB/trunk/src/ZODB/DemoStorage.py
===================================================================
--- ZODB/trunk/src/ZODB/DemoStorage.py 2008-10-27 19:20:52 UTC (rev 92626)
+++ ZODB/trunk/src/ZODB/DemoStorage.py 2008-10-27 19:20:54 UTC (rev 92627)
@@ -20,13 +20,22 @@
"""
import random
+import tempfile
import threading
+import ZODB.blob
+import ZODB.interfaces
import ZODB.MappingStorage
import ZODB.POSException
import ZODB.utils
+import zope.interface
-class DemoStorage:
+class DemoStorage(object):
+ zope.interface.implements(
+ ZODB.interfaces.IStorage,
+ ZODB.interfaces.IStorageIteration,
+ )
+
def __init__(self, name=None, base=None, changes=None):
if base is None:
base = ZODB.MappingStorage.MappingStorage()
@@ -34,17 +43,40 @@
if changes is None:
changes = ZODB.MappingStorage.MappingStorage()
+ zope.interface.alsoProvides(self, ZODB.interfaces.IBlobStorage)
+ self._temporary_changes = True
+ self._blob_dir = None
+ else:
+ if ZODB.interfaces.IBlobStorage.providedBy(changes):
+ zope.interface.alsoProvides(self, ZODB.interfaces.IBlobStorage)
+ self._temporary_changes = False
+
self.changes = changes
if name is None:
name = 'DemoStorage(%r, %r)' % (base.getName(), changes.getName())
self.__name__ = name
- supportsUndo = getattr(changes, 'supportsUndo', None)
- if supportsUndo is not None and supportsUndo():
- for meth in ('supportsUndo', 'undo', 'undoLog', 'undoInfo'):
- setattr(self, meth, getattr(changes, meth))
+ self._copy_methods_from_changes(changes)
+
+ def _blobify(self):
+ if self._temporary_changes and self._blob_dir is None:
+ self._blob_dir = tempfile.mkdtemp('blobs')
+ self.changes = ZODB.blob.BlobStorage(self._blob_dir, self.changes)
+ self._copy_methods_from_changes(self.changes)
+ return True
+
+ def cleanup(self):
+ self.base.cleanup()
+ self.changes.cleanup()
+ def close(self):
+ self.base.close()
+ self.changes.close()
+ if getattr(self, '_blob_dir', ''):
+ ZODB.blob.remove_committed_dir(self._blob_dir)
+
+ def _copy_methods_from_changes(self, changes):
for meth in (
'_lock_acquire', '_lock_release',
'getSize', 'history', 'isReadOnly', 'registerDB',
@@ -53,18 +85,16 @@
):
setattr(self, meth, getattr(changes, meth))
+ supportsUndo = getattr(changes, 'supportsUndo', None)
+ if supportsUndo is not None and supportsUndo():
+ for meth in ('supportsUndo', 'undo', 'undoLog', 'undoInfo'):
+ setattr(self, meth, getattr(changes, meth))
+ zope.interface.alsoProvides(self, ZODB.interfaces.IStorageUndoable)
+
lastInvalidations = getattr(changes, 'lastInvalidations', None)
if lastInvalidations is not None:
self.lastInvalidations = lastInvalidations
-
- def cleanup(self):
- self.base.cleanup()
- self.changes.cleanup()
- def close(self):
- self.base.close()
- self.changes.close()
-
def getName(self):
return self.__name__
__repr__ = getName
@@ -113,7 +143,23 @@
pass
return result
-
+
+ def loadBlob(self, oid, serial):
+ try:
+ return self.changes.loadBlob(oid, serial)
+ except ZODB.POSException.POSKeyError:
+ try:
+ return self.base.loadBlob(oid, serial)
+ except AttributeError:
+ if not zope.interface.IBlobStorage.providBy(self.base):
+ raise ZODB.POSException.POSKeyError(oid, serial)
+ raise
+ except AttributeError:
+ if self._blobify():
+ return self.loadBlob(oid, serial)
+ raise
+
+
def loadSerial(self, oid, serial):
try:
return self.changes.loadSerial(oid, serial)
@@ -163,3 +209,22 @@
oid=oid, serials=(old, serial)) # XXX untested branch
return self.changes.store(oid, serial, data, '', transaction)
+
+ def storeBlob(self, oid, oldserial, data, blobfilename, version,
+ transaction):
+ try:
+ return self.changes.storeBlob(
+ oid, oldserial, data, blobfilename, version, transaction)
+ except AttributeError:
+ if self._blobify():
+ return self.changes.storeBlob(
+ oid, oldserial, data, blobfilename, version, transaction)
+ raise
+
+ def temporaryDirectory(self):
+ try:
+ return self.changes.temporaryDirectory()
+ except AttributeError:
+ if self._blobify():
+ return self.changes.temporaryDirectory()
+ raise
Modified: ZODB/trunk/src/ZODB/DemoStorage.test
===================================================================
--- ZODB/trunk/src/ZODB/DemoStorage.test 2008-10-27 19:20:52 UTC (rev 92626)
+++ ZODB/trunk/src/ZODB/DemoStorage.test 2008-10-27 19:20:54 UTC (rev 92627)
@@ -1,5 +1,6 @@
+==========================
DemoStorage demo (doctest)
---------------------------
+==========================
Note that most people will configure the storage through ZConfig. If
you are one of those people, you may want to stop here. :) The
@@ -120,3 +121,93 @@
... for name in ('supportsUndo', 'undo', 'undoLog', 'undoInfo')
... ]
[True, True, True, True]
+
+ >>> db.close()
+
+Blob Support
+============
+
+DemoStorage supports Blobs if the changes database supports blobs.
+
+ >>> import ZODB.blob
+ >>> base = ZODB.blob.BlobStorage('base', FileStorage('base.fs'))
+ >>> db = DB(base)
+ >>> conn = db.open()
+ >>> conn.root()['blob'] = ZODB.blob.Blob()
+ >>> conn.root()['blob'].open('w').write('state 1')
+ >>> transaction.commit()
+ >>> db.close()
+
+ >>> base = ZODB.blob.BlobStorage('base',
+ ... FileStorage('base.fs', read_only=True))
+ >>> changes = ZODB.blob.BlobStorage('changes',
+ ... FileStorage('changes.fs', create=True))
+ >>> storage = DemoStorage(base=base, changes=changes)
+
+ >>> db = DB(storage)
+ >>> conn = db.open()
+ >>> conn.root()['blob'].open().read()
+ 'state 1'
+ >>> _ = transaction.begin()
+ >>> conn.root()['blob'].open('w').write('state 2')
+ >>> transaction.commit()
+ >>> conn.root()['blob'].open().read()
+ 'state 2'
+
+ >>> storage.temporaryDirectory() == changes.temporaryDirectory()
+ True
+
+ >>> db.close()
+
+It isn't necessary for the base database to support blobs.
+
+ >>> base = FileStorage('base.fs', read_only=True)
+ >>> changes = ZODB.blob.BlobStorage('changes', FileStorage('changes.fs'))
+ >>> storage = DemoStorage(base=base, changes=changes)
+ >>> db = DB(storage)
+ >>> conn = db.open()
+ >>> conn.root()['blob'].open().read()
+ 'state 2'
+
+ >>> _ = transaction.begin()
+ >>> conn.root()['blob2'] = ZODB.blob.Blob()
+ >>> conn.root()['blob2'].open('w').write('state 1')
+ >>> conn.root()['blob2'].open().read()
+ 'state 1'
+
+ >>> db.close()
+
+If the changes database is created implicitly, it will get a blob
+storage wrapped around it when necessary:
+
+ >>> base = ZODB.blob.BlobStorage('base',
+ ... FileStorage('base.fs', read_only=True))
+ >>> storage = DemoStorage(base=base)
+
+ >>> type(storage.changes).__name__
+ 'MappingStorage'
+
+ >>> db = DB(storage)
+ >>> conn = db.open()
+ >>> conn.root()['blob'].open().read()
+ 'state 1'
+
+ >>> type(storage.changes).__name__
+ 'BlobStorage'
+
+ >>> _ = transaction.begin()
+ >>> conn.root()['blob'].open('w').write('state 2')
+ >>> transaction.commit()
+ >>> conn.root()['blob'].open().read()
+ 'state 2'
+
+ >>> storage.temporaryDirectory() == storage.changes.temporaryDirectory()
+ True
+
+ >>> db.close()
+
+.. Check that the temporary directory is gone
+
+ >>> import os
+ >>> os.path.exists(storage.temporaryDirectory())
+ False
More information about the Zodb-checkins
mailing list