comparison piecrust/serving.py @ 190:430ee5b80962

serve: Make the server find assets generated by external tools. When a processor is bypassing PieCrust's pipeline, we don't know what output files they have produced, so if we don't find an asset in the record for a given path, we quickly check the output folder for any matching file there. If there is, just return it.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 11 Jan 2015 22:58:38 -0800
parents d47d9493bb0a
children 76e459d48c43
comparison
equal deleted inserted replaced
189:a333cdadf5b0 190:430ee5b80962
133 exc = None 133 exc = None
134 try: 134 try:
135 response = self._try_serve_page(app, environ, request) 135 response = self._try_serve_page(app, environ, request)
136 return response(environ, start_response) 136 return response(environ, start_response)
137 except (RouteNotFoundError, SourceNotFoundError) as ex: 137 except (RouteNotFoundError, SourceNotFoundError) as ex:
138 logger.exception(ex)
139 exc = NotFound(str(ex)) 138 exc = NotFound(str(ex))
140 except NotFound as ex: 139 except NotFound as ex:
141 exc = ex 140 exc = ex
142 except HTTPException: 141 except HTTPException:
143 raise 142 raise
164 entry = self._asset_record.findEntry(rel_req_path) 163 entry = self._asset_record.findEntry(rel_req_path)
165 if entry is None: 164 if entry is None:
166 # We don't know any asset that could have created this path. 165 # We don't know any asset that could have created this path.
167 # It could be a new asset that the user just created, but we'll 166 # It could be a new asset that the user just created, but we'll
168 # check for that later. 167 # check for that later.
168 # What we can do however is see if there's anything that already
169 # exists there, because it could have been created by a processor
170 # that bypasses structured processing (like e.g. the compass
171 # processor). In that case, just return that file, hoping it will
172 # be up-to-date.
173 full_path = os.path.join(self._out_dir, rel_req_path)
174 try:
175 response = self._make_wrapped_file_response(
176 environ, full_path)
177 logger.debug("Didn't find record entry, but found existing "
178 "output file at: %s" % rel_req_path)
179 return response
180 except OSError:
181 pass
169 return None 182 return None
170 183
171 # Yep, we know about this URL because we processed an asset that 184 # Yep, we know about this URL because we processed an asset that
172 # maps to it... make sure it's up to date by re-processing it 185 # maps to it... make sure it's up to date by re-processing it
173 # before serving. 186 # before serving.
185 r = pipeline.run(asset_in_path, delete=False, save_record=False, 198 r = pipeline.run(asset_in_path, delete=False, save_record=False,
186 previous_record=self._asset_record) 199 previous_record=self._asset_record)
187 assert len(r.entries) == 1 200 assert len(r.entries) == 1
188 self._asset_record.replaceEntry(r.entries[0]) 201 self._asset_record.replaceEntry(r.entries[0])
189 202
190 logger.debug("Serving %s" % asset_out_path) 203 return self._make_wrapped_file_response(environ, asset_out_path)
191 wrapper = wrap_file(environ, open(asset_out_path, 'rb'))
192 response = Response(wrapper)
193 _, ext = os.path.splitext(rel_req_path)
194 response.mimetype = self._mimetype_map.get(
195 ext.lstrip('.'), 'text/plain')
196 return response
197 204
198 def _try_serve_new_asset(self, app, environ, request): 205 def _try_serve_new_asset(self, app, environ, request):
199 logger.debug("Searching for a new asset with path: %s" % request.path) 206 logger.debug("Searching for a new asset with path: %s" % request.path)
200 mounts = app.assets_dirs 207 mounts = app.assets_dirs
201 pipeline = ProcessorPipeline( 208 pipeline = ProcessorPipeline(
212 if entry is None: 219 if entry is None:
213 return None 220 return None
214 221
215 asset_out_path = os.path.join(self._out_dir, rel_req_path) 222 asset_out_path = os.path.join(self._out_dir, rel_req_path)
216 logger.debug("Found new asset: %s" % entry.path) 223 logger.debug("Found new asset: %s" % entry.path)
217 logger.debug("Serving %s" % asset_out_path) 224 return self._make_wrapped_file_response(environ, asset_out_path)
218 wrapper = wrap_file(environ, open(asset_out_path, 'rb'))
219 response = Response(wrapper)
220 _, ext = os.path.splitext(rel_req_path)
221 response.mimetype = self._mimetype_map.get(
222 ext.lstrip('.'), 'text/plain')
223 return response
224 225
225 def _try_serve_page_asset(self, app, environ, request): 226 def _try_serve_page_asset(self, app, environ, request):
226 if not request.path.startswith('/_asset/'): 227 if not request.path.startswith('/_asset/'):
227 return None 228 return None
228 229
229 full_path = os.path.join(app.root_dir, request.path[len('/_asset/'):]) 230 full_path = os.path.join(app.root_dir, request.path[len('/_asset/'):])
230 if not os.path.isfile(full_path): 231 if not os.path.isfile(full_path):
231 return None 232 return None
232 233
233 logger.debug("Serving %s" % full_path) 234 return self._make_wrapped_file_response(environ, full_path)
234 wrapper = wrap_file(environ, open(full_path, 'rb'))
235 response = Response(wrapper)
236 _, ext = os.path.splitext(full_path)
237 response.mimetype = self._mimetype_map.get(
238 ext.lstrip('.'), 'text/plain')
239 return response
240 235
241 def _try_serve_page(self, app, environ, request): 236 def _try_serve_page(self, app, environ, request):
242 # Try to find what matches the requested URL. 237 # Try to find what matches the requested URL.
243 req_path = request.path 238 req_path = request.path
244 page_num = 1 239 page_num = 1
370 "falling back to uncompressed.") 365 "falling back to uncompressed.")
371 response.set_data(rp_content) 366 response.set_data(rp_content)
372 367
373 return response 368 return response
374 369
370 def _make_wrapped_file_response(self, environ, path):
371 logger.debug("Serving %s" % path)
372 wrapper = wrap_file(environ, open(path, 'rb'))
373 response = Response(wrapper)
374 _, ext = os.path.splitext(path)
375 response.mimetype = self._mimetype_map.get(
376 ext.lstrip('.'), 'text/plain')
377 return response
378
375 def _handle_error(self, exception, environ, start_response): 379 def _handle_error(self, exception, environ, start_response):
376 path = 'error' 380 path = 'error'
377 if isinstance(exception, NotFound): 381 if isinstance(exception, NotFound):
378 path += '404' 382 path += '404'
379 description = str(exception) 383 description = str(exception)