Mercurial > piecrust2
diff piecrust/sources/base.py @ 242:f130365568ff
internal: Code reorganization to put less stuff in `sources.base`.
Interfaces that sources can implement are in `sources.interfaces`. The default
page source is in `sources.default`. The `SimplePageSource` is gone since most
subclasses only wanted to do *some* stuff the same, but *lots* of stuff
slightly different. I may have to revisit the code to extract exactly the code
that's in common.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 18 Feb 2015 18:35:03 -0800 |
parents | 4dce0e61b48c |
children | 88bffd469b04 |
line wrap: on
line diff
--- a/piecrust/sources/base.py Mon Feb 16 08:25:08 2015 -0800 +++ b/piecrust/sources/base.py Wed Feb 18 18:35:03 2015 -0800 @@ -1,11 +1,6 @@ -import re -import os -import os.path import logging from werkzeug.utils import cached_property from piecrust.configuration import ConfigurationError -from piecrust.data.base import IPaginationSource, PaginationData -from piecrust.data.filters import PaginationFilter from piecrust.page import Page @@ -23,19 +18,12 @@ logger = logging.getLogger(__name__) -page_ref_pattern = re.compile(r'(?P<src>[\w]+)\:(?P<path>.*?)(;|$)') - - def build_pages(app, factories): with app.env.page_repository.startBatchGet(): for f in factories: yield f.buildPage() -class PageNotFoundError(Exception): - pass - - class InvalidFileSystemEndpointError(Exception): def __init__(self, source_name, fs_endpoint): super(InvalidFileSystemEndpointError, self).__init__( @@ -75,120 +63,6 @@ return page -class CachedPageFactory(object): - """ A `PageFactory` (in appearance) that already has a page built. - """ - def __init__(self, page): - self._page = page - - @property - def rel_path(self): - return self._page.rel_path - - @property - def metadata(self): - return self._page.source_metadata - - @property - def ref_spec(self): - return self._page.ref_spec - - @property - def path(self): - return self._page.path - - def buildPage(self): - return self._page - - -class PageRef(object): - """ A reference to a page, with support for looking a page in different - realms. - """ - def __init__(self, app, page_ref): - self.app = app - self._page_ref = page_ref - self._paths = None - self._first_valid_path_index = -2 - self._exts = list(app.config.get('site/auto_formats').keys()) - - @property - def exists(self): - try: - self._checkPaths() - return True - except PageNotFoundError: - return False - - @property - def source_name(self): - self._checkPaths() - return self._paths[self._first_valid_path_index][0] - - @property - def source(self): - return self.app.getSource(self.source_name) - - @property - def rel_path(self): - self._checkPaths() - return self._paths[self._first_valid_path_index][1] - - @property - def path(self): - self._checkPaths() - return self._paths[self._first_valid_path_index][2] - - @property - def possible_rel_paths(self): - self._load() - return [p[1] for p in self._paths] - - @property - def possible_paths(self): - self._load() - return [p[2] for p in self._paths] - - def _load(self): - if self._paths is not None: - return - - it = list(page_ref_pattern.finditer(self._page_ref)) - if len(it) == 0: - raise Exception("Invalid page ref: %s" % self._page_ref) - - self._paths = [] - for m in it: - source_name = m.group('src') - source = self.app.getSource(source_name) - if source is None: - raise Exception("No such source: %s" % source_name) - rel_path = m.group('path') - path = source.resolveRef(rel_path) - if '%ext%' in rel_path: - for e in self._exts: - self._paths.append((source_name, - rel_path.replace('%ext%', e), - path.replace('%ext%', e))) - else: - self._paths.append((source_name, rel_path, path)) - - def _checkPaths(self): - if self._first_valid_path_index >= 0: - return - if self._first_valid_path_index == -1: - raise PageNotFoundError( - "No valid paths were found for page reference: %s" % - self._page_ref) - - self._load() - self._first_valid_path_index = -1 - for i, path_info in enumerate(self._paths): - if os.path.isfile(path_info[2]): - self._first_valid_path_index = i - break - - class PageSource(object): """ A source for pages, e.g. a directory with one file per page. """ @@ -235,11 +109,11 @@ def buildDataProvider(self, page, user_data): if self._provider_type is None: cls = next((pt for pt in self.app.plugin_loader.getDataProviders() - if pt.PROVIDER_NAME == self.data_type), - None) + if pt.PROVIDER_NAME == self.data_type), + None) if cls is None: - raise ConfigurationError("Unknown data provider type: %s" % - self.data_type) + raise ConfigurationError( + "Unknown data provider type: %s" % self.data_type) self._provider_type = cls return self._provider_type(self, page, user_data) @@ -250,230 +124,3 @@ return None return tax_pages.get(tax_name) - -class IPreparingSource: - def setupPrepareParser(self, parser, app): - raise NotImplementedError() - - def buildMetadata(self, args): - raise NotImplementedError() - - -class IListableSource: - def listPath(self, rel_path): - raise NotImplementedError() - - def getDirpath(self, rel_path): - raise NotImplementedError() - - def getBasename(self, rel_path): - raise NotImplementedError() - - -class SimplePaginationSourceMixin(IPaginationSource): - def getItemsPerPage(self): - return self.config['items_per_page'] - - def getSourceIterator(self): - return SourceFactoryIterator(self) - - def getSorterIterator(self, it): - return DateSortIterator(it) - - def getTailIterator(self, it): - return PaginationDataBuilderIterator(it) - - def getPaginationFilter(self, page): - conf = (page.config.get('items_filters') or - page.app.config.get('site/items_filters')) - if conf == 'none' or conf == 'nil' or conf == '': - conf = None - if conf is not None: - f = PaginationFilter() - f.addClausesFromConfig(conf) - return f - return None - - def getSettingAccessor(self): - return lambda i, n: i.config.get(n) - - -class ArraySource(PageSource, SimplePaginationSourceMixin): - def __init__(self, app, inner_source, name='array', config=None): - super(ArraySource, self).__init__(app, name, config or {}) - self.inner_source = inner_source - - @property - def page_count(self): - return len(self.inner_source) - - def getPageFactories(self): - for p in self.inner_source: - yield CachedPageFactory(p) - - -class SimplePageSource(PageSource, IListableSource, IPreparingSource, - SimplePaginationSourceMixin): - def __init__(self, app, name, config): - super(SimplePageSource, self).__init__(app, name, config) - self.fs_endpoint = config.get('fs_endpoint', name) - self.fs_endpoint_path = os.path.join(self.root_dir, self.fs_endpoint) - self.supported_extensions = list(app.config.get('site/auto_formats').keys()) - self.default_auto_format = app.config.get('site/default_auto_format') - - def buildPageFactories(self): - logger.debug("Scanning for pages in: %s" % self.fs_endpoint_path) - if not os.path.isdir(self.fs_endpoint_path): - if self.ignore_missing_dir: - return - raise InvalidFileSystemEndpointError(self.name, self.fs_endpoint_path) - - for dirpath, dirnames, filenames in os.walk(self.fs_endpoint_path): - rel_dirpath = os.path.relpath(dirpath, self.fs_endpoint_path) - dirnames[:] = list(filter(self._filterPageDirname, dirnames)) - for f in filter(self._filterPageFilename, filenames): - fac_path = f - if rel_dirpath != '.': - fac_path = os.path.join(rel_dirpath, f) - slug = self._makeSlug(fac_path) - metadata = {'slug': slug} - fac_path = fac_path.replace('\\', '/') - self._populateMetadata(fac_path, metadata) - yield PageFactory(self, fac_path, metadata) - - def resolveRef(self, ref_path): - return os.path.normpath( - os.path.join(self.fs_endpoint_path, ref_path.lstrip("\\/"))) - - def findPagePath(self, metadata, mode): - uri_path = metadata.get('slug', '') - if not uri_path: - uri_path = '_index' - path = os.path.join(self.fs_endpoint_path, uri_path) - _, ext = os.path.splitext(path) - - if mode == MODE_CREATING: - if ext == '': - path = '%s.%s' % (path, self.default_auto_format) - rel_path = os.path.relpath(path, self.fs_endpoint_path) - rel_path = rel_path.replace('\\', '/') - self._populateMetadata(rel_path, metadata, mode) - return rel_path, metadata - - if ext == '': - paths_to_check = [ - '%s.%s' % (path, e) - for e in self.supported_extensions] - else: - paths_to_check = [path] - for path in paths_to_check: - if os.path.isfile(path): - rel_path = os.path.relpath(path, self.fs_endpoint_path) - rel_path = rel_path.replace('\\', '/') - self._populateMetadata(rel_path, metadata, mode) - return rel_path, metadata - - return None, None - - def listPath(self, rel_path): - rel_path = rel_path.lstrip('\\/') - path = os.path.join(self.fs_endpoint_path, rel_path) - names = sorted(os.listdir(path)) - items = [] - for name in names: - if os.path.isdir(os.path.join(path, name)): - if self._filterPageDirname(name): - rel_subdir = os.path.join(rel_path, name) - items.append((True, name, rel_subdir)) - else: - if self._filterPageFilename(name): - slug = self._makeSlug(os.path.join(rel_path, name)) - metadata = {'slug': slug} - - fac_path = name - if rel_path != '.': - fac_path = os.path.join(rel_path, name) - fac_path = fac_path.replace('\\', '/') - - self._populateMetadata(fac_path, metadata) - fac = PageFactory(self, fac_path, metadata) - - name, _ = os.path.splitext(name) - items.append((False, name, fac)) - return items - - def getDirpath(self, rel_path): - return os.path.dirname(rel_path) - - def getBasename(self, rel_path): - filename = os.path.basename(rel_path) - name, _ = os.path.splitext(filename) - return name - - def setupPrepareParser(self, parser, app): - parser.add_argument('uri', help='The URI for the new page.') - - def buildMetadata(self, args): - return {'slug': args.uri} - - def _makeSlug(self, rel_path): - slug, ext = os.path.splitext(rel_path) - slug = slug.replace('\\', '/') - if ext.lstrip('.') not in self.supported_extensions: - slug += ext - if slug.startswith('./'): - slug = slug[2:] - if slug == '_index': - slug = '' - return slug - - def _filterPageDirname(self, d): - return not d.endswith('-assets') - - def _filterPageFilename(self, f): - return (f[0] != '.' and # .DS_store and other crap - f[-1] != '~' and # Vim temp files and what-not - f not in ['Thumbs.db']) # Windows bullshit - - def _populateMetadata(self, rel_path, metadata, mode=None): - pass - - -class DefaultPageSource(SimplePageSource): - SOURCE_NAME = 'default' - - def __init__(self, app, name, config): - super(DefaultPageSource, self).__init__(app, name, config) - - -class SourceFactoryIterator(object): - def __init__(self, source): - self.source = source - self.it = None # This is to permit recursive traversal of the - # iterator chain. It acts as the end. - - def __iter__(self): - return self.source.getPages() - - -class DateSortIterator(object): - def __init__(self, it, reverse=True): - self.it = it - self.reverse = reverse - - def __iter__(self): - return iter(sorted(self.it, - key=lambda x: x.datetime, reverse=self.reverse)) - - -class PaginationDataBuilderIterator(object): - def __init__(self, it): - self.it = it - - def __iter__(self): - for page in self.it: - if page is None: - yield None - else: - yield PaginationData(page) -