diff piecrust/routing.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 58ebf50235a5
children 08e02c2a2a1a
line wrap: on
line diff
--- a/piecrust/routing.py	Sat Apr 29 21:42:22 2017 -0700
+++ b/piecrust/routing.py	Wed May 17 00:11:48 2017 -0700
@@ -1,6 +1,5 @@
 import re
 import os.path
-import copy
 import logging
 import urllib.parse
 from werkzeug.utils import cached_property
@@ -10,7 +9,8 @@
 
 
 route_re = re.compile(r'%((?P<qual>[\w\d]+):)?(?P<var>\+)?(?P<name>\w+)%')
-route_esc_re = re.compile(r'\\%((?P<qual>[\w\d]+)\\:)?(?P<var>\\\+)?(?P<name>\w+)\\%')
+route_esc_re = re.compile(
+    r'\\%((?P<qual>[\w\d]+)\\:)?(?P<var>\\\+)?(?P<name>\w+)\\%')
 ugly_url_cleaner = re.compile(r'\.html$')
 
 
@@ -22,15 +22,6 @@
     pass
 
 
-def create_route_metadata(page):
-    route_metadata = copy.deepcopy(page.source_metadata)
-    return route_metadata
-
-
-ROUTE_TYPE_SOURCE = 0
-ROUTE_TYPE_GENERATOR = 1
-
-
 class RouteParameter(object):
     TYPE_STRING = 0
     TYPE_PATH = 1
@@ -46,29 +37,21 @@
 class Route(object):
     """ Information about a route for a PieCrust application.
         Each route defines the "shape" of an URL and how it maps to
-        sources and generators.
+        content sources.
     """
     def __init__(self, app, cfg):
         self.app = app
 
-        self.source_name = cfg.get('source')
-        self.generator_name = cfg.get('generator')
-        if not self.source_name and not self.generator_name:
-            raise InvalidRouteError(
-                    "Both `source` and `generator` are specified.")
-
+        self.source_name = cfg['source']
         self.uri_pattern = cfg['url'].lstrip('/')
 
-        if self.is_source_route:
-            self.supported_params = self.source.getSupportedRouteParameters()
-        else:
-            self.supported_params = self.generator.getSupportedRouteParameters()
+        self.supported_params = self.source.getSupportedRouteParameters()
 
         self.pretty_urls = app.config.get('site/pretty_urls')
         self.trailing_slash = app.config.get('site/trailing_slash')
         self.show_debug_info = app.config.get('site/show_debug_info')
         self.pagination_suffix_format = app.config.get(
-                '__cache/pagination_suffix_format')
+            '__cache/pagination_suffix_format')
         self.uri_root = app.config.get('site/root')
 
         self.uri_params = []
@@ -87,9 +70,9 @@
         # (maybe there's a better way to do it but I can't think of any
         # right now)
         uri_pattern_no_path = (
-                route_re.sub(self._uriNoPathRepl, self.uri_pattern)
-                .replace('//', '/')
-                .rstrip('/'))
+            route_re.sub(self._uriNoPathRepl, self.uri_pattern)
+            .replace('//', '/')
+            .rstrip('/'))
         if uri_pattern_no_path != self.uri_pattern:
             p = route_esc_re.sub(self._uriPatternRepl,
                                  re.escape(uri_pattern_no_path)) + '$'
@@ -109,43 +92,15 @@
             last_param = self.getParameter(self.uri_params[-1])
             self.func_has_variadic_parameter = last_param.variadic
 
-    @property
-    def route_type(self):
-        if self.source_name:
-            return ROUTE_TYPE_SOURCE
-        elif self.generator_name:
-            return ROUTE_TYPE_GENERATOR
-        else:
-            raise InvalidRouteError()
-
-    @property
-    def is_source_route(self):
-        return self.route_type == ROUTE_TYPE_SOURCE
-
-    @property
-    def is_generator_route(self):
-        return self.route_type == ROUTE_TYPE_GENERATOR
-
     @cached_property
     def source(self):
-        if not self.is_source_route:
-            return InvalidRouteError("This is not a source route.")
         for src in self.app.sources:
             if src.name == self.source_name:
                 return src
