~launchpad-pqm/launchpad/devel

6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
1
= Serving directories of files =
2
3
LAZR adds special views that can be used to serve all the files under a
4
particular directory.
5
6
== ExportedFolder ==
7
8
This is the base implementation. To export a directory, you need to
9
subclass that view and provide a folder property returning the path of
10
the directory to expose.
11
12
    >>> import os
13
    >>> import tempfile
14
    >>> resource_dir = tempfile.mkdtemp(prefix='resources')
15
    >>> file(os.path.join(resource_dir, 'test.txt'), 'w').write('Text file')
16
    >>> file(os.path.join(resource_dir, 'image1.gif'), 'w').write(
17
    ...     'GIF file')
18
    >>> file(os.path.join(resource_dir, 'image2.png'), 'w').write(
19
    ...     'PNG file')
6574.1.9 by Francis J. Lacoste
Review comments.
20
    >>> os.mkdir(os.path.join(resource_dir, 'a_dir'))
21
    >>> file(os.path.join(resource_dir, 'other.txt'), 'w').write(
22
    ...     'Other file')
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
23
14606.4.15 by William Grant
Move folder and xml into lp.app and lp.services.
24
    >>> from lp.app.browser.folder import ExportedFolder
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
25
    >>> class MyFolder(ExportedFolder):
26
    ...     folder = resource_dir
27
28
That view provides the IBrowserPublisher interface necessary to handle
29
all the traversal logic.
30
31
    >>> from zope.interface.verify import verifyObject
32
    >>> from zope.publisher.interfaces.browser import IBrowserPublisher
8122.4.5 by Leonard Richardson
Switched over to the new testing.webservice file.
33
    >>> from lazr.restful.testing.webservice import FakeRequest
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
34
10304.5.11 by Leonard Richardson
Corrected the folder test.
35
    >>> view = MyFolder(object(), FakeRequest(version="devel"))
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
36
    >>> verifyObject(IBrowserPublisher, view)
37
    True
38
6574.1.6 by Francis J. Lacoste
Pre-review clean-up.
39
The view will serve the file that it traverses to.
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
40
41
    >>> view = view.publishTraverse(view.request, 'test.txt')
42
    >>> print view()
43
    Text file
44
6574.1.6 by Francis J. Lacoste
Pre-review clean-up.
45
It also sets the appropriate headers for cache control on the response.
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
46
47
    >>> for name in sorted(view.request.response.headers):
48
    ...     print "%s: %s" % (name, view.request.response.getHeader(name))
49
    Cache-Control: public...
50
    Content-Type: text/plain
51
    Expires: ...
52
    Last-Modified: ...
53
54
It accepts traversing to the file through an arbitrary revision
55
identifier.
56
10304.5.11 by Leonard Richardson
Corrected the folder test.
57
    >>> view = MyFolder(object(), FakeRequest(version="devel"))
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
58
    >>> view = view.publishTraverse(view.request, 'rev6510')
59
    >>> view = view.publishTraverse(view.request, 'image1.gif')
60
    >>> print view()
61
    GIF file
62
6753.5.1 by Diogo Matsubara
merge the twozero tour again
63
Requesting a directory raises a NotFound.
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
64
10304.5.11 by Leonard Richardson
Corrected the folder test.
65
    >>> view = MyFolder(object(), FakeRequest(version="devel"))
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
66
    >>> view = view.publishTraverse(view.request, 'a_dir')
67
    >>> view()
68
    Traceback (most recent call last):
69
      ...
70
    NotFound:...
71
6753.5.1 by Diogo Matsubara
merge the twozero tour again
72
By default, subdirectories are not exported. (See below on how to enable
73
this)
74
10304.5.11 by Leonard Richardson
Corrected the folder test.
75
    >>> view = MyFolder(object(), FakeRequest(version="devel"))
6574.1.9 by Francis J. Lacoste
Review comments.
76
    >>> view = view.publishTraverse(view.request, 'a_dir')
77
    >>> view = view.publishTraverse(view.request, 'other.txt')
78
    >>> view()
79
    Traceback (most recent call last):
80
      ...
81
    NotFound:...
82
6753.5.1 by Diogo Matsubara
merge the twozero tour again
83
Not requesting any file, also raises NotFound.
84
10304.5.11 by Leonard Richardson
Corrected the folder test.
85
    >>> view = MyFolder(object(), FakeRequest(version="devel"))
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
86
    >>> view()
87
    Traceback (most recent call last):
88
      ...
89
    NotFound:...
90
6753.5.1 by Diogo Matsubara
merge the twozero tour again
91
As requesting a non-existent file.
92
10304.5.11 by Leonard Richardson
Corrected the folder test.
93
    >>> view = MyFolder(object(), FakeRequest(version="devel"))
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
94
    >>> view = view.publishTraverse(view.request, 'image2')
95
    >>> view()
96
    Traceback (most recent call last):
97
      ...
98
    NotFound:...
99
100
101
== ExportedImageFolder ==
102
6574.1.6 by Francis J. Lacoste
Pre-review clean-up.
103
For images, it's often convenient not to request the extension. There is
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
104
an ExportedImageFolder subclass, that will accept serving an image file
105
without extension.  For example, requesting 'image1' or 'image2' will
6574.1.9 by Francis J. Lacoste
Review comments.
106
serve the correct file. The supported extensions are defined in the
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
107
image_extensions property.
108
14606.4.15 by William Grant
Move folder and xml into lp.app and lp.services.
109
    >>> from lp.app.browser.folder import ExportedImageFolder
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
110
111
    >>> class MyImageFolder(ExportedImageFolder):
