changeset 867:757fba54bfd3

refactor: Improve pagination and iterators to work with other sources. This makes the assets work as a pagination source again.
author Ludovic Chabant <ludovic@chabant.com>
date Mon, 12 Jun 2017 22:20:58 -0700
parents d9059257743c
children 8d25f76fce98
files piecrust/data/assetor.py piecrust/data/paginator.py piecrust/dataproviders/pageiterator.py
diffstat 3 files changed, 61 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/data/assetor.py	Mon Jun 12 22:10:50 2017 -0700
+++ b/piecrust/data/assetor.py	Mon Jun 12 22:20:58 2017 -0700
@@ -21,7 +21,7 @@
         self.uri = uri
 
 
-class Assetor(collections.abc.Mapping):
+class Assetor(collections.abc.Sequence):
     debug_render_doc = """Helps render URLs to files in the current page's
                           asset folder."""
     debug_render = []
@@ -29,7 +29,8 @@
 
     def __init__(self, page):
         self._page = page
-        self._cache = None
+        self._cache_map = None
+        self._cache_list = None
 
     def __getattr__(self, name):
         try:
@@ -38,36 +39,32 @@
         except KeyError:
             raise AttributeError()
 
-    def __getitem__(self, key):
+    def __getitem__(self, i):
         self._cacheAssets()
-        return self._cache[key].uri
-
-    def __iter__(self):
-        self._cacheAssets()
-        return self._cache.keys()
+        return self._cache_list[i]
 
     def __len__(self):
         self._cacheAssets()
-        return len(self._cache)
+        return len(self._cache_list)
 
     def _debugRenderAssetNames(self):
         self._cacheAssets()
         return list(self._cache.keys())
 
     def _cacheAssets(self):
-        if self._cache is not None:
+        if self._cache_map is not None:
             return
 
         source = self._page.source
         content_item = self._page.content_item
+        assets = source.getRelatedContents(content_item, REL_ASSETS)
 
-        assets = source.getRelatedContents(content_item, REL_ASSETS)
+        self._cache_map = {}
+        self._cache_list = []
+
         if assets is None:
-            self._cache = {}
             return
 
-        self._cache = {}
-
         app = source.app
         root_dir = app.root_dir
         asset_url_format = app.config.get('site/asset_url_format')
@@ -85,7 +82,7 @@
 
         for a in assets:
             name = a.metadata['name']
