Package: lazr.exportedfolder
Please seehttps://dev.launchpad.net/LazrStyleGuideandhttps://dev.launchpad.net/Hackingfor how to develop in this
package.Serving directories of filesLAZR Exported Folder adds special views that can be used to serve all
the files under a particular directory.ExportedFolderThis is the base implementation. To export a directory, you need to
subclass that view and provide a folder property returning the path of
the directory to expose.>>> import os
>>> import tempfile
>>> resource_dir = tempfile.mkdtemp(prefix='resources')
>>> file(os.path.join(resource_dir, 'test.txt'), 'w').write('Text file')
>>> file(os.path.join(resource_dir, 'image1.gif'), 'w').write(
...     'GIF file')
>>> file(os.path.join(resource_dir, 'image2.png'), 'w').write(
...     'PNG file')
>>> os.mkdir(os.path.join(resource_dir, 'a_dir'))
>>> file(os.path.join(resource_dir, 'other.txt'), 'w').write(
...     'Other file')>>> from lazr.exportedfolder.folder import ExportedFolder
>>> class MyFolder(ExportedFolder):
...     folder = resource_dirThat view provides the IBrowserPublisher interface necessary to handle
all the traversal logic.>>> from zope.interface.verify import verifyObject
>>> from zope.publisher.interfaces.browser import IBrowserPublisher
>>> from zope.publisher.tests.httprequest import TestRequest>>> view = MyFolder(object(), TestRequest())
>>> verifyObject(IBrowserPublisher, view)
TrueThe view will serve the file that it traverses to.>>> view = view.publishTraverse(view.request, 'test.txt')
>>> print view()
Text fileIt also sets the appropriate headers for cache control on the response.>>> for name, value in sorted(view.request.response.getHeaders()):
...     print "%s: %s" % (name, value)
Cache-Control: public,max-age=86400
Content-Type: text/plain
Expires: ...
Last-Modified: ...It’s possible to override the default max-age:>>> view = MyFolder(object(), TestRequest())
>>> view.max_age = 1440>>> view = view.publishTraverse(view.request, 'test.txt')
>>> print view()
Text file>>> for name, value in sorted(view.request.response.getHeaders()):
...     print "%s: %s" % (name, value)
Cache-Control: public,max-age=1440
Content-Type: text/plain
Expires: ...
Last-Modified: ...It accepts traversing to the file through an arbitrary revision
identifier.>>> view = MyFolder(object(), TestRequest())
>>> view = view.publishTraverse(view.request, 'rev6510')
>>> view = view.publishTraverse(view.request, 'image1.gif')
>>> print view()
GIF fileRequesting a directory raises a NotFound.>>> view = MyFolder(object(), TestRequest())
>>> view = view.publishTraverse(view.request, 'a_dir')
>>> view()
Traceback (most recent call last):
  ...
NotFound:...By default, subdirectories are not exported. (See below on how to enable
this)>>> view = MyFolder(object(), TestRequest())
>>> view = view.publishTraverse(view.request, 'a_dir')
>>> view = view.publishTraverse(view.request, 'other.txt')
>>> view()
Traceback (most recent call last):
  ...
NotFound:...Not requesting any file, also raises NotFound.>>> view = MyFolder(object(), TestRequest())
>>> view()
Traceback (most recent call last):
  ...
NotFound:...As requesting a non-existent file.>>> view = MyFolder(object(), TestRequest())
>>> view = view.publishTraverse(view.request, 'image2')
>>> view()
Traceback (most recent call last):
  ...
NotFound:...ExportedImageFolderFor images, it’s often convenient not to request the extension. There is
an ExportedImageFolder subclass, that will accept serving an image file
without extension.  For example, requesting ‘image1’ or ‘image2’ will
serve the correct file. The supported extensions are defined in the
image_extensions property.>>> from lazr.exportedfolder.folder import ExportedImageFolder>>> class MyImageFolder(ExportedImageFolder):
...     folder = resource_dir>>> view = MyImageFolder(object(), TestRequest())
>>> view.image_extensions
('.png', '.gif')>>> view = view.publishTraverse(view.request, 'image2')
>>> print view()
PNG file
>>> print view.request.response.getHeader('Content-Type')
image/pngIf a file without extension exists, that one will be served.>>> file(os.path.join(resource_dir, 'image3'), 'w').write(
...     'Image without extension')
>>> file(os.path.join(resource_dir, 'image3.gif'), 'w').write(
...     'Image with extension')>>> view = MyImageFolder(object(), TestRequest())
>>> view = view.publishTraverse(view.request, 'image3')
>>> print view()
Image without extension>>> view = MyImageFolder(object(), TestRequest())
>>> view = view.publishTraverse(view.request, 'image3.gif')
>>> print view()
Image with extensionExporting treesBy default ExportedFolder doesn’t export contained folders, but if the
export_subdirectories is set to True, it will allow traversing to
subdirectories.>>> os.mkdir(os.path.join(resource_dir, 'public'))
>>> file(os.path.join(
...     resource_dir, 'public', 'test1.txt'), 'w').write('Public File')
>>> os.mkdir(os.path.join(resource_dir, 'public', 'subdir1'))
>>> file(os.path.join(
...     resource_dir, 'public', 'subdir1', 'test1.txt'), 'w').write(
...         'Sub file 1')>>> class MyTree(ExportedFolder):
...     folder = resource_dir
...     export_subdirectories = TrueTraversing to a file in a subdirectory will now work.>>> view = MyTree(object(), TestRequest())
>>> view = view.publishTraverse(view.request, 'public')
>>> view = view.publishTraverse(view.request, 'subdir1')
>>> view = view.publishTraverse(view.request, 'test1.txt')
>>> print view()
Sub file 1But traversing to the subdirectory itself will raise a NotFound.>>> view = MyTree(object(), TestRequest())
>>> view = view.publishTraverse(view.request, 'public')
>>> print view()
Traceback (most recent call last):
  ...
NotFound:...Trying to request a non-existent file, will also raise a NotFound.>>> view = MyTree(object(), TestRequest())
>>> view = view.publishTraverse(view.request, 'public')
>>> view = view.publishTraverse(view.request, 'nosuchfile.txt')
>>> view()
Traceback (most recent call last):
  ...
NotFound:...Traversing beyond an existing file to a non-existant file raises a
NotFound.>>> view = MyTree(object(), TestRequest())
>>> view = view.publishTraverse(view.request, 'public')
>>> view = view.publishTraverse(view.request, 'subdir1')
>>> view = view.publishTraverse(view.request, 'test1.txt')
>>> view = view.publishTraverse(view.request, 'nosuchpath')
>>> view()
Traceback (most recent call last):
  ...
NotFound:...Clean-up>>> import shutil
>>> shutil.rmtree(resource_dir)NEWS for lazr.exportedfolder1.0.0 (2009-10-26)Initial release