Welcome, guest | Sign In | My Account | Store | Cart

Notice! PyPM is being replaced with the ActiveState Platform, which enhances PyPM’s build and deploy capabilities. Create your free Platform account to download ActivePython or customize Python with the packages you require and get automatic updates.

Download
ActivePython
INSTALL>
pypm install lazr.exportedfolder

How to install lazr.exportedfolder

  1. Download and install ActivePython
  2. Open Command Prompt
  3. Type pypm install lazr.exportedfolder
 Python 2.7Python 3.2Python 3.3
Windows (32-bit)
1.0.0 Available View build log
Windows (64-bit)
1.0.0 Available View build log
Mac OS X (10.5+)
1.0.0 Available View build log
Linux (32-bit)
1.0.0 Available View build log
Linux (64-bit)
1.0.0 Available View build log
 
License
LGPL v3
Lastest release
version 1.0.0 on Jan 5th, 2011

System Message: WARNING/2 (<string>, line 2)

Explicit markup ends without a blank line; unexpected unindent.

This file is part of lazr.exportedfolder

lazr.exportedfolder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

lazr.exportedfolder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

LAZR exportedfolder

Please see https://dev.launchpad.net/LazrStyleGuide and https://dev.launchpad.net/Hacking for how to develop in this package.

Serving directories of files

LAZR Exported Folder adds special views that can be used to serve all the files under a particular directory.

ExportedFolder

This 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_dir

That 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)
True

The view will serve the file that it traverses to.

>>> view = view.publishTraverse(view.request, 'test.txt')
>>> print view()
Text file

It 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 file

Requesting 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:...
ExportedImageFolder

For 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/png

If 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 extension
Exporting trees

By 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 = True

Traversing 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 1

But 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.exportedfolder
1.0.0 (2009-10-26)
  • Initial release

Subscribe to package updates

Last updated Jan 5th, 2011

Download Stats

Last month:2

What does the lock icon mean?

Builds marked with a lock icon are only available via PyPM to users with a current ActivePython Business Edition subscription.

Need custom builds or support?

ActivePython Enterprise Edition guarantees priority access to technical support, indemnification, expert consulting and quality-assured language builds.

Plan on re-distributing ActivePython?

Get re-distribution rights and eliminate legal risks with ActivePython OEM Edition.