Mercurial > piecrust2
view piecrust/serving/middlewares.py @ 1051:971b4d67e82a
serve: Fix problems with assets disappearing between servings.
When an asset file changes, its source's pipeline is re-run. But that created
a bake record that only had that pipeline's output, so the other outputs were
incorrectly considered empty and therefore any stray files were removed. Now we
copy over bake records for the pipelines we don't run.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Fri, 26 Jan 2018 18:05:02 -0800 |
parents | 33a89139c284 |
children | 8d5b8a3dca02 |
line wrap: on
line source
import os.path from werkzeug.exceptions import NotFound, Forbidden from werkzeug.wrappers import Request, Response from werkzeug.wsgi import ClosingIterator from piecrust import RESOURCES_DIR, CACHE_DIR from piecrust.data.builder import ( DataBuildingContext, build_page_data) from piecrust.data.debug import build_var_debug_info from piecrust.page import PageNotFoundError from piecrust.routing import RouteNotFoundError from piecrust.serving.util import ( make_wrapped_file_response, get_requested_page, get_app_for_server) class PieCrustStaticResourcesMiddleware(object): """ WSGI middleware that serves static files from the `resources/server` directory in the PieCrust package. """ def __init__(self, app): self.app = app def __call__(self, environ, start_response): static_mount = '/__piecrust_static/' request = Request(environ) if request.path.startswith(static_mount): rel_req_path = request.path[len(static_mount):] mount = os.path.join(RESOURCES_DIR, 'server') full_path = os.path.join(mount, rel_req_path) try: response = make_wrapped_file_response( environ, request, full_path) return response(environ, start_response) except OSError: pass return self.app(environ, start_response) class PieCrustDebugMiddleware(object): """ WSGI middleware that handles debugging of PieCrust stuff, and runs the asset pipeline in an SSE thread. """ def __init__(self, app, appfactory, run_sse_check=None): self.app = app self.appfactory = appfactory self.run_sse_check = run_sse_check self._proc_loop = None self._out_dir = os.path.join( appfactory.root_dir, CACHE_DIR, appfactory.cache_key, 'server') self._handlers = { 'debug_info': self._getDebugInfo, 'werkzeug_shutdown': self._shutdownWerkzeug, 'pipeline_status': self._startSSEProvider} if not self.run_sse_check or self.run_sse_check(): # When using a server with code reloading, some implementations # use process forking and we end up going here twice. We only want # to start the pipeline loop in the inner process most of the # time so we let the implementation tell us if this is OK. from piecrust.serving.procloop import ProcessingLoop self._proc_loop = ProcessingLoop(self.appfactory, self._out_dir) self._proc_loop.start() def __call__(self, environ, start_response): debug_mount = '/__piecrust_debug/' request = Request(environ) if request.path.startswith(debug_mount): rel_req_path = request.path[len(debug_mount):] handler = self._handlers.get(rel_req_path) if handler is not None: return handler(request, start_response) return self.app(environ, start_response) def _getDebugInfo(self, request, start_response): app = get_app_for_server(self.appfactory) if not app.config.get('site/enable_debug_info'): return Forbidden() found = False page_path = request.args.get('page') try: req_page = get_requested_page(app, page_path) found = (req_page is not None) except (RouteNotFoundError, PageNotFoundError): pass if not found: return NotFound("No such page: %s" % page_path) ctx = DataBuildingContext(req_page.page, sub_num=req_page.sub_num) data = build_page_data(ctx) var_path = request.args.getlist('var') if not var_path: var_path = None output = build_var_debug_info(data, var_path) response = Response(output, mimetype='text/html') return response(request.environ, start_response) def _shutdownWerkzeug(self, request, start_response): shutdown_func = request.environ.get('werkzeug.server.shutdown') if shutdown_func is None: raise RuntimeError('Not running with the Werkzeug Server') shutdown_func() response = Response("Server shutting down...") return response(request.environ, start_response) def _startSSEProvider(self, request, start_response): from piecrust.serving.procloop import ( PipelineStatusServerSentEventProducer) provider = PipelineStatusServerSentEventProducer( self._proc_loop) it = provider.run() response = Response(it, mimetype='text/event-stream') response.headers['Cache-Control'] = 'no-cache' response.headers['Last-Event-ID'] = \ self._proc_loop.last_status_id return ClosingIterator( response(request.environ, start_response), [provider.close])