diff piecrust/data/builder.py @ 440:32c7c2d219d2

performance: Refactor how data is managed to reduce copying. * Make use of `collections.abc.Mapping` to better identify things that are supposed to look like dictionaries. * Instead of handling "overlay" of data in a dict tree in each different data object, make all objects `Mapping`s and handle merging at a higher level with the new `MergedMapping` object. * Since this new object is read-only, remove the need for deep-copying of app and page configurations. * Split data classes into separate modules.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 28 Jun 2015 08:22:39 -0700
parents f1b759c188b0
children 93b656f0af54
line wrap: on
line diff
--- a/piecrust/data/builder.py	Sat Jun 27 22:28:32 2015 -0700
+++ b/piecrust/data/builder.py	Sun Jun 28 08:22:39 2015 -0700
@@ -1,15 +1,12 @@
-import re
-import time
-import copy
 import logging
 from werkzeug.utils import cached_property
-from piecrust import APP_VERSION
-from piecrust.configuration import merge_dicts
 from piecrust.data.assetor import Assetor
-from piecrust.data.base import LazyPageConfigData
-from piecrust.data.debug import build_debug_info
+from piecrust.data.base import MergedMapping
 from piecrust.data.linker import PageLinkerData
+from piecrust.data.pagedata import PageData
 from piecrust.data.paginator import Paginator
+from piecrust.data.piecrustdata import PieCrustData
+from piecrust.data.providersdata import DataProvidersData
 from piecrust.uriutil import split_sub_uri
 
 
@@ -36,10 +33,10 @@
     app = ctx.app
     page = ctx.page
     first_uri, _ = split_sub_uri(app, ctx.uri)
+    pgn_source = ctx.pagination_source or get_default_pagination_source(page)
 
     pc_data = PieCrustData()
-    config_data = LazyPageConfigData(page)
-    pgn_source = ctx.pagination_source or get_default_pagination_source(page)
+    config_data = PageData(page, ctx)
     paginator = Paginator(page, pgn_source,
                           page_num=ctx.page_num,
                           pgn_filter=ctx.pagination_filter)
@@ -53,20 +50,11 @@
             'family': linker
             }
 
-    for k, v in page.source_metadata.items():
-        config_data.mapValue(k, copy.deepcopy(v))
-    config_data.mapValue('url', ctx.uri, override_existing=True)
-    config_data.mapValue('timestamp', time.mktime(page.datetime.timetuple()),
-                         override_existing=True)
-    date_format = app.config.get('site/date_format')
-    if date_format:
-        config_data.mapValue('date', page.datetime.strftime(date_format),
-                             override_existing=True)
-
     #TODO: handle slugified taxonomy terms.
 
-    site_data = build_site_data(page)
-    merge_dicts(data, site_data)
+    site_data = app.config.getAll()
+    providers_data = DataProvidersData(page)
+    data = MergedMapping([data, providers_data, site_data])
 
     # Do this at the end because we want all the data to be ready to be
     # displayed in the debugger window.
@@ -82,58 +70,7 @@
         if name in page_data:
             logger.warning("Content segment '%s' will hide existing data." %
                            name)
-        page_data[name] = txt
-
-
-class PieCrustData(object):
-    debug_render = ['version', 'url', 'branding', 'debug_info']
-    debug_render_invoke = ['version', 'url', 'branding', 'debug_info']
-    debug_render_redirect = {'debug_info': '_debugRenderDebugInfo'}
-
-    def __init__(self):
-        self.version = APP_VERSION
-        self.url = 'http://bolt80.com/piecrust/'
-        self.branding = 'Baked with <em><a href="%s">PieCrust</a> %s</em>.' % (
-                'http://bolt80.com/piecrust/', APP_VERSION)
-        self._page = None
-        self._data = None
-
-    @property
-    def debug_info(self):
-        if self._page is not None and self._data is not None:
-            try:
-                return build_debug_info(self._page, self._data)
-            except Exception as ex:
-                logger.exception(ex)
-                return ('An error occured while generating debug info. '
-                        'Please check the logs.')
-        return ''
-
-    def _enableDebugInfo(self, page, data):
-        self._page = page
-        self._data = data
-
-    def _debugRenderDebugInfo(self):
-        return "The very thing you're looking at!"
-
-
-re_endpoint_sep = re.compile(r'[\/\.]')
-
-
-def build_site_data(page):
-    app = page.app
-    data = app.config.getDeepcopy(app.debug)
-    for source in app.sources:
-        endpoint_bits = re_endpoint_sep.split(source.data_endpoint)
-        endpoint = data
-        for e in endpoint_bits[:-1]:
-            if e not in endpoint:
-                endpoint[e] = {}
-            endpoint = endpoint[e]
-        user_data = endpoint.get(endpoint_bits[-1])
-        provider = source.buildDataProvider(page, user_data)
-        endpoint[endpoint_bits[-1]] = provider
-    return data
+    page_data._prependMapping(contents)
 
 
 def get_default_pagination_source(page):