diff piecrust/serving/server.py @ 852:4850f8c21b6e

core: Start of the big refactor for PieCrust 3.0. * Everything is a `ContentSource`, including assets directories. * Most content sources are subclasses of the base file-system source. * A source is processed by a "pipeline", and there are 2 built-in pipelines, one for assets and one for pages. The asset pipeline is vaguely functional, but the page pipeline is completely broken right now. * Rewrite the baking process as just running appropriate pipelines on each content item. This should allow for better parallelization.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 17 May 2017 00:11:48 -0700
parents ab5c6a8ae90a
children f070a4fc033c
line wrap: on
line diff
--- a/piecrust/serving/server.py	Sat Apr 29 21:42:22 2017 -0700
+++ b/piecrust/serving/server.py	Wed May 17 00:11:48 2017 -0700
@@ -6,15 +6,15 @@
 import hashlib
 import logging
 from werkzeug.exceptions import (
-        NotFound, MethodNotAllowed, InternalServerError, HTTPException)
+    NotFound, MethodNotAllowed, InternalServerError, HTTPException)
 from werkzeug.wrappers import Request, Response
 from jinja2 import FileSystemLoader, Environment
 from piecrust import CACHE_DIR, RESOURCES_DIR
-from piecrust.rendering import PageRenderingContext, render_page
+from piecrust.rendering import RenderingContext, render_page
 from piecrust.routing import RouteNotFoundError
 from piecrust.serving.util import (
-        content_type_map, make_wrapped_file_response, get_requested_page,
-        get_app_for_server)
+    content_type_map, make_wrapped_file_response, get_requested_page,
+    get_app_for_server)
 from piecrust.sources.base import SourceNotFoundError
 
 
@@ -22,6 +22,8 @@
 
 
 class WsgiServer(object):
+    """ A WSGI application that serves a PieCrust website.
+    """
     def __init__(self, appfactory, **kwargs):
         self.server = Server(appfactory, **kwargs)
 
@@ -29,30 +31,11 @@
         return self.server._run_request(environ, start_response)
 
 
-class ServeRecord(object):
-    def __init__(self):
-        self.entries = {}
-
-    def addEntry(self, entry):
-        key = self._makeKey(entry.uri, entry.sub_num)
-        self.entries[key] = entry
-
-    def getEntry(self, uri, sub_num):
-        key = self._makeKey(uri, sub_num)
-        return self.entries.get(key)
-
-    def _makeKey(self, uri, sub_num):
-        return "%s:%s" % (uri, sub_num)
-
-
-class ServeRecordPageEntry(object):
-    def __init__(self, uri, sub_num):
-        self.uri = uri
-        self.sub_num = sub_num
-        self.used_source_names = set()
-
-
 class MultipleNotFound(HTTPException):
+    """ Represents a 404 (not found) error that tried to serve one or
+        more pages. It will report which pages it tried to serve
+        before failing.
+    """
     code = 404
 
     def __init__(self, description, nfes):
@@ -70,6 +53,8 @@
 
 
 class Server(object):
+    """ The PieCrust server.
+    """
     def __init__(self, appfactory,
                  enable_debug_info=True,
                  root_url='/',
@@ -78,12 +63,11 @@
         self.enable_debug_info = enable_debug_info
         self.root_url = root_url
         self.static_preview = static_preview
-        self._page_record = ServeRecord()
         self._out_dir = os.path.join(
-                appfactory.root_dir,
-                CACHE_DIR,
-                (appfactory.cache_key or 'default'),
-                'server')
+            appfactory.root_dir,
+            CACHE_DIR,
+            (appfactory.cache_key or 'default'),
+            'server')
 
     def _run_request(self, environ, start_response):
         try:
@@ -104,11 +88,17 @@
                          request.method)
             raise MethodNotAllowed()
 
-        # Also handle requests to a pipeline-built asset right away.
+        # Handle requests to a pipeline-built asset right away.
         response = self._try_serve_asset(environ, request)
         if response is not None:
             return response
 
