Mercurial > piecrust2
diff piecrust/sources/base.py @ 852:4850f8c21b6e
core: Start of the big refactor for PieCrust 3.0.
* Everything is a `ContentSource`, including assets directories.
* Most content sources are subclasses of the base file-system source.
* A source is processed by a "pipeline", and there are 2 built-in pipelines,
one for assets and one for pages. The asset pipeline is vaguely functional,
but the page pipeline is completely broken right now.
* Rewrite the baking process as just running appropriate pipelines on each
content item. This should allow for better parallelization.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 17 May 2017 00:11:48 -0700 |
parents | 7f3043f9f26f |
children | f070a4fc033c |
line wrap: on
line diff
--- a/piecrust/sources/base.py Sat Apr 29 21:42:22 2017 -0700 +++ b/piecrust/sources/base.py Wed May 17 00:11:48 2017 -0700 @@ -1,88 +1,78 @@ -import copy import logging -from werkzeug.utils import cached_property -from piecrust.page import Page -from piecrust.data.assetor import Assetor +import collections +# Source realms, to differentiate sources in the site itself ('User') +# and sources in the site's theme ('Theme'). REALM_USER = 0 REALM_THEME = 1 REALM_NAMES = { - REALM_USER: 'User', - REALM_THEME: 'Theme'} + REALM_USER: 'User', + REALM_THEME: 'Theme'} -MODE_PARSING = 0 -MODE_CREATING = 1 +# Types of relationships a content source can be asked for. +REL_ASSETS = 1 logger = logging.getLogger(__name__) -def build_pages(app, factories): - for f in factories: - yield f.buildPage() - - class SourceNotFoundError(Exception): pass -class InvalidFileSystemEndpointError(Exception): - def __init__(self, source_name, fs_endpoint): - super(InvalidFileSystemEndpointError, self).__init__( - "Invalid file-system endpoint for source '%s': %s" % - (source_name, fs_endpoint)) +class InsufficientRouteParameters(Exception): + pass + + +class AbortedSourceUseError(Exception): + pass -class PageFactory(object): - """ A class responsible for creating a page. +class GeneratedContentException(Exception): + pass + + +CONTENT_TYPE_PAGE = 0 +CONTENT_TYPE_ASSET = 1 + + +class ContentItem: + """ Describes a piece of content. """ - def __init__(self, source, rel_path, metadata): - self.source = source - self.rel_path = rel_path + def __init__(self, spec, metadata): + self.spec = spec self.metadata = metadata - @cached_property - def ref_spec(self): - return '%s:%s' % (self.source.name, self.rel_path) - - @cached_property - def path(self): - path, _ = self.source.resolveRef(self.rel_path) - return path - - def buildPage(self): - repo = self.source.app.env.page_repository - cache_key = '%s:%s' % (self.source.name, self.rel_path) - return repo.get(cache_key, self._doBuildPage) - - def _doBuildPage(self): - logger.debug("Building page: %s" % self.path) - page = Page(self.source, copy.deepcopy(self.metadata), self.rel_path) - return page + @property + def is_group(self): + return False -class PageSource(object): - """ A source for pages, e.g. a directory with one file per page. +class ContentGroup: + """ Describes a group of `ContentItem`s. + """ + def __init__(self, spec, metadata): + self.spec = spec + self.metadata = metadata + + @property + def is_group(self): + return True + + +class ContentSource: + """ A source for content. """ def __init__(self, app, name, config): self.app = app self.name = name self.config = config or {} - self.config.setdefault('realm', REALM_USER) - self._factories = None - self._provider_type = None - - def __getattr__(self, name): - try: - return self.config[name] - except KeyError: - raise AttributeError() @property def is_theme_source(self): - return self.realm == REALM_THEME + return self.config['realm'] == REALM_THEME @property def root_dir(self): @@ -90,48 +80,47 @@ return self.app.theme_dir return self.app.root_dir - def getPages(self): - return build_pages(self.app, self.getPageFactories()) + def openItem(self, item, mode='r'): + raise NotImplementedError() + + def getItemMtime(self, item): + raise NotImplementedError() - def getPage(self, metadata): - factory = self.findPageFactory(metadata, MODE_PARSING) - if factory is None: - return None - return factory.buildPage() + def getAllContents(self): + stack = collections.deque() + stack.append(None) + while len(stack) > 0: + cur = stack.popleft() + try: + contents = self.getContents(cur) + except GeneratedContentException: + continue + if contents is not None: + for c in contents: + if c.is_group: + stack.append(c) + else: + yield c - def getPageFactories(self): - if self._factories is None: - self._factories = list(self.buildPageFactories()) - return self._factories + def getContents(self, group): + raise NotImplementedError("'%s' doesn't implement 'getContents'." % + self.__class__) + + def getRelatedContents(self, item, relationship): + raise NotImplementedError() + + def findContent(self, route_params): + raise NotImplementedError() def getSupportedRouteParameters(self): raise NotImplementedError() - def buildPageFactories(self): - raise NotImplementedError() - - def buildPageFactory(self, path): - raise NotImplementedError() - - def resolveRef(self, ref_path): - """ Returns the full path and source metadata given a source - (relative) path, like a ref-spec. - """ - raise NotImplementedError() - - def findPageFactory(self, metadata, mode): - raise NotImplementedError() - - def buildDataProvider(self, page, override): - if not self._provider_type: - from piecrust.data.provider import get_data_provider_class - self._provider_type = get_data_provider_class(self.app, - self.data_type) - return self._provider_type(self, page, override) - - def finalizeConfig(self, page): + def prepareRenderContext(self, ctx): pass - def buildAssetor(self, page, uri): - return Assetor(page, uri) + def onRouteFunctionUsed(self, route_params): + pass + def describe(self): + return None +