[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