Package: dolmen.file
dolmen.fileallows you to manage and store files within the ZODB.
It takes the core functionalities ofzope.app.file, and simplifies
them, using Grok for views and adapters registrations.CompatibilityIn order to make sure that ourFileimplementation is complete and
functional, we test it against the originalzope.app.filetests:>>> from dolmen.file import NamedFile, INamedFile, FileChunkLet’s test the constructor:>>> file = NamedFile()
>>> file.contentType
''
>>> file.data
''

>>> file = NamedFile('Foobar')
>>> file.contentType
''
>>> file.data
'Foobar'

>>> file = NamedFile('Foobar', 'text/plain')
>>> file.contentType
'text/plain'
>>> file.data
'Foobar'

>>> file = NamedFile(data='Foobar', contentType='text/plain')
>>> file.contentType
'text/plain'
>>> file.data
'Foobar'Let’s test the mutators:>>> file = NamedFile()
>>> file.contentType = 'text/plain'
>>> file.contentType
'text/plain'

>>> file.data = 'Foobar'
>>> file.data
'Foobar'

>>> file.data = None
Traceback (most recent call last):
...
TypeError: Cannot set None data on a file.Let’s test large data input:>>> file = NamedFile()

Insert as string:

>>> file.data = 'Foobar'*60000
>>> file.size
360000
>>> file.data == 'Foobar'*60000
TrueInsert data as FileChunk:>>> fc = FileChunk('Foobar'*4000)
>>> file.data = fc
>>> file.size
24000
>>> file.data == 'Foobar'*4000
TrueInsert data from file object:>>> import cStringIO
>>> sio = cStringIO.StringIO()
>>> sio.write('Foobar'*100000)
>>> sio.seek(0)
>>> file.data = sio
>>> file.size
600000
>>> file.data == 'Foobar'*100000
TrueLast, but not least, verify the interface implementation:>>> from zope.interface.verify import verifyClass
>>> INamedFile.implementedBy(NamedFile)
True
>>> verifyClass(INamedFile, NamedFile)
TrueNamingWhen no name is provided, the fallback is a simple empty unicode
string:>>> file = NamedFile('Foobar')
>>> file.contentType
''
>>> file.data
'Foobar'
>>> file.filename
u''To specifiy a filename, we can give it to the constructor:>>> file = NamedFile('Foobar', filename='foobar.txt')
>>> file.data
'Foobar'
>>> file.filename
u'foobar.txt'The filename can be both unicode or simple string:>>> file = NamedFile('Foobar', filename=u'foobar.txt')
>>> file.data
'Foobar'
>>> file.filename
u'foobar.txt'The filename provided had an extension : ‘txt’. This extension is used
by the NamedFile, while instanciated, to try and guess the mimetype of
the data:>>> file.contentType
'text/plain'

The filename can be set later, but this won't trigger the mime
type guess::

>>> file.filename = u"something.zip"
>>> file.filename
u'something.zip'
>>> file.contentType
'text/plain'SizeTo represent the size of the stored data,dolmen.fileuses a
normalized adaptation, based onzope.sizedefinitions:>>> from zope.size import ISized
>>> sized = ISized(file)
>>> sized
<dolmen.file.size.Sized object at ...>
>>> sized.sizeForSorting()
('byte', 6)
>>> sized.sizeForDisplay()
u'1 KB'AccessIn order to access our file,dolmen.fileprovides a view calledfile_publishthat sets the proper headers and returns the
data. Let’s set up a simple environment to test that behavior:>>> from zope.component.hooks import getSite
>>> from zope.component import getMultiAdapter
>>> from zope.publisher.browser import TestRequest

>>> root = getSite()
>>> root['myfile'] = NamedFile('Foobar', filename='foobar.txt')
>>> myfile = root['myfile']

The `file_publish` view will adapt a INamedFile and a request and,
when called, will return the data.

