view piecrust/generation/base.py @ 736:13ec290bfc13

bake: Fix some crashes with new blog archive/taxonomy for incremental bakes.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 01 Jun 2016 22:46:11 -0700
parents 606f6d57b5df
children 661f7ba15762
line wrap: on
line source

from werkzeug.utils import cached_property
from piecrust.baking.records import BakeRecordEntry
from piecrust.baking.worker import save_factory, JOB_BAKE
from piecrust.configuration import ConfigurationError
from piecrust.routing import create_route_metadata
from piecrust.sources.pageref import PageRef


class InvalidRecordExtraKey(Exception):
    pass


class PageGeneratorBakeContext(object):
    def __init__(self, app, record, pool, generator):
        self._app = app
        self._record = record
        self._pool = pool
        self._generator = generator
        self._job_queue = []
        self._is_running = False

    def getRecordExtraKey(self, seed):
        return '%s:%s' % (self._generator.name, seed)

    def matchesRecordExtraKey(self, extra_key):
        return (extra_key is not None and
                extra_key.startswith(self._generator.name + ':'))

    def getSeedFromRecordExtraKey(self, extra_key):
        if not self.matchesRecordExtraKey(extra_key):
            raise InvalidRecordExtraKey("Invalid extra key: %s" % extra_key)
        return extra_key[len(self._generator.name) + 1:]

    def getAllPageRecords(self):
        return self._record.transitions.values()

    def getBakedPageRecords(self):
        for prev, cur in self.getAllPageRecords():
            if cur and cur.was_any_sub_baked:
                yield (prev, cur)

    def collapseRecord(self, entry):
        self._record.collapseEntry(entry)

    def queueBakeJob(self, page_fac, route, extra_route_metadata, seed):
        if self._is_running:
            raise Exception("The job queue is running.")

        extra_key = self.getRecordExtraKey(seed)
        entry = BakeRecordEntry(
                page_fac.source.name,
                page_fac.path,
                extra_key)
        self._record.addEntry(entry)

        page = page_fac.buildPage()
        route_metadata = create_route_metadata(page)
        route_metadata.update(extra_route_metadata)
        uri = route.getUri(route_metadata)
        override_entry = self._record.getOverrideEntry(page.path, uri)
        if override_entry is not None:
            override_source = self.app.getSource(
                    override_entry.source_name)
            if override_source.realm == fac.source.realm:
                cur_entry.errors.append(
                        "Page '%s' maps to URL '%s' but is overriden "
                        "by page '%s'." %
                        (fac.ref_spec, uri, override_entry.path))
                logger.error(cur_entry.errors[-1])
            cur_entry.flags |= BakeRecordEntry.FLAG_OVERRIDEN
            return

        route_index = self._app.routes.index(route)
        job = {
                'type': JOB_BAKE,
                'job': {
                        'factory_info': save_factory(page_fac),
                        'generator_name': self._generator.name,
                        'generator_record_key': extra_key,
                        'route_index': route_index,
                        'route_metadata': route_metadata,
                        'dirty_source_names': self._record.dirty_source_names,
                        'needs_config': True
                        }
                }
        self._job_queue.append(job)

    def runJobQueue(self):
        def _handler(res):
            entry = self._record.getCurrentEntry(
                    res['path'], res['generator_record_key'])
            entry.config = res['config']
            entry.subs = res['sub_entries']
            if res['errors']:
                entry.errors += res['errors']
            if entry.has_any_error:
                self._record.current.success = False

        self._is_running = True
        try:
            ar = self._pool.queueJobs(self._job_queue, handler=_handler)
            ar.wait()
        finally:
            self._is_running = False


class PageGenerator(object):
    def __init__(self, app, name, config):
        self.app = app
        self.name = name
        self.config = config or {}

        self.source_name = config.get('source')
        if self.source_name is None:
            raise ConfigurationError(
                    "Generator '%s' requires a source name" % name)

        page_ref = config.get('page')
        if page_ref is None:
            raise ConfigurationError(
                    "Generator '%s' requires a listing page ref." % name)
        self.page_ref = PageRef(app, page_ref)

    @cached_property
    def source(self):
        for src in self.app.sources:
            if src.name == self.source_name:
                return src
        raise Exception("Can't find source '%s' for generator '%s'." % (
            self.source_name, self.name))

    def getPageFactory(self, route_metadata):
        # This will raise `PageNotFoundError` naturally if not found.
        return self.page_ref.getFactory()

    def bake(self, ctx):
        raise NotImplementedError()

    def onRouteFunctionUsed(self, route, route_metadata):
        pass