{ "info": { "author": "LAZR Developers", "author_email": "lazr-developers@lists.launchpad.net", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Affero General Public License v3", "Operating System :: OS Independent", "Programming Language :: Python" ], "description": "..\n This file is part of lazr.exportedfolder\n\n lazr.exportedfolder is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n lazr.exportedfolder is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see .\n\nLAZR exportedfolder\n*******************\n\nPlease see https://dev.launchpad.net/LazrStyleGuide and\nhttps://dev.launchpad.net/Hacking for how to develop in this\npackage.\n\n\n============================\nServing directories of files\n============================\n\nLAZR Exported Folder adds special views that can be used to serve all\nthe files under a particular directory.\n\nExportedFolder\n==============\n\nThis is the base implementation. To export a directory, you need to\nsubclass that view and provide a folder property returning the path of\nthe directory to expose.\n\n >>> import os\n >>> import tempfile\n >>> resource_dir = tempfile.mkdtemp(prefix='resources')\n >>> file(os.path.join(resource_dir, 'test.txt'), 'w').write('Text file')\n >>> file(os.path.join(resource_dir, 'image1.gif'), 'w').write(\n ... 'GIF file')\n >>> file(os.path.join(resource_dir, 'image2.png'), 'w').write(\n ... 'PNG file')\n >>> os.mkdir(os.path.join(resource_dir, 'a_dir'))\n >>> file(os.path.join(resource_dir, 'other.txt'), 'w').write(\n ... 'Other file')\n\n >>> from lazr.exportedfolder.folder import ExportedFolder\n >>> class MyFolder(ExportedFolder):\n ... folder = resource_dir\n\nThat view provides the IBrowserPublisher interface necessary to handle\nall the traversal logic.\n\n >>> from zope.interface.verify import verifyObject\n >>> from zope.publisher.interfaces.browser import IBrowserPublisher\n >>> from zope.publisher.tests.httprequest import TestRequest\n\n >>> view = MyFolder(object(), TestRequest())\n >>> verifyObject(IBrowserPublisher, view)\n True\n\nThe view will serve the file that it traverses to.\n\n >>> view = view.publishTraverse(view.request, 'test.txt')\n >>> print view()\n Text file\n\nIt also sets the appropriate headers for cache control on the response.\n\n >>> for name, value in sorted(view.request.response.getHeaders()):\n ... print \"%s: %s\" % (name, value)\n Cache-Control: public,max-age=86400\n Content-Type: text/plain\n Expires: ...\n Last-Modified: ...\n\nIt's possible to override the default max-age:\n\n >>> view = MyFolder(object(), TestRequest())\n >>> view.max_age = 1440\n\n >>> view = view.publishTraverse(view.request, 'test.txt')\n >>> print view()\n Text file\n\n >>> for name, value in sorted(view.request.response.getHeaders()):\n ... print \"%s: %s\" % (name, value)\n Cache-Control: public,max-age=1440\n Content-Type: text/plain\n Expires: ...\n Last-Modified: ...\n\n\nIt accepts traversing to the file through an arbitrary revision\nidentifier.\n\n >>> view = MyFolder(object(), TestRequest())\n >>> view = view.publishTraverse(view.request, 'rev6510')\n >>> view = view.publishTraverse(view.request, 'image1.gif')\n >>> print view()\n GIF file\n\nRequesting a directory raises a NotFound.\n\n >>> view = MyFolder(object(), TestRequest())\n >>> view = view.publishTraverse(view.request, 'a_dir')\n >>> view()\n Traceback (most recent call last):\n ...\n NotFound:...\n\nBy default, subdirectories are not exported. (See below on how to enable\nthis)\n\n >>> view = MyFolder(object(), TestRequest())\n >>> view = view.publishTraverse(view.request, 'a_dir')\n >>> view = view.publishTraverse(view.request, 'other.txt')\n >>> view()\n Traceback (most recent call last):\n ...\n NotFound:...\n\nNot requesting any file, also raises NotFound.\n\n >>> view = MyFolder(object(), TestRequest())\n >>> view()\n Traceback (most recent call last):\n ...\n NotFound:...\n\nAs requesting a non-existent file.\n\n >>> view = MyFolder(object(), TestRequest())\n >>> view = view.publishTraverse(view.request, 'image2')\n >>> view()\n Traceback (most recent call last):\n ...\n NotFound:...\n\n\nExportedImageFolder\n===================\n\nFor images, it's often convenient not to request the extension. There is\nan ExportedImageFolder subclass, that will accept serving an image file\nwithout extension. For example, requesting 'image1' or 'image2' will\nserve the correct file. The supported extensions are defined in the\nimage_extensions property.\n\n >>> from lazr.exportedfolder.folder import ExportedImageFolder\n\n >>> class MyImageFolder(ExportedImageFolder):\n ... folder = resource_dir\n\n >>> view = MyImageFolder(object(), TestRequest())\n >>> view.image_extensions\n ('.png', '.gif')\n\n >>> view = view.publishTraverse(view.request, 'image2')\n >>> print view()\n PNG file\n >>> print view.request.response.getHeader('Content-Type')\n image/png\n\nIf a file without extension exists, that one will be served.\n\n >>> file(os.path.join(resource_dir, 'image3'), 'w').write(\n ... 'Image without extension')\n >>> file(os.path.join(resource_dir, 'image3.gif'), 'w').write(\n ... 'Image with extension')\n\n >>> view = MyImageFolder(object(), TestRequest())\n >>> view = view.publishTraverse(view.request, 'image3')\n >>> print view()\n Image without extension\n\n >>> view = MyImageFolder(object(), TestRequest())\n >>> view = view.publishTraverse(view.request, 'image3.gif')\n >>> print view()\n Image with extension\n\n\nExporting trees\n===============\n\nBy default ExportedFolder doesn't export contained folders, but if the\nexport_subdirectories is set to True, it will allow traversing to\nsubdirectories.\n\n >>> os.mkdir(os.path.join(resource_dir, 'public'))\n >>> file(os.path.join(\n ... resource_dir, 'public', 'test1.txt'), 'w').write('Public File')\n >>> os.mkdir(os.path.join(resource_dir, 'public', 'subdir1'))\n >>> file(os.path.join(\n ... resource_dir, 'public', 'subdir1', 'test1.txt'), 'w').write(\n ... 'Sub file 1')\n\n >>> class MyTree(ExportedFolder):\n ... folder = resource_dir\n ... export_subdirectories = True\n\nTraversing to a file in a subdirectory will now work.\n\n >>> view = MyTree(object(), TestRequest())\n >>> view = view.publishTraverse(view.request, 'public')\n >>> view = view.publishTraverse(view.request, 'subdir1')\n >>> view = view.publishTraverse(view.request, 'test1.txt')\n >>> print view()\n Sub file 1\n\nBut traversing to the subdirectory itself will raise a NotFound.\n\n >>> view = MyTree(object(), TestRequest())\n >>> view = view.publishTraverse(view.request, 'public')\n >>> print view()\n Traceback (most recent call last):\n ...\n NotFound:...\n\nTrying to request a non-existent file, will also raise a NotFound.\n\n >>> view = MyTree(object(), TestRequest())\n >>> view = view.publishTraverse(view.request, 'public')\n >>> view = view.publishTraverse(view.request, 'nosuchfile.txt')\n >>> view()\n Traceback (most recent call last):\n ...\n NotFound:...\n\nTraversing beyond an existing file to a non-existant file raises a\nNotFound.\n\n >>> view = MyTree(object(), TestRequest())\n >>> view = view.publishTraverse(view.request, 'public')\n >>> view = view.publishTraverse(view.request, 'subdir1')\n >>> view = view.publishTraverse(view.request, 'test1.txt')\n >>> view = view.publishTraverse(view.request, 'nosuchpath')\n >>> view()\n Traceback (most recent call last):\n ...\n NotFound:...\n\n\nClean-up\n========\n\n >>> import shutil\n >>> shutil.rmtree(resource_dir)\n\n============================\nNEWS for lazr.exportedfolder\n============================\n\n1.0.0 (2009-10-26)\n==================\n\n- Initial release", "description_content_type": null, "docs_url": null, "download_url": "https://launchpad.net/lazr.exportedfolder/+download", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://launchpad.net/lazr.exportedfolder", "keywords": null, "license": "LGPL v3", "maintainer": null, "maintainer_email": null, "name": "lazr.exportedfolder", "package_url": "https://pypi.org/project/lazr.exportedfolder/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/lazr.exportedfolder/", "project_urls": { "Download": "https://launchpad.net/lazr.exportedfolder/+download", "Homepage": "https://launchpad.net/lazr.exportedfolder" }, "release_url": "https://pypi.org/project/lazr.exportedfolder/1.0.0/", "requires_dist": null, "requires_python": null, "summary": "View that gives access to the files in a folder.", "version": "1.0.0" }, "last_serial": 1740390, "releases": { "1.0.0": [] }, "urls": [] }