112
    ...     folder = resource_dir
113
10304.5.11 by Leonard Richardson
Corrected the folder test.
114
    >>> view = MyImageFolder(object(), FakeRequest(version="devel"))
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
115
    >>> view.image_extensions
6574.1.9 by Francis J. Lacoste
Review comments.
116
    ('.png', '.gif')
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
117
118
    >>> view = view.publishTraverse(view.request, 'image2')
119
    >>> print view()
120
    PNG file
121
    >>> print view.request.response.getHeader('Content-Type')
122
    image/png
123
124
If a file without extension exists, that one will be served.
125
126
    >>> file(os.path.join(resource_dir, 'image3'), 'w').write(
127
    ...     'Image without extension')
128
    >>> file(os.path.join(resource_dir, 'image3.gif'), 'w').write(
129
    ...     'Image with extension')
130
10304.5.11 by Leonard Richardson
Corrected the folder test.
131
    >>> view = MyImageFolder(object(), FakeRequest(version="devel"))
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
132
    >>> view = view.publishTraverse(view.request, 'image3')
133
    >>> print view()
134
    Image without extension
135
10304.5.11 by Leonard Richardson
Corrected the folder test.
136
    >>> view = MyImageFolder(object(), FakeRequest(version="devel"))
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
137
    >>> view = view.publishTraverse(view.request, 'image3.gif')
138
    >>> print view()
139
    Image with extension
140
6574.1.6 by Francis J. Lacoste
Pre-review clean-up.
141
6753.5.1 by Diogo Matsubara
merge the twozero tour again
142
== Exporting trees ==
143
144
By default ExportedFolder doesn't export contained folders, but if the
145
export_subdirectories is set to True, it will allow traversing to
146
subdirectories.
147
148
    >>> os.mkdir(os.path.join(resource_dir, 'public'))
149
    >>> file(os.path.join(
150
    ...     resource_dir, 'public', 'test1.txt'), 'w').write('Public File')
151
    >>> os.mkdir(os.path.join(resource_dir, 'public', 'subdir1'))
152
    >>> file(os.path.join(
153
    ...     resource_dir, 'public', 'subdir1', 'test1.txt'), 'w').write(
154
    ...         'Sub file 1')
155
156
    >>> class MyTree(ExportedFolder):
157
    ...     folder = resource_dir
158
    ...     export_subdirectories = True
159
160
Traversing to a file in a subdirectory will now work.
161
10304.5.11 by Leonard Richardson
Corrected the folder test.
162
    >>> view = MyTree(object(), FakeRequest(version="devel"))
6753.5.1 by Diogo Matsubara
merge the twozero tour again
163
    >>> view = view.publishTraverse(view.request, 'public')
164
    >>> view = view.publishTraverse(view.request, 'subdir1')
165
    >>> view = view.publishTraverse(view.request, 'test1.txt')
166
    >>> print view()
167
    Sub file 1
168
169
But traversing to the subdirectory itself will raise a NotFound.
170
10304.5.11 by Leonard Richardson
Corrected the folder test.
171
    >>> view = MyTree(object(), FakeRequest(version="devel"))
6753.5.1 by Diogo Matsubara
merge the twozero tour again
172
    >>> view = view.publishTraverse(view.request, 'public')
173
    >>> print view()
174
    Traceback (most recent call last):
175
      ...
176
    NotFound:...
177
178
Trying to request a non-existent file, will also raise a NotFound.
179
10304.5.11 by Leonard Richardson
Corrected the folder test.
180
    >>> view = MyTree(object(), FakeRequest(version="devel"))
6753.5.1 by Diogo Matsubara
merge the twozero tour again
181
    >>> view = view.publishTraverse(view.request, 'public')
182
    >>> view = view.publishTraverse(view.request, 'nosuchfile.txt')
183
    >>> view()
184
    Traceback (most recent call last):
185
      ...
186
    NotFound:...
187
7146.1.7 by Maris Fogels
Added a test for traversing beyond existant file paths.
188
Traversing beyond an existing file to a non-existant file raises a
189
NotFound.
190
10304.5.11 by Leonard Richardson
Corrected the folder test.
191
    >>> view = MyTree(object(), FakeRequest(version="devel"))
7146.1.7 by Maris Fogels
Added a test for traversing beyond existant file paths.
192
    >>> view = view.publishTraverse(view.request, 'public')
193
    >>> view = view.publishTraverse(view.request, 'subdir1')
194
    >>> view = view.publishTraverse(view.request, 'test1.txt')
195
    >>> view = view.publishTraverse(view.request, 'nosuchpath')
196
    >>> view()
197
    Traceback (most recent call last):
198
      ...
199
    NotFound:...
200
6753.5.1 by Diogo Matsubara
merge the twozero tour again
201
6574.1.6 by Francis J. Lacoste
Pre-review clean-up.
202
== Clean-up ==
203
6574.1.4 by Francis J. Lacoste
Add doctest for ExportedFolder. Added an ExportedImageFolder.
204
    >>> import shutil
205
    >>> shutil.rmtree(resource_dir)