>>> request = TestRequest()
>>> view = getMultiAdapter((myfile, request), name='file_publish')
>>> view
<dolmen.file.access.FilePublisher object at ...>In the update of the view, the headers are set properly, using the
info of the file:>>> view.update()
>>> for key, value in view.response.getHeaders(): print key, repr(value)
X-Powered-By 'Zope (www.zope.org), Python (www.python.org)'
Content-Length '6'
Content-Type 'text/plain'
Content-Disposition 'attachment; filename="foobar.txt"'
>>> view.render()
'Foobar'Field, download and securityIn a site, the file object is rarely accessed directly. Often, it’s
just a part of a more complex object. For that matter, we have three
dedicated components: the field, the property and the traverser.Field and PropertyA property is provided to allow a transparent use of a INamedFile component.Working exemple>>> from persistent import Persistent
>>> from dolmen.file import FileProperty, FileField
>>> from zope.interface import Interface, implements>>> class IContent(Interface):
...     binary = FileField(title=u"Binary data")>>> class MyContent(Persistent):
...     implements(IContent)
...     binary = FileProperty(IContent['binary'])>>> root['mammoth'] = MyContent()
>>> manfred = root['mammoth']
>>> manfred.binary = FileChunk('Foobar')
>>> manfred.binary
<dolmen.file.file.NamedFile object at ...>>>> manfred.binary.data
'Foobar'Custom factory>>> class MyFile(NamedFile):
...     """My own file type.
...     """>>> class CustomContent(object):
...     implements(IContent)
...     binary = FileProperty(IContent['binary'], factory=MyFile)>>> custom = CustomContent()
>>> custom.binary = FileChunk('Foobar')
>>> custom.binary
<MyFile object at ...>Error>>> class MyFalseFile(object):
...     """My own file type.
...     """>>> class FaultyContent(object):
...     implements(IContent)
...     binary = FileProperty(IContent['binary'], factory=MyFalseFile)
Traceback (most recent call last):
...
ValueError: Provided factory is not a valid INamedFileFieldsThere are two fields provided bydolmen.file: the FileField and the
ImageField. They are just logical separation but have a common base:>>> from dolmen.file import IImageField, IFileField, ImageField
>>> IImageField.extends(IFileField)
True
>>> isinstance(ImageField(), FileField)
TrueTraversalThe traverser will take care of both the fetching and the security
checking, while accessing your data. The basic permission used to
check the availability of the data, iszope.View.Here, we set up two principals to test this. ‘jason’ is a logged in
member with no rights while ‘judith’ has thezope.Viewpermission
granted:>>> import zope.security.management as security
>>> from zope.traversing.interfaces import ITraversable
>>> from zope.security.testing import Principal, Participation

>>> judith = Principal('zope.judith', 'Judith')
>>> jason = Principal('zope.jason', 'Jason')We create the interaction and try to traverse to our binary data:>>> security.newInteraction(Participation(jason))
>>> traverser = getMultiAdapter(
...              (manfred, request), ITraversable, 'download')
>>> traverser
<dolmen.file.access.DownloadTraverser object at ...>
>>> traverser.traverse('binary')
Traceback (most recent call last):
...
Unauthorized: binary
>>> security.endInteraction()It fails. An Unauthorized Error is raised. We now try with Judith:>>> security.newInteraction(Participation(judith))
>>> traverser.traverse('binary')
<dolmen.file.access.FilePublisher object at ...>Our data is returned, wrapped in aFilePublisherview, ready to be
rendered (see the Access section, for more information).What if we traverse to an unknown field ? Let’s try:>>> traverser.traverse('zorglub')
Traceback (most recent call last):
...
NotFound: Object: <MyContent object at ...>, name: 'zorglub'Everything is fine : a NotFound error has been raised. If we try to
access a file that is not an INamedFile, we get another error:>>> traverser.traverse('__name__')
Traceback (most recent call last):
...
LocationError: '__name__ is not a valid INamedFile'We gracefully end our tests:>>> security.endInteraction()Enjoy !Changelog0.6 (2010-11-17)Tested for Grok 1.2.zope.testingdependency has been removed.The INamedFile factory is now pluggable, in the file property. Tests
have been added to fix that behavior.0.5.1 (2010-02-28)Added anISizedadapter forINamedFileobjects. Added tests
accordingly.0.5.0 (2010-02-28)Updated code base to be fully pep8 compatible.zope.appdependencies have been entirely dropped.dolmen.fileis no longer a layer abovezope.app.file. It
nows integrates the few features it needed fromzope.app.file.0.4.0 (2009-11-18)Release compatible with ZTK 1.0dev versions. Pinned down the version
of zope.traversing in the setup.py. It now runs on Python 2.6 !0.3.2 (2009-10-23)Corrected a bug on the clean_filename util function, that would fail
on unicode values. Added a test to fix that behavior.0.3.1 (2009-10-23)Removed the __parent__ attribution in the property. If you relied on
this, you now have to take care of the location proxying yourself.0.3.0 (2009-10-23)Changed the filename cleaning method, now exposed in the public
API. We now use a compiled regexp to get the name.0.2.0 (2009-10-21)Added an image field with the corresponding interface. This was
previously part ofdolmen.imaging. The ImageField component is a
simple subclass of the default FileField.0.1.0 (2009-10-16)Initial release