[Checkins] SVN: z3c.blobfile/trunk/src/z3c/blobfile/ - choosing the right storage implementation is now done through utility

Grégoire Weber zope.org at incept.ch
Mon Nov 12 07:53:07 EST 2007


Log message for revision 81765:
  - choosing the right storage implementation is now done through utility 
    lookup
  - added str, unicode, file and FileChunk storages (code from former 
    '_setData' of 'File')
  - it is now possible to pass any type of file or file data to the 
    constructor (will fail if there is not storage utility that is able
    to handle this file/file data type).
  - open points:
    - formal: didn't know how to register utilities for tests correctly
    - the '_data' attribute of 'File' should be renamed to '_blob'
    - maybe we should keep '_data' to not break misbehaving code accessing
      it directly (I don't know the zope3 policy in this regard)
    - the current tests only use the 'storages.StringStorage'

Changed:
  U   z3c.blobfile/trunk/src/z3c/blobfile/configure.zcml
  U   z3c.blobfile/trunk/src/z3c/blobfile/file.py
  U   z3c.blobfile/trunk/src/z3c/blobfile/interfaces.py
  A   z3c.blobfile/trunk/src/z3c/blobfile/storages.py
  U   z3c.blobfile/trunk/src/z3c/blobfile/tests.py

-=-
Modified: z3c.blobfile/trunk/src/z3c/blobfile/configure.zcml
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/configure.zcml	2007-11-12 09:03:42 UTC (rev 81764)
+++ z3c.blobfile/trunk/src/z3c/blobfile/configure.zcml	2007-11-12 12:53:07 UTC (rev 81765)
@@ -6,11 +6,39 @@
 
   <!-- content classes -->
 
-  <!-- TODO: to decide later if BlobFilesand BlobImages shall appear 
+  <!-- TODO: to decide later if BlobFiles and BlobImages shall appear 
              in the add menu or not  
   -->
 
+  <!-- `IStorage` utilities should be named with the dotted name
+       referencing the implementation. 
+  -->
 
+  <utility
+      name="__builtin__.str"
+      provides=".interfaces.IStorage"
+      factory=".storages.StringStorable"
+      />
+
+  <utility
+      name="__builtin__.unicode"
+      provides=".interfaces.IStorage"
+      factory=".storages.UnicodeStorable"
+      />
+
+  <utility
+      name="zope.app.file.file.FileChunk"
+      provides=".interfaces.IStorage"
+      factory=".storages.FileChunkStorable"
+      />
+
+  <utility
+      name="__builtin__.file"
+      provides=".interfaces.IStorage"
+      factory=".storages.FileDescriptorStorable"
+      />
+
+
   <adapter
       for=".interfaces.IBlobImage"
       provides="zope.size.interfaces.ISized"

Modified: z3c.blobfile/trunk/src/z3c/blobfile/file.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/file.py	2007-11-12 09:03:42 UTC (rev 81764)
+++ z3c.blobfile/trunk/src/z3c/blobfile/file.py	2007-11-12 12:53:07 UTC (rev 81765)
@@ -13,17 +13,18 @@
 ##############################################################################
 """File content component
 
-$Id: file.py 38759 2005-10-04 21:40:46Z tim_one $
+TODO: 
+- we should rename `_data` to `_blob` in `File`
+- should we then keep `_data` in `File` for backwards compatibility?
 """
 __docformat__ = 'restructuredtext'
 
 from persistent import Persistent
 import transaction
 from zope.interface import implements
-from zope.component import getUtilitiesFor
+import zope.component
 import zope.app.publication.interfaces
 import zope.app.file.interfaces
-from zope.app.file.file import FileChunk
 
 from ZODB.blob import Blob
 
@@ -35,6 +36,22 @@
 class File(Persistent):
     """A persistent content component storing binary file data
 
+    XXX: Don't know how to set up utilities for tests correctly::
+
+      >>> import storages
+      >>> zope.component.provideUtility(storages.StringStorable,
+      ...                               interfaces.IStorage,
+      ...                               name="__builtin__.str")
+      >>> zope.component.provideUtility(storages.UnicodeStorable,
+      ...                               interfaces.IStorage,
+      ...                               name="__builtin__.unicode")
+      >>> zope.component.provideUtility(storages.FileChunkStorable,
+      ...                               interfaces.IStorage,
+      ...                               name="zope.app.file.file.FileChunk")
+      >>> zope.component.provideUtility(storages.FileDescriptorStorable,
+      ...                               interfaces.IStorage,
+      ...                               name="__builtin__.file")
+
     Let's test the constructor:
 
     >>> file = File()
@@ -130,64 +147,19 @@
     def __init__(self, data='', contentType=''):
         self._data = Blob()
         self.contentType = contentType
-        fp = self._data.open('w')
-        fp.write(data)
-        fp.close()
+        self._setData(data)
 
     def open(self, mode="r"):
         return self._data.open(mode)
 
     def _setData(self, data):
-        # Check first if there is a supplier that may deduce a file name
-        # from the data passed. In such case `consumeFile` from ZODB's
-        # blob support may be used which is very efficient.
-        consumables = [(c[0], c[1](data))
-                       for c in getUtilitiesFor(interfaces.IConsumable)]
-        consumables = [(n, fn) for n, fn in consumable if fn is not None]
-	
-	# if more than one supplier may return a consumable file name
-        # there is no chance to deduce which to use.
-        if len(consumables) > 1:
-             names = "', '".join([n for n in consumables[0]])
-             raise AmbiguousConsumables("%s `IConsumables` (registered "
-                 "under '%s') supplied a file name. Please deregister "
-                 "all except one of them." % (len(consumables), names))
-	elif len(consumables) == 1:
-             self._data.consumeFile(consumables[0][1])
-             return
+        # Search for a storable that is able to store the data
+        dottedName = ".".join((data.__class__.__module__,
+                               data.__class__.__name__))
+        storable = zope.component.getUtility(interfaces.IStorage, 
+                                             name=dottedName)
+        storable.store(data, self._data)
 
-        # Handle case when data is a string but not a consumable
-        if isinstance(data, unicode):
-            data = data.encode('UTF-8')
-
-        if isinstance(data, str):
-            fp = self._data.open('w')
-            fp.write(data)
-            fp.close()
-
-        if data is None:
-            raise TypeError('Cannot set None data on a file.')
-
-        # Handle case when data is already a FileChunk
-        if isinstance(data, FileChunk):
-            fp = self._data.open('w')
-            chunk = data
-            while chunk:
-                fp.write(chunk._data)
-                chunk = chunk.next
-            fp.close()
-            return
-
-        # Handle case when data is a file object
-        seek = data.seek
-        read = data.read
-
-        fp = self._data.open('w')
-        block = data.read(MAXCHUNKSIZE)
-        while block:
-            fp.write(block)
-        fp.close()
-
     def _getData(self):
         fp = self._data.open('r')
         data = fp.read()

Modified: z3c.blobfile/trunk/src/z3c/blobfile/interfaces.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/interfaces.py	2007-11-12 09:03:42 UTC (rev 81764)
+++ z3c.blobfile/trunk/src/z3c/blobfile/interfaces.py	2007-11-12 12:53:07 UTC (rev 81765)
@@ -21,24 +21,28 @@
 
 import zope.app.file.interfaces
 
-class IConsumable(zope.interface.Interface):
-    """Support for Blob `consumeFile`
+
+class IStorage(zope.interface.Interface):
+    """Store file data
     """
 
-    def __call__(data):
-        """Return a consumable file name or None
+    def store(data, blob):
+        """Store the data into the blob
 
-        If the data doesn't represent a consumable file None is returned
+	Raises NonStorable if data is not storable.
         """
 
+
 class IOpenable(zope.interface.Interface):
-    """Openable File
+    """Openable file
     """
 
     def open(mode):
         """Open file and return the file descriptor
         """
 
-class AmbiguousConsumables(Exception):
-    """More than one consumable
+
+class NotStorable(Exception):
+    """Data is not storable
     """
+

Added: z3c.blobfile/trunk/src/z3c/blobfile/storages.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/storages.py	                        (rev 0)
+++ z3c.blobfile/trunk/src/z3c/blobfile/storages.py	2007-11-12 12:53:07 UTC (rev 81765)
@@ -0,0 +1,74 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Storables
+"""
+
+__docformat__ = 'restructuredtext'
+
+import zope.interface
+
+import interfaces
+from zope.app.file.file import FileChunk
+
+class StringStorable(object):
+    zope.interface.implements(interfaces.IStorage)
+     
+    def store(self, data, blob):
+        if not isinstance(data, str):
+            raise NotStorable("Could not store data (not of 'str' type).")
+
+        fp = blob.open('w')
+        fp.write(data)
+        fp.close()
+
+
+class UnicodeStorable(StringStorable):
+    zope.interface.implements(interfaces.IStorage)
+    
+    def store(self, data, blob):
+        if not isinstance(data, unicode):
+            raise NotStorable("Could not store data (not of 'unicode' "
+                              "type).")
+
+        data = data.encode('UTF-8')
+	StringStorable.store(self, data, blob)
+
+
+class FileChunkStorable(object):
+    zope.interface.implements(interfaces.IStorage)
+     
+    def store(self, data, blob):
+        if not isinstance(data, FileChunk):
+            raise NotStorable("Could not store data (not a of 'FileChunk' "
+                              "type).")
+
+        fp = blob.open('w')
+        chunk = data
+        while chunk:
+            fp.write(chunk._data)
+            chunk = chunk.next
+        fp.close()
+
+
+class FileDescriptorStorable(object):
+    zope.interface.implements(interfaces.IStorage)
+     
+    def store(self, data, blob):
+        if not isinstance(data, file):
+            raise NotStorable("Could not store data (not of 'file').")
+
+        filename = getattr(data, "name", None)
+        if filename is not None:
+            blob.consumeFile(filename)
+            return

Modified: z3c.blobfile/trunk/src/z3c/blobfile/tests.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/tests.py	2007-11-12 09:03:42 UTC (rev 81764)
+++ z3c.blobfile/trunk/src/z3c/blobfile/tests.py	2007-11-12 12:53:07 UTC (rev 81765)
@@ -41,8 +41,32 @@
     '\x00A\x00;'
     )
 
