view piecrust/data/base.py @ 6:f5ca5c5bed85

More Python 3 fixes, modularization, and new unit tests.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 16 Aug 2014 08:15:30 -0700
parents 474c9882decf
children 30a42341cfa8
line wrap: on
line source

import time
import logging
from piecrust.data.assetor import Assetor


logger = logging.getLogger(__name__)


class IPaginationSource(object):
    def getItemsPerPage(self):
        raise NotImplementedError()

    def getSourceIterator(self):
        raise NotImplementedError()

    def getSorterIterator(self, it):
        raise NotImplementedError()

    def getTailIterator(self, it):
        raise NotImplementedError()

    def getPaginationFilter(self, page):
        raise NotImplementedError()


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
        to the templating system.
    """
    def __init__(self, page):
        self._page = page
        self._values = None
        self._loaders = None

    @property
    def page(self):
        return self._page

    def __getitem__(self, name):
        self._load()

        if self._loaders:
            loader = self._loaders.get(name)
            if loader is not None:
                try:
                    self._values[name] = loader(self, name)
                except Exception as ex:
                    logger.error("Error while loading attribute '%s' for: %s"
                            % (name, self._page.path))
                    logger.exception(ex)
                    raise Exception("Internal Error: %s" % ex)

                # We need to double-check `_loaders` here because
                # the loader could have removed all loaders, which
                # would set this back to `None`.
                if self._loaders is not None:
                    del self._loaders[name]
                    if len(self._loaders) == 0:
                        self._loaders = None

        return self._values[name]

    def setValue(self, name, value):
        self._values[name] = value

    def mapLoader(self, attr_name, loader):
        if loader is None:
            if self._loaders is None or attr_name not in self._loaders:
                return
            del self._loaders[attr_name]
            if len(self._loaders) == 0:
                self._loaders = None
            return

        if self._loaders is None:
            self._loaders = {}
        if attr_name in self._loaders:
            raise Exception("A loader has already been mapped for: %s" %
                    attr_name)
        self._loaders[attr_name] = loader

    def _load(self):
        if self._values is not None:
            return
        self._values = dict(self._page.config.get())
        try:
            self._loadCustom()
        except Exception as ex:
            logger.error("Error while loading data for: %s" % self._page.path)
            logger.exception(ex)
            raise Exception("Internal Error: %s" % ex)

    def _loadCustom(self):
        pass


class PaginationData(LazyPageConfigData):
    def __init__(self, page):
        super(PaginationData, self).__init__(page)

    def _get_uri(self):
        page = self._page
        route = page.app.getRoute(page.source.name, page.source_metadata)
        if route is None:
            raise Exception("Can't get route for page: %s" % page.path)
        return route.getUri(page.source_metadata)

    def _loadCustom(self):
        page_url = self._get_uri()
        self.setValue('url', page_url)
        self.setValue('slug', page_url)
        self.setValue('timestamp',
                time.mktime(self.page.datetime.timetuple()))
        date_format = self.page.app.config.get('site/date_format')
        if date_format:
            self.setValue('date', self.page.datetime.strftime(date_format))

        assetor = Assetor(self.page, page_url)
        self.setValue('assets', assetor)

        segment_names = self.page.config.get('segments')
        for name in segment_names:
            self.mapLoader(name, self._load_rendered_segment)

    def _load_rendered_segment(self, data, name):
        from piecrust.rendering import PageRenderingContext, render_page_segments

        assert self is data
        uri = self._get_uri()
        try:
            ctx = PageRenderingContext(self._page, uri)
            segs = render_page_segments(ctx)
        except Exception as e:
            logger.exception("Error rendering segments for '%s': %s" % (uri, e))
            raise

        for k, v in segs.items():
            self.mapLoader(k, None)
            self.setValue(k, v)

        if 'content.abstract' in segs:
            self.setValue('content', segs['content.abstract'])
            self.setValue('has_more', True)
            if name == 'content':
                return segs['content.abstract']

        return segs[name]