[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