Mercurial > piecrust2
diff piecrust/page.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 | e01473c3ea7e |
children | f070a4fc033c |
line wrap: on
line diff
--- a/piecrust/page.py Sat Apr 29 21:42:22 2017 -0700 +++ b/piecrust/page.py Wed May 17 00:11:48 2017 -0700 @@ -9,8 +9,8 @@ import collections from werkzeug.utils import cached_property from piecrust.configuration import ( - Configuration, ConfigurationError, - parse_config_header) + Configuration, ConfigurationError, + parse_config_header) logger = logging.getLogger(__name__) @@ -36,32 +36,61 @@ FLAG_RAW_CACHE_VALID = 2**0 +class PageNotFoundError(Exception): + pass + + +class QualifiedPage(object): + def __init__(self, page, route, route_params, *, page_num=1): + self.page = page + self.page_num = page_num + self.route = route + self.route_params = route_params + + @property + def app(self): + return self.page.app + + @property + def source(self): + return self.page.source + + @cached_property + def uri(self): + return self.route.getUri(self.route_params, self.page_num) + + def getSubPage(self, page_num): + return QualifiedPage(self.page, self.route, self.route_params, + page_num=self.page_num + 1) + + class Page(object): - def __init__(self, source, source_metadata, rel_path): - self.source = source - self.source_metadata = source_metadata - self.rel_path = rel_path + def __init__(self, content_item): + self.content_item = content_item self._config = None self._segments = None self._flags = FLAG_NONE self._datetime = None @property - def app(self): - return self.source.app + def source(self): + return self.content_item.source @property - def ref_spec(self): - return '%s:%s' % (self.source.name, self.rel_path) + def source_metadata(self): + return self.content_item.metadata + + @property + def content_spec(self): + return self.content_item.spec + + @property + def app(self): + return self.content_item.source.app @cached_property - def path(self): - path, _ = self.source.resolveRef(self.rel_path) - return path - - @cached_property - def path_mtime(self): - return os.path.getmtime(self.path) + def content_mtime(self): + return self.content_item.getmtime() @property def flags(self): @@ -91,20 +120,20 @@ page_time = _parse_config_time(self.config.get('time')) if page_time is not None: self._datetime = datetime.datetime( - page_date.year, - page_date.month, - page_date.day) + page_time + page_date.year, + page_date.month, + page_date.day) + page_time else: self._datetime = datetime.datetime( - page_date.year, page_date.month, page_date.day) + page_date.year, page_date.month, page_date.day) elif 'date' in self.config: # Get the date from the page config, and maybe the # time too. page_date = _parse_config_date(self.config.get('date')) self._datetime = datetime.datetime( - page_date.year, - page_date.month, - page_date.day) + page_date.year, + page_date.month, + page_date.day) page_time = _parse_config_time(self.config.get('time')) if page_time is not None: self._datetime += page_time @@ -114,8 +143,8 @@ except Exception as ex: logger.exception(ex) raise Exception( - "Error computing time for page: %s" % - self.path) from ex + "Error computing time for page: %s" % + self.path) from ex return self._datetime @datetime.setter @@ -129,8 +158,9 @@ if self._config is not None: return - config, content, was_cache_valid = load_page(self.app, self.path, - self.path_mtime) + config, content, was_cache_valid = load_page( + self.app, self.path, self.path_mtime) + if 'config' in self.source_metadata: config.merge(self.source_metadata['config']) @@ -141,6 +171,7 @@ self.source.finalizeConfig(self) + def _parse_config_date(page_date): if page_date is None: return None @@ -152,9 +183,9 @@ logger.exception(ex) raise ConfigurationError("Invalid date: %s" % page_date) from ex return datetime.date( - year=parsed_d.year, - month=parsed_d.month, - day=parsed_d.day) + year=parsed_d.year, + month=parsed_d.month, + day=parsed_d.day) raise ConfigurationError("Invalid date: %s" % page_date) @@ -173,9 +204,9 @@ logger.exception(ex) raise ConfigurationError("Invalid time: %s" % page_time) from ex return datetime.timedelta( - hours=parsed_t.hour, - minutes=parsed_t.minute, - seconds=parsed_t.second) + hours=parsed_t.hour, + minutes=parsed_t.minute, + seconds=parsed_t.second) if isinstance(page_time, int): # Total seconds... convert to a time struct. @@ -187,8 +218,8 @@ class PageLoadingError(Exception): def __init__(self, path, inner=None): super(PageLoadingError, self).__init__( - "Error loading page: %s" % path, - inner) + "Error loading page: %s" % path, + inner) class ContentSegment(object): @@ -242,8 +273,8 @@ return _do_load_page(app, path, path_mtime) except Exception as e: logger.exception( - "Error loading page: %s" % - os.path.relpath(path, app.root_dir)) + "Error loading page: %s" % + os.path.relpath(path, app.root_dir)) _, __, traceback = sys.exc_info() raise PageLoadingError(path, e).with_traceback(traceback) @@ -255,11 +286,11 @@ page_time = path_mtime or os.path.getmtime(path) if cache.isValid(cache_path, page_time): cache_data = json.loads( - cache.read(cache_path), - object_pairs_hook=collections.OrderedDict) + cache.read(cache_path), + object_pairs_hook=collections.OrderedDict) config = PageConfiguration( - values=cache_data['config'], - validate=False) + values=cache_data['config'], + validate=False) content = json_load_segments(cache_data['content']) return config, content, True @@ -280,19 +311,19 @@ # Save to the cache. cache_data = { - 'config': config.getAll(), - 'content': json_save_segments(content)} + 'config': config.getAll(), + 'content': json_save_segments(content)} cache.write(cache_path, json.dumps(cache_data)) return config, content, False segment_pattern = re.compile( - r"""^\-\-\-\s*(?P<name>\w+)(\:(?P<fmt>\w+))?\s*\-\-\-\s*$""", - re.M) + r"""^\-\-\-\s*(?P<name>\w+)(\:(?P<fmt>\w+))?\s*\-\-\-\s*$""", + re.M) part_pattern = re.compile( - r"""^<\-\-\s*(?P<fmt>\w+)\s*\-\->\s*$""", - re.M) + r"""^<\-\-\s*(?P<fmt>\w+)\s*\-\->\s*$""", + re.M) def _count_lines(s): @@ -323,7 +354,7 @@ if not do_parse: seg = ContentSegment() seg.parts = [ - ContentSegmentPart(raw[offset:], None, offset, current_line)] + ContentSegmentPart(raw[offset:], None, offset, current_line)] return {'content': seg} # Start parsing segments and parts. @@ -337,7 +368,7 @@ # There's some default content segment at the beginning. seg = ContentSegment() seg.parts, current_line = parse_segment_parts( - raw, offset, first_offset, current_line) + raw, offset, first_offset, current_line) contents['content'] = seg for i in range(1, num_matches): @@ -345,16 +376,16 @@ m2 = matches[i] seg = ContentSegment() seg.parts, current_line = parse_segment_parts( - raw, m1.end() + 1, m2.start(), current_line, - m1.group('fmt')) + raw, m1.end() + 1, m2.start(), current_line, + m1.group('fmt')) contents[m1.group('name')] = seg # Handle text past the last match. lastm = matches[-1] seg = ContentSegment() seg.parts, current_line = parse_segment_parts( - raw, lastm.end() + 1, len(raw), current_line, - lastm.group('fmt')) + raw, lastm.end() + 1, len(raw), current_line, + lastm.group('fmt')) contents[lastm.group('name')] = seg return contents @@ -362,7 +393,7 @@ # No segments, just content. seg = ContentSegment() seg.parts, current_line = parse_segment_parts( - raw, offset, len(raw), current_line) + raw, offset, len(raw), current_line) return {'content': seg} @@ -375,8 +406,8 @@ # First part, before the first format change. part_text = raw[start:matches[0].start()] parts.append( - ContentSegmentPart(part_text, first_part_fmt, start, - line_offset)) + ContentSegmentPart(part_text, first_part_fmt, start, + line_offset)) line_offset += _count_lines(part_text) for i in range(1, num_matches): @@ -384,16 +415,16 @@ m2 = matches[i] part_text = raw[m1.end() + 1:m2.start()] parts.append( - ContentSegmentPart( - part_text, m1.group('fmt'), m1.end() + 1, - line_offset)) + ContentSegmentPart( + part_text, m1.group('fmt'), m1.end() + 1, + line_offset)) line_offset += _count_lines(part_text) lastm = matches[-1] part_text = raw[lastm.end() + 1:end] parts.append(ContentSegmentPart( - part_text, lastm.group('fmt'), lastm.end() + 1, - line_offset)) + part_text, lastm.group('fmt'), lastm.end() + 1, + line_offset)) return parts, line_offset else: