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