+        # Same for page assets.
+        response = self._try_serve_page_asset(
+            self.appfactory.root_dir, environ, request)
+        if response is not None:
+            return response
+
         # Create the app for this request.
         app = get_app_for_server(self.appfactory,
                                  root_url=self.root_url)
@@ -118,14 +108,10 @@
             app.config.set('site/show_debug_info', True)
 
         # We'll serve page assets directly from where they are.
-        app.env.base_asset_url_format = self.root_url + '_asset/%path%'
+        app.config.set('site/asset_url_format',
+                       self.root_url + '_asset/%path%')
 
-        # Let's see if it can be a page asset.
-        response = self._try_serve_page_asset(app, environ, request)
-        if response is not None:
-            return response
-
-        # Nope. Let's see if it's an actual page.
+        # Let's try to serve a page.
         try:
             response = self._try_serve_page(app, environ, request)
             return response
@@ -152,23 +138,22 @@
             full_path = os.path.join(self._out_dir, rel_req_path)
 
         try:
-            response = make_wrapped_file_response(environ, request, full_path)
-            return response
+            return make_wrapped_file_response(environ, request, full_path)
         except OSError:
-            pass
-        return None
+            return None
 
-    def _try_serve_page_asset(self, app, environ, request):
+    def _try_serve_page_asset(self, app_root_dir, environ, request):
         if not request.path.startswith(self.root_url + '_asset/'):
             return None
 
         offset = len(self.root_url + '_asset/')
-        full_path = os.path.join(app.root_dir, request.path[offset:])
-        if not os.path.isfile(full_path):
+        full_path = os.path.join(app_root_dir, request.path[offset:])
+
+        try:
+            return make_wrapped_file_response(environ, request, full_path)
+        except OSError:
             return None
 
-        return make_wrapped_file_response(environ, request, full_path)
-
     def _try_serve_page(self, app, environ, request):
         # Find a matching page.
         req_page = get_requested_page(app, request.path)
@@ -181,33 +166,12 @@
             raise MultipleNotFound(msg, req_page.not_found_errors)
 
         # We have a page, let's try to render it.
-        render_ctx = PageRenderingContext(qp,
-                                          page_num=req_page.page_num,
-                                          force_render=True,
-                                          is_from_request=True)
-        if qp.route.is_generator_route:
-            qp.route.generator.prepareRenderContext(render_ctx)
-
-        # See if this page is known to use sources. If that's the case,
-        # just don't use cached rendered segments for that page (but still
-        # use them for pages that are included in it).
-        uri = qp.getUri()
-        entry = self._page_record.getEntry(uri, req_page.page_num)
-        if (qp.route.is_generator_route or entry is None or
-                entry.used_source_names):
-            cache_key = '%s:%s' % (uri, req_page.page_num)
-            app.env.rendered_segments_repository.invalidate(cache_key)
+        render_ctx = RenderingContext(qp, force_render=True)
+        qp.page.source.prepareRenderContext(render_ctx)
 
         # Render the page.
         rendered_page = render_page(render_ctx)
 
-        # Remember stuff for next time.
-        if entry is None:
-            entry = ServeRecordPageEntry(req_page.req_path, req_page.page_num)
-            self._page_record.addEntry(entry)
-        for pinfo in render_ctx.render_passes:
-            entry.used_source_names |= pinfo.used_source_names
-
         # Start doing stuff.
         page = rendered_page.page
         rp_content = rendered_page.content
@@ -216,10 +180,10 @@
         if app.config.get('site/show_debug_info'):
             now_time = time.perf_counter()
             timing_info = (
-                    '%8.1f ms' %
-                    ((now_time - app.env.start_time) * 1000.0))
+                '%8.1f ms' %
+                ((now_time - app.env.start_time) * 1000.0))
             rp_content = rp_content.replace(
-                    '__PIECRUST_TIMING_INFORMATION__', timing_info)
+                '__PIECRUST_TIMING_INFORMATION__', timing_info)
 
         # Build the response.
         response = Response()
@@ -311,4 +275,3 @@
         template += '.html'
         return super(ErrorMessageLoader, self).get_source(env, template)
 
-