changeset 381:4c9eab0e283b

data: Fix problems with using non-existing metadata on a linked page. The page data wrapper now has supports for loader to raise a custom exception to declare that they can't return a value after all (this is especially useful for the wildcard loader). The returned value, if any, is also now cached properly.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 10 May 2015 00:37:28 -0700
parents f33712c4cfab
children 5c723d4bd2e4
files piecrust/data/base.py piecrust/data/linker.py
diffstat 2 files changed, 29 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/data/base.py	Sun May 10 00:34:21 2015 -0700
+++ b/piecrust/data/base.py	Sun May 10 00:37:28 2015 -0700
@@ -8,6 +8,13 @@
 logger = logging.getLogger(__name__)
 
 
+class LazyPageConfigLoaderHasNoValue(Exception):
+    """ An exception that can be returned when a loader for `LazyPageConfig`
+        can't return any value.
+    """
+    pass
+
+
 class LazyPageConfigData(object):
     """ An object that represents the configuration header of a page,
         but also allows for additional data. It's meant to be exposed
@@ -28,26 +35,34 @@
     def get(self, name):
         try:
             return self._getValue(name)
-        except KeyError:
+        except LazyPageConfigLoaderHasNoValue:
             return None
 
     def __getattr__(self, name):
         try:
             return self._getValue(name)
-        except KeyError:
+        except LazyPageConfigLoaderHasNoValue:
             raise AttributeError
 
     def __getitem__(self, name):
-        return self._getValue(name)
+        try:
+            return self._getValue(name)
+        except LazyPageConfigLoaderHasNoValue:
+            raise KeyError
 
     def _getValue(self, name):
         self._load()
 
+        if name in self._values:
+            return self._values[name]
+
         if self._loaders:
             loader = self._loaders.get(name)
             if loader is not None:
                 try:
                     self._values[name] = loader(self, name)
+                except LazyPageConfigLoaderHasNoValue:
+                    raise
                 except Exception as ex:
                     raise Exception(
                             "Error while loading attribute '%s' for: %s" %
@@ -61,16 +76,20 @@
                     if len(self._loaders) == 0:
                         self._loaders = None
 
-            elif name not in self._values:
+            else:
                 loader = self._loaders.get('*')
                 if loader is not None:
                     try:
                         self._values[name] = loader(self, name)
+                    except LazyPageConfigLoaderHasNoValue:
+                        raise
                     except Exception as ex:
                         raise Exception(
-                                "Error while loading attirbute '%s' for: %s" %
+                                "Error while loading attribute '%s' for: %s" %
                                 (name, self._page.rel_path)) from ex
+                    # We always keep the wildcard loader in the loaders list.
 
+        assert name in self._values
         return self._values[name]
 
     def _setValue(self, name, value):
--- a/piecrust/data/linker.py	Sun May 10 00:34:21 2015 -0700
+++ b/piecrust/data/linker.py	Sun May 10 00:37:28 2015 -0700
@@ -1,8 +1,7 @@
 import logging
 import collections
-from piecrust.data.base import PaginationData
+from piecrust.data.base import PaginationData, LazyPageConfigLoaderHasNoValue
 from piecrust.data.iterators import PageIterator
-from piecrust.sources.base import build_pages
 from piecrust.sources.interfaces import IPaginationSource, IListableSource
 
 
@@ -53,8 +52,10 @@
 
         self.mapLoader('*', self._linkerChildLoader)
 
-    def _linkerChildLoader(self, name):
-        return getattr(self.children, name)
+    def _linkerChildLoader(self, data, name):
+        if self.children and hasattr(self.children, name):
+            return getattr(self.children, name)
+        raise LazyPageConfigLoaderHasNoValue
 
 
 class LinkedPageDataBuilderIterator(object):