+
+
+
+# XXX: Don't know how to set up utilities for tests correctly::
+def registerUtilities():
+     import zope.component
+     import storages
+     import interfaces
+     zope.component.provideUtility(storages.StringStorable(),
+                                   interfaces.IStorage,
+                                   name="__builtin__.str")
+     zope.component.provideUtility(storages.UnicodeStorable(),
+                                   interfaces.IStorage,
+                                   name="__builtin__.unicode")
+     zope.component.provideUtility(storages.FileChunkStorable(),
+                                   interfaces.IStorage,
+                                   name="zope.app.file.file.FileChunk")
+     zope.component.provideUtility(storages.FileDescriptorStorable(),
+                                   interfaces.IStorage,
+                                   name="__builtin__.file")
+
 class TestImage(unittest.TestCase):
 
+    def setUp(self):
+        registerUtilities()
+
     def _makeImage(self, *args, **kw):
         return Image(*args, **kw)
 
@@ -73,6 +97,9 @@
 
 class TestFileAdapters(unittest.TestCase):
 
+    def setUp(self):
+        registerUtilities()
+
     def _makeFile(self, *args, **kw):
         return Image(*args, **kw)
 
@@ -92,6 +119,9 @@
 
 class DummyImage(object):
 
+    def setUp(self):
+        registerUtilities()
+
     def __init__(self, width, height, bytes):
         self.width = width
         self.height = height
@@ -106,6 +136,9 @@
 
 class TestFileFactory(unittest.TestCase):
 
+    def setUp(self):
+        registerUtilities()
+
     def test_image(self):
         factory = FileFactory(None)
         f = factory("spam.txt", "image/foo", "hello world")
@@ -130,6 +163,9 @@
 
 class TestSized(unittest.TestCase):
 
+    def setUp(self):
+        registerUtilities()
+
     def testInterface(self):
         from zope.size.interfaces import ISized
         self.failUnless(ISized.implementedBy(ImageSized))



More information about the Checkins mailing list