Mercurial > piecrust2
view piecrust/serving/util.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 | 8adc27285d93 |
children | c8518f5cedbb |
line wrap: on
line source
import re import os.path import hashlib import logging import datetime from werkzeug.wrappers import Response from werkzeug.wsgi import wrap_file from piecrust.page import PageNotFoundError from piecrust.routing import RouteNotFoundError from piecrust.uriutil import split_sub_uri logger = logging.getLogger(__name__) def get_app_for_server(appfactory, root_url='/'): app = appfactory.create() app.config.set('site/root', root_url) app.config.set('server/is_serving', True) # We'll serve page assets directly from where they are. app.config.set('site/asset_url_format', root_url + '_asset/%path%') return app class RequestedPage(object): def __init__(self): self.page = None self.sub_num = 1 self.req_path = None self.not_found_errors = [] def find_routes(routes, uri, decomposed_uri=None): """ Returns routes matching the given URL. """ sub_num = 0 uri_no_sub = None if decomposed_uri is not None: uri_no_sub, sub_num = decomposed_uri res = [] for route in routes: route_params = route.matchUri(uri) if route_params is not None: res.append((route, route_params, 1)) if sub_num > 1: route_params = route.matchUri(uri_no_sub) if route_params is not None: res.append((route, route_params, sub_num)) return res def get_requested_page(app, req_path): # Remove the trailing slash to simplify how we parse URLs. root_url = app.config.get('site/root') if req_path != root_url: req_path = req_path.rstrip('/') # Try to find what matches the requested URL. # It could also be a sub-page (i.e. the URL ends with a page number), so # we try to also match the base URL (without the number). req_path_no_sub, sub_num = split_sub_uri(app, req_path) routes = find_routes(app.routes, req_path, (req_path_no_sub, sub_num)) if len(routes) == 0: raise RouteNotFoundError("Can't find route for: %s" % req_path) req_page = RequestedPage() for route, route_params, route_sub_num in routes: cur_req_path = req_path if route_sub_num > 1: cur_req_path = req_path_no_sub page = _get_requested_page_for_route(app, route, route_params) if page is not None: req_page.page = page req_page.sub_num = route_sub_num req_page.req_path = cur_req_path break req_page.not_found_errors.append(PageNotFoundError( "No path found for '%s' in source '%s'." % (cur_req_path, route.source_name))) return req_page def _get_requested_page_for_route(app, route, route_params): source = app.getSource(route.source_name) item = source.findContentFromRoute(route_params) if item is not None: return app.getPage(source, item) return None def load_mimetype_map(): mimetype_map = {} sep_re = re.compile(r'\s+') path = os.path.join(os.path.dirname(__file__), 'mime.types') with open(path, 'r') as f: for line in f: tokens = sep_re.split(line) if len(tokens) > 1: for t in tokens[1:]: mimetype_map[t] = tokens[0] return mimetype_map def make_wrapped_file_response(environ, request, path): logger.debug("Serving %s" % path) # Check if we can return a 304 status code. mtime = os.path.getmtime(path) etag_str = '%s$$%s' % (path, mtime) etag = hashlib.md5(etag_str.encode('utf8')).hexdigest() if etag in request.if_none_match: response = Response() response.status_code = 304 return response wrapper = wrap_file(environ, open(path, 'rb')) response = Response(wrapper) _, ext = os.path.splitext(path) response.set_etag(etag) response.last_modified = datetime.datetime.fromtimestamp(mtime) response.mimetype = mimetype_map.get( ext.lstrip('.'), 'text/plain') response.direct_passthrough = True return response mimetype_map = load_mimetype_map() content_type_map = { 'html': 'text/html', 'xml': 'text/xml', 'txt': 'text/plain', 'text': 'text/plain', 'css': 'text/css', 'xhtml': 'application/xhtml+xml', 'atom': 'application/atom+xml', # or 'text/xml'? 'rss': 'application/rss+xml', # or 'text/xml'? 'json': 'application/json'}