view piecrust/data/paginationdata.py @ 1188:a7c43131d871

bake: Fix file write flushing problem with Python 3.8+ Writing the cache files fails in Python 3.8 because it looks like flushing behaviour has changed. We need to explicitly flush. And even then, in very rare occurrences, it looks like it can still run into racing conditions, so we do a very hacky and ugly "retry" loop when fetching cached data :(
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 15 Jun 2021 22:36:23 -0700
parents 7e4742a60d14
children
line wrap: on
line source

import copy
import time
import logging
from piecrust.data.pagedata import LazyPageConfigData
from piecrust.sources.base import AbortedSourceUseError


logger = logging.getLogger(__name__)


class PaginationData(LazyPageConfigData):
    def __init__(self, page, extra_data=None):
        super().__init__(page)
        if extra_data:
            self._values.update(extra_data)

    def _load(self):
        from piecrust.uriutil import split_uri

        page = self._page
        set_val = self._setValue

        page_url = page.getUri()
        _, rel_url = split_uri(page.app, page_url)
        set_val('url', page_url)
        set_val('rel_url', rel_url)
        set_val('slug', rel_url)  # For backwards compatibility
        set_val('route', copy.deepcopy(page.source_metadata['route_params']))

        self._mapLoader('date', _load_date)
        self._mapLoader('datetime', _load_datetime)
        self._mapLoader('timestamp', _load_timestamp)
        self._mapLoader('mtime', _load_content_mtime)
        self._mapLoader('assets', _load_assets)
        self._mapLoader('family', _load_family)

        segment_names = page.config.get('segments')
        for name in segment_names:
            self._mapLoader('raw_' + name, _load_raw_segment)
            self._mapLoader(name, _load_rendered_segment)


def _load_assets(data, name):
    from piecrust.data.assetor import Assetor
    return Assetor(data._page)


def _load_family(data, name):
    from piecrust.data.linker import Linker
    return Linker(data._page.source, data._page.content_item)


def _load_date(data, name):
    page = data._page
    date_format = page.app.config.get('site/date_format')
    if date_format:
        return page.datetime.strftime(date_format)
    return None


def _load_datetime(data, name):
    dt = data._page.datetime
    return {
        'year': dt.year, 'month': dt.month, 'day': dt.day,
        'hour': dt.hour, 'minute': dt.minute, 'second': dt.second}


def _load_timestamp(data, name):
    page = data._page
    return time.mktime(page.datetime.timetuple())


def _load_content_mtime(data, name):
    return data._page.content_mtime


def _load_raw_segment(data, name):
    page = data._page
    return page.getSegment(name[4:])


def _load_rendered_segment(data, name):
    page = data._page

    do_render = True
    stack = page.app.env.render_ctx_stack
    if stack.hasPage(page):
        # This is the pagination data for the page that is currently
        # being rendered! Inception! But this is possible... so just
        # prevent infinite recursion.
        do_render = False

    if do_render:
        uri = page.getUri()
        try:
            from piecrust.rendering import (
                RenderingContext, render_page_segments)
            ctx = RenderingContext(page)
            render_result = render_page_segments(ctx)
            segs = render_result.segments
        except AbortedSourceUseError:
            raise
        except Exception as ex:
            logger.exception(ex)
            raise Exception(
                "Error rendering segments for '%s'" % uri) from ex
    else:
        segs = {}
        for name in page.config.get('segments'):
            segs[name] = "<unavailable: current page>"

    unmap_loader = data._unmapLoader
    set_val = data._setValue

    for k, v in segs.items():
        unmap_loader(k)
        set_val(k, v)

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

    return segs[name]