-            if name in self._cache:
+            if name in self._cache_map:
                 raise UnsupportedAssetsError(
                     "An asset with name '%s' already exists for item '%s'. "
                     "Do you have multiple assets with colliding names?" %
@@ -97,7 +94,8 @@
             uri_build_tokens['%filename%'] = a.metadata['filename'],
             uri = multi_replace(asset_url_format, uri_build_tokens)
 
-            self._cache[name] = _AssetInfo(a, uri)
+            self._cache_map[name] = _AssetInfo(a, uri)
+            self._cache_list.append(uri)
 
         stack = app.env.render_ctx_stack
         cur_ctx = stack.current_ctx
--- a/piecrust/data/paginator.py	Mon Jun 12 22:10:50 2017 -0700
+++ b/piecrust/data/paginator.py	Mon Jun 12 22:20:58 2017 -0700
@@ -90,9 +90,11 @@
         if ipp is not None:
             return ipp
 
-        ipp = self._source.config.get('items_per_page')
-        if ipp is not None:
-            return ipp
+        from piecrust.sources.base import ContentSource
+        if isinstance(self._source, ContentSource):
+            ipp = self._source.config.get('items_per_page')
+            if ipp is not None:
+                return ipp
 
         raise Exception("No way to figure out how many items to display "
                         "per page.")
@@ -188,11 +190,11 @@
         from piecrust.data.filters import PaginationFilter
         from piecrust.dataproviders.pageiterator import (
             PageIterator, HardCodedFilterIterator)
+        from piecrust.sources.base import ContentSource
 
         self._iterator = PageIterator(
             self._source,
             current_page=self._page)
-        #self._iterator._iter_event += self._onIteration
 
         if self._pgn_filter is not None:
             pag_fil = PaginationFilter()
@@ -206,7 +208,8 @@
 
         self._iterator._lockIterator()
 
-        self._onIteration(self._iterator)
+        if isinstance(self._source, ContentSource):
+            self._onIteration(self._iterator)
 
     def _getPageUri(self, index):
         return self._page.getUri(index)
--- a/piecrust/dataproviders/pageiterator.py	Mon Jun 12 22:10:50 2017 -0700
+++ b/piecrust/dataproviders/pageiterator.py	Mon Jun 12 22:20:58 2017 -0700
@@ -3,7 +3,7 @@
 from piecrust.data.paginationdata import PaginationData
 from piecrust.events import Event
 from piecrust.dataproviders.base import DataProvider
-from piecrust.sources.base import AbortedSourceUseError
+from piecrust.sources.base import ContentSource, AbortedSourceUseError
 
 
 logger = logging.getLogger(__name__)
@@ -13,6 +13,7 @@
     def __init__(self):
         self.it = None
         self.iterated = False
+        self.source_name = None
 
 
 class PageIteratorDataProvider(DataProvider):
@@ -51,13 +52,14 @@
             i = _ItInfo()
             i.it = PageIterator(source, current_page=self._page)
             i.it._iter_event += self._onIteration
+            i.source_name = source.name
             self._its.append(i)
 
     def _onIteration(self, it):
         ii = next(filter(lambda i: i.it == it, self._its))
         if not ii.iterated:
             rcs = self._app.env.render_ctx_stack
-            rcs.current_ctx.addUsedSource(self._source.name)
+            rcs.current_ctx.addUsedSource(ii.source_name)
             ii.iterated = True
 
     def _debugRenderDoc(self):
@@ -67,6 +69,7 @@
 class PageIterator:
     def __init__(self, source, *, current_page=None):
         self._source = source
+        self._is_content_source = isinstance(source, ContentSource)
         self._cache = None
         self._pagination_slicer = None
         self._has_sorter = False
@@ -75,7 +78,7 @@
         self._locked = False
         self._iter_event = Event()
         self._current_page = current_page
-        self._it = PageContentSourceIterator(self._source)
+        self._initIterator()
 
     @property
     def total_count(self):
@@ -215,11 +218,18 @@
     def _ensureSorter(self):
         if self._has_sorter:
             return
-        self._it = DateSortIterator(self._it, reverse=True)
+        if self._is_content_source:
+            self._it = DateSortIterator(self._it, reverse=True)
         self._has_sorter = True
 
+    def _initIterator(self):
+        if self._is_content_source:
+            self._it = PageContentSourceIterator(self._source)
+        else:
+            self._it = GenericSourceIterator(self._source)
+
     def _unload(self):
-        self._it = PageContentSourceIterator(self._source)
+        self._initIterator()
         self._cache = None
         self._paginationSlicer = None
         self._has_sorter = False
@@ -230,27 +240,29 @@
         if self._cache is not None:
             return
 
-        if self._source.app.env.abort_source_use:
-            if self._current_page is not None:
-                logger.debug("Aborting iteration of '%s' from: %s." %
-                             (self._source.name,
-                              self._current_page.content_spec))
-            else:
-                logger.debug("Aborting iteration of '%s'." %
-                             self._source.name)
-            raise AbortedSourceUseError()
+        if self._is_content_source:
+            if self._source.app.env.abort_source_use:
+                if self._current_page is not None:
+                    logger.debug("Aborting iteration of '%s' from: %s." %
+                                 (self._source.name,
+                                  self._current_page.content_spec))
+                else:
+                    logger.debug("Aborting iteration of '%s'." %
+                                 self._source.name)
+                raise AbortedSourceUseError()
 
         self._ensureSorter()
 
-        tail_it = PaginationDataBuilderIterator(self._it, self._source.route)
-        self._cache = list(tail_it)
+        if self._is_content_source:
+            self._it = PaginationDataBuilderIterator(self._it)
+
+        self._cache = list(self._it)
 
         if (self._current_page is not None and
                 self._pagination_slicer is not None):
             pn = [self._pagination_slicer.prev_page,
                   self._pagination_slicer.next_page]
-            pn_it = PaginationDataBuilderIterator(iter(pn),
-                                                  self._source.route)
+            pn_it = PaginationDataBuilderIterator(iter(pn))
             self._prev_page, self._next_page = (list(pn_it))
 
         self._iter_event.fire(self)
@@ -367,9 +379,8 @@
 
 
 class PaginationDataBuilderIterator:
-    def __init__(self, it, route):
+    def __init__(self, it):
         self.it = it
-        self.route = route
 
     def __iter__(self):
         for page in self.it:
@@ -378,3 +389,11 @@
             else:
                 yield None
 
+
+class GenericSourceIterator:
+    def __init__(self, source):
+        self.source = source
+        self.it = None
+
+    def __iter__(self):
+        yield from self.source