-        raise Exception("Can't find source '%s' for route '%s'." % (
+        raise Exception(
+            "Can't find source '%s' for route '%s'." % (
                 self.source_name, self.uri_pattern))
 
-    @cached_property
-    def generator(self):
-        if not self.is_generator_route:
-            return InvalidRouteError("This is not a generator route.")
-        for gen in self.app.generators:
-            if gen.name == self.generator_name:
-                return gen
-        raise Exception("Can't find generator '%s' for route '%s'." % (
-                self.generator_name, self.uri_pattern))
-
     def hasParameter(self, name):
         return any(lambda p: p.param_name == name, self.supported_params)
 
@@ -159,8 +114,8 @@
     def getParameterType(self, name):
         return self.getParameter(name).param_type
 
-    def matchesMetadata(self, route_metadata):
-        return set(self.uri_params).issubset(route_metadata.keys())
+    def matchesParameters(self, route_params):
+        return set(self.uri_params).issubset(route_params.keys())
 
     def matchUri(self, uri, strict=False):
         if not uri.startswith(self.uri_root):
@@ -172,42 +127,42 @@
         elif self.trailing_slash:
             uri = uri.rstrip('/')
 
-        route_metadata = None
+        route_params = None
         m = self.uri_re.match(uri)
         if m:
-            route_metadata = m.groupdict()
+            route_params = m.groupdict()
         if self.uri_re_no_path:
             m = self.uri_re_no_path.match(uri)
             if m:
-                route_metadata = m.groupdict()
-        if route_metadata is None:
+                route_params = m.groupdict()
+        if route_params is None:
             return None
 
         if not strict:
             # When matching URIs, if the URI is a match but is missing some
-            # metadata, fill those up with empty strings. This can happen if,
+            # parameters, fill those up with empty strings. This can happen if,
             # say, a route's pattern is `/foo/%slug%`, and we're matching an
             # URL like `/foo`.
-            matched_keys = set(route_metadata.keys())
+            matched_keys = set(route_params.keys())
             missing_keys = set(self.uri_params) - matched_keys
             for k in missing_keys:
                 if self.getParameterType(k) != RouteParameter.TYPE_PATH:
                     return None
-                route_metadata[k] = ''
+                route_params[k] = ''
 
-        for k in route_metadata:
-            route_metadata[k] = self._coerceRouteParameter(
-                    k, route_metadata[k])
+        for k in route_params:
+            route_params[k] = self._coerceRouteParameter(
+                k, route_params[k])
 
-        return route_metadata
+        return route_params
 
-    def getUri(self, route_metadata, *, sub_num=1):
-        route_metadata = dict(route_metadata)
-        for k in route_metadata:
-            route_metadata[k] = self._coerceRouteParameter(
-                    k, route_metadata[k])
+    def getUri(self, route_params, *, sub_num=1):
+        route_params = dict(route_params)
+        for k in route_params:
+            route_params[k] = self._coerceRouteParameter(
+                k, route_params[k])
 
-        uri = self.uri_format % route_metadata
+        uri = self.uri_format % route_params
         suffix = None
         if sub_num > 1:
             # Note that we know the pagination suffix starts with a slash.
@@ -258,9 +213,9 @@
 
         if len(args) < fixed_param_count:
             raise Exception(
-                    "Route function '%s' expected %d arguments, "
-                    "got %d: %s" %
-                    (self.func_name, fixed_param_count, len(args), args))
+                "Route function '%s' expected %d arguments, "
+                "got %d: %s" %
+                (self.func_name, fixed_param_count, len(args), args))
 
         if self.func_has_variadic_parameter:
             coerced_args = list(args[:fixed_param_count])
@@ -270,15 +225,14 @@
         else:
             coerced_args = args
 
-        metadata = {}
+        route_params = {}
         for arg_name, arg_val in zip(self.uri_params, coerced_args):
-            metadata[arg_name] = self._coerceRouteParameter(
-                    arg_name, arg_val)
+            route_params[arg_name] = self._coerceRouteParameter(
+                arg_name, arg_val)
 
-        if self.is_generator_route:
-            self.generator.onRouteFunctionUsed(self, metadata)
+        self.source.onRouteFunctionUsed(self, route_params)
 
-        return self.getUri(metadata)
+        return self.getUri(route_params)
 
     def _uriFormatRepl(self, m):
         if m.group('qual') or m.group('var'):
@@ -350,32 +304,9 @@
         return name
 
 
-class CompositeRouteFunction(object):
-    def __init__(self):
-        self._routes = []
-        self._arg_names = None
-
-    def addFunc(self, route):
-        if self._arg_names is None:
-            self._arg_names = list(route.uri_params)
-
-        if route.uri_params != self._arg_names:
-            raise Exception("Cannot merge route function with arguments '%s' "
-                            "with route function with arguments '%s'." %
-                            (route.uri_params, self._arg_names))
-        self._routes.append(route)
+class RouteFunction:
+    def __init__(self, route):
+        self._route = route
 
     def __call__(self, *args, **kwargs):
-        if len(self._routes) == 1 or len(args) == len(self._arg_names):
-            return self._routes[0].execTemplateFunc(*args, **kwargs)
-
-        if len(args) == len(self._arg_names) + 1:
-            f_args = args[:-1]
-            for r in self._routes:
-                if r.source_name == args[-1]:
-                    return r.execTemplateFunc(*f_args, **kwargs)
-            raise Exception("No such source: %s" % args[-1])
-
-        raise Exception("Incorrect number of arguments for route function. "
-                        "Expected '%s', got '%s'" % (self._arg_names, args))
-
+        return self._route.execTemplateFunc(*args, **kwargs)