Mercurial > piecrust2
comparison piecrust/serving.py @ 371:c2ca72fb7f0b 2.0.0a8
caching: Use separate caches for config variants and other contexts.
* The `_cache` directory is now organized in multiple "sub-caches" for
different contexts.
* A new context is created when config variants or overrides are applied.
* `serve` context uses a different context that the other commends, to prevent
the `bake` command's output from messing up the preview server (e.g. with
how asset URLs are generated differently between the two).
* Fix a few places where the cache directory was referenced directly.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 03 May 2015 23:59:46 -0700 |
parents | 4b1019bb2533 |
children |
comparison
equal
deleted
inserted
replaced
370:a1bbe66cba03 | 371:c2ca72fb7f0b |
---|---|
64 return self.server._run_request(environ, start_response) | 64 return self.server._run_request(environ, start_response) |
65 | 65 |
66 | 66 |
67 class Server(object): | 67 class Server(object): |
68 def __init__(self, root_dir, | 68 def __init__(self, root_dir, |
69 debug=False, use_reloader=False, static_preview=True): | 69 debug=False, sub_cache_dir=None, |
70 use_reloader=False, static_preview=True): | |
70 self.root_dir = root_dir | 71 self.root_dir = root_dir |
71 self.debug = debug | 72 self.debug = debug |
73 self.sub_cache_dir = sub_cache_dir | |
72 self.use_reloader = use_reloader | 74 self.use_reloader = use_reloader |
73 self.static_preview = static_preview | 75 self.static_preview = static_preview |
74 self._out_dir = None | 76 self._out_dir = None |
75 self._page_record = None | 77 self._page_record = None |
76 self._proc_loop = None | 78 self._proc_loop = None |
78 | 80 |
79 def getWsgiApp(self): | 81 def getWsgiApp(self): |
80 # Bake all the assets so we know what we have, and so we can serve | 82 # Bake all the assets so we know what we have, and so we can serve |
81 # them to the client. We need a temp app for this. | 83 # them to the client. We need a temp app for this. |
82 app = PieCrust(root_dir=self.root_dir, debug=self.debug) | 84 app = PieCrust(root_dir=self.root_dir, debug=self.debug) |
83 self._out_dir = os.path.join(app.cache_dir, 'server') | 85 app._useSubCacheDir(self.sub_cache_dir) |
86 self._out_dir = os.path.join(app.sub_cache_dir, 'server') | |
84 self._page_record = ServeRecord() | 87 self._page_record = ServeRecord() |
85 | 88 |
86 if (not self.use_reloader or | 89 if (not self.use_reloader or |
87 os.environ.get('WERKZEUG_RUN_MAIN') == 'true'): | 90 os.environ.get('WERKZEUG_RUN_MAIN') == 'true'): |
88 # We don't want to run the processing loop here if this isn't | 91 # We don't want to run the processing loop here if this isn't |
127 if response is not None: | 130 if response is not None: |
128 return response(environ, start_response) | 131 return response(environ, start_response) |
129 | 132 |
130 # Create the app for this request. | 133 # Create the app for this request. |
131 app = PieCrust(root_dir=self.root_dir, debug=self.debug) | 134 app = PieCrust(root_dir=self.root_dir, debug=self.debug) |
135 app._useSubCacheDir(self.sub_cache_dir) | |
132 app.config.set('site/root', '/') | 136 app.config.set('site/root', '/') |
133 app.config.set('server/is_serving', True) | 137 app.config.set('server/is_serving', True) |
134 if (app.config.get('site/enable_debug_info') and | 138 if (app.config.get('site/enable_debug_info') and |
135 '!debug' in request.args): | 139 '!debug' in request.args): |
136 app.config.set('site/show_debug_info', True) | 140 app.config.set('site/show_debug_info', True) |
224 | 228 |
225 routes = find_routes(app.routes, req_path) | 229 routes = find_routes(app.routes, req_path) |
226 if len(routes) == 0: | 230 if len(routes) == 0: |
227 raise RouteNotFoundError("Can't find route for: %s" % req_path) | 231 raise RouteNotFoundError("Can't find route for: %s" % req_path) |
228 | 232 |
229 taxonomy = None | 233 rendered_page = None |
230 tax_terms = None | 234 first_not_found = None |
231 for route, route_metadata in routes: | 235 for route, route_metadata in routes: |
232 source = app.getSource(route.source_name) | 236 try: |
233 if route.taxonomy_name is None: | 237 logger.debug("Trying to render match from source '%s'." % |
234 factory = source.findPageFactory(route_metadata, MODE_PARSING) | 238 route.source_name) |
235 if factory is not None: | 239 rendered_page = self._try_render_page( |
240 app, route, route_metadata, page_num, req_path) | |
241 if rendered_page is not None: | |
236 break | 242 break |
237 else: | 243 except NotFound as nfe: |
238 taxonomy = app.getTaxonomy(route.taxonomy_name) | 244 if first_not_found is None: |
239 route_terms = route_metadata.get(taxonomy.term_name) | 245 first_not_found = nfe |
240 if route_terms is not None: | |
241 tax_page_ref = taxonomy.getPageRef(source.name) | |
242 factory = tax_page_ref.getFactory() | |
243 tax_terms = route.unslugifyTaxonomyTerm(route_terms) | |
244 factory.metadata[taxonomy.term_name] = tax_terms | |
245 break | |
246 else: | 246 else: |
247 raise SourceNotFoundError( | 247 raise SourceNotFoundError( |
248 "Can't find path for: %s (looked in: %s)" % | 248 "Can't find path for: %s (looked in: %s)" % |
249 (req_path, [r.source_name for r, _ in routes])) | 249 (req_path, [r.source_name for r, _ in routes])) |
250 | 250 |
251 # Build the page. | 251 # If we haven't found any good match, raise whatever exception we |
252 page = factory.buildPage() | 252 # first got. Otherwise, raise a generic exception. |
253 # We force the rendering of the page because it could not have | 253 if rendered_page is None: |
254 # changed, but include pages that did change. | 254 first_not_found = first_not_found or NotFound( |
255 qp = QualifiedPage(page, route, route_metadata) | 255 "This page couldn't be found.") |
256 render_ctx = PageRenderingContext(qp, | 256 raise first_not_found |
257 page_num=page_num, | 257 |
258 force_render=True) | 258 # Start doing stuff. |
259 if taxonomy is not None: | 259 page = rendered_page.page |
260 render_ctx.setTaxonomyFilter(taxonomy, tax_terms) | |
261 | |
262 # See if this page is known to use sources. If that's the case, | |
263 # just don't use cached rendered segments for that page (but still | |
264 # use them for pages that are included in it). | |
265 entry = self._page_record.getEntry(req_path, page_num) | |
266 if (taxonomy is not None or entry is None or | |
267 entry.used_source_names): | |
268 cache_key = '%s:%s' % (req_path, page_num) | |
269 app.env.rendered_segments_repository.invalidate(cache_key) | |
270 | |
271 # Render the page. | |
272 rendered_page = render_page(render_ctx) | |
273 rp_content = rendered_page.content | 260 rp_content = rendered_page.content |
274 | |
275 if taxonomy is not None: | |
276 paginator = rendered_page.data.get('pagination') | |
277 if (paginator and paginator.is_loaded and | |
278 len(paginator.items) == 0): | |
279 message = ("This URL matched a route for taxonomy '%s' but " | |
280 "no pages have been found to have it. This page " | |
281 "won't be generated by a bake." % taxonomy.name) | |
282 raise NotFound(message) | |
283 | |
284 if entry is None: | |
285 entry = ServeRecordPageEntry(req_path, page_num) | |
286 self._page_record.addEntry(entry) | |
287 for p, pinfo in render_ctx.render_passes.items(): | |
288 entry.used_source_names |= pinfo.used_source_names | |
289 | 261 |
290 # Profiling. | 262 # Profiling. |
291 if app.config.get('site/show_debug_info'): | 263 if app.config.get('site/show_debug_info'): |
292 now_time = time.clock() | 264 now_time = time.clock() |
293 timing_info = ('%8.1f ms' % | 265 timing_info = ('%8.1f ms' % |
338 logger.exception("Error compressing response, " | 310 logger.exception("Error compressing response, " |
339 "falling back to uncompressed.") | 311 "falling back to uncompressed.") |
340 response.set_data(rp_content) | 312 response.set_data(rp_content) |
341 | 313 |
342 return response | 314 return response |
315 | |
316 def _try_render_page(self, app, route, route_metadata, page_num, req_path): | |
317 # Match the route to an actual factory. | |
318 taxonomy_info = None | |
319 source = app.getSource(route.source_name) | |
320 if route.taxonomy_name is None: | |
321 factory = source.findPageFactory(route_metadata, MODE_PARSING) | |
322 if factory is None: | |
323 return None | |
324 else: | |
325 taxonomy = app.getTaxonomy(route.taxonomy_name) | |
326 route_terms = route_metadata.get(taxonomy.term_name) | |
327 if route_terms is None: | |
328 return None | |
329 | |
330 tax_page_ref = taxonomy.getPageRef(source.name) | |
331 factory = tax_page_ref.getFactory() | |
332 tax_terms = route.unslugifyTaxonomyTerm(route_terms) | |
333 route_metadata[taxonomy.term_name] = tax_terms | |
334 taxonomy_info = (taxonomy, tax_terms) | |
335 | |
336 # Build the page. | |
337 page = factory.buildPage() | |
338 # We force the rendering of the page because it could not have | |
339 # changed, but include pages that did change. | |
340 qp = QualifiedPage(page, route, route_metadata) | |
341 render_ctx = PageRenderingContext(qp, | |
342 page_num=page_num, | |
343 force_render=True) | |
344 if taxonomy_info is not None: | |
345 taxonomy, tax_terms = taxonomy_info | |
346 render_ctx.setTaxonomyFilter(taxonomy, tax_terms) | |
347 | |
348 # See if this page is known to use sources. If that's the case, | |
349 # just don't use cached rendered segments for that page (but still | |
350 # use them for pages that are included in it). | |
351 uri = qp.getUri() | |
352 assert uri == req_path | |
353 entry = self._page_record.getEntry(uri, page_num) | |
354 if (taxonomy_info is not None or entry is None or | |
355 entry.used_source_names): | |
356 cache_key = '%s:%s' % (uri, page_num) | |
357 app.env.rendered_segments_repository.invalidate(cache_key) | |
358 | |
359 # Render the page. | |
360 rendered_page = render_page(render_ctx) | |
361 | |
362 # Check if this page is a taxonomy page that actually doesn't match | |
363 # anything. | |
364 if taxonomy_info is not None: | |
365 paginator = rendered_page.data.get('pagination') | |
366 if (paginator and paginator.is_loaded and | |
367 len(paginator.items) == 0): | |
368 taxonomy = taxonomy_info[0] | |
369 message = ("This URL matched a route for taxonomy '%s' but " | |
370 "no pages have been found to have it. This page " | |
371 "won't be generated by a bake." % taxonomy.name) | |
372 raise NotFound(message) | |
373 | |
374 # Remember stuff for next time. | |
375 if entry is None: | |
376 entry = ServeRecordPageEntry(req_path, page_num) | |
377 self._page_record.addEntry(entry) | |
378 for p, pinfo in render_ctx.render_passes.items(): | |
379 entry.used_source_names |= pinfo.used_source_names | |
380 | |
381 # Ok all good. | |
382 return rendered_page | |
343 | 383 |
344 def _make_wrapped_file_response(self, environ, request, path): | 384 def _make_wrapped_file_response(self, environ, request, path): |
345 logger.debug("Serving %s" % path) | 385 logger.debug("Serving %s" % path) |
346 | 386 |
347 # Check if we can return a 304 status code. | 387 # Check if we can return a 304 status code. |