Mercurial > piecrust2
diff piecrust/baking/records.py @ 338:938be93215cb
bake: Improve render context and bake record, fix incremental bake bugs.
* Used sources and taxonomies are now stored on a per-render-pass basis.
This fixes bugs where sources/taxonomies were used for one pass, but that
pass is skipped on a later bake because its result is cached.
* Bake records are now created for all pages even when they're not baked.
Record collapsing is gone except for taxonomy index pages.
* Bake records now also have sub-entries in order to store information about
each sub-page, since some sub-pages could use sources/taxonomies differently
than others, or be missing from the output. This lets PieCrust handle
clean/dirty states on a sub-page level.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Mon, 06 Apr 2015 19:59:54 -0700 |
parents | b034f6f15e22 |
children | 2cd2b5d07129 |
line wrap: on
line diff
--- a/piecrust/baking/records.py Sat Apr 04 07:55:49 2015 -0700 +++ b/piecrust/baking/records.py Mon Apr 06 19:59:54 2015 -0700 @@ -1,3 +1,4 @@ +import copy import os.path import logging from piecrust.records import Record, TransitionalRecord @@ -19,7 +20,7 @@ class BakeRecord(Record): - RECORD_VERSION = 11 + RECORD_VERSION = 12 def __init__(self): super(BakeRecord, self).__init__() @@ -28,10 +29,43 @@ self.success = True -FLAG_NONE = 0 -FLAG_SOURCE_MODIFIED = 2**0 -FLAG_OVERRIDEN = 2**1 -FLAG_FORCED_BY_SOURCE = 2**2 +class BakeRecordPassInfo(object): + def __init__(self): + self.used_source_names = set() + self.used_taxonomy_terms = set() + + +class BakeRecordSubPageEntry(object): + FLAG_NONE = 0 + FLAG_BAKED = 2**0 + FLAG_FORCED_BY_SOURCE = 2**1 + FLAG_FORCED_BY_NO_PREVIOUS = 2**2 + FLAG_FORCED_BY_PREVIOUS_ERRORS = 2**3 + FLAG_FORMATTING_INVALIDATED = 2**4 + + def __init__(self, out_uri, out_path): + self.out_uri = out_uri + self.out_path = out_path + self.flags = self.FLAG_NONE + self.errors = [] + self.render_passes = {} + + @property + def was_clean(self): + return (self.flags & self.FLAG_BAKED) == 0 and len(self.errors) == 0 + + @property + def was_baked(self): + return (self.flags & self.FLAG_BAKED) != 0 + + @property + def was_baked_successfully(self): + return self.was_baked and len(self.errors) == 0 + + def collapseRenderPasses(self, other): + for p, pinfo in self.render_passes.items(): + if p not in other.render_passes: + other.render_passes[p] = copy.deepcopy(pinfo) class BakeRecordPageEntry(object): @@ -40,49 +74,73 @@ The `taxonomy_info` attribute should be a tuple of the form: (taxonomy name, term, source name) """ + FLAG_NONE = 0 + FLAG_NEW = 2**0 + FLAG_SOURCE_MODIFIED = 2**1 + FLAG_OVERRIDEN = 2**2 + def __init__(self, source_name, rel_path, path, taxonomy_info=None): self.source_name = source_name self.rel_path = rel_path self.path = path self.taxonomy_info = taxonomy_info - - self.flags = FLAG_NONE + self.flags = self.FLAG_NONE self.config = None - self.errors = [] - self.out_uris = [] - self.out_paths = [] - self.clean_uris = [] - self.clean_out_paths = [] - self.used_source_names = set() - self.used_taxonomy_terms = set() - self.used_pagination_item_count = 0 + self.subs = [] + self.assets = [] @property def path_mtime(self): return os.path.getmtime(self.path) @property - def was_baked(self): - return len(self.out_paths) > 0 or len(self.errors) > 0 - - @property - def was_baked_successfully(self): - return len(self.out_paths) > 0 and len(self.errors) == 0 + def was_overriden(self): + return (self.flags & self.FLAG_OVERRIDEN) != 0 @property def num_subs(self): - return len(self.out_paths) + return len(self.subs) + + @property + def was_any_sub_baked(self): + for o in self.subs: + if o.was_baked: + return True + return False + + def getSub(self, sub_index): + return self.subs[sub_index - 1] + + def getAllErrors(self): + for o in self.subs: + yield from o.errors + + def getAllUsedSourceNames(self): + res = set() + for o in self.subs: + for p, pinfo in o.render_passes.items(): + res |= pinfo.used_source_names + return res + + def getAllUsedTaxonomyTerms(self): + res = set() + for o in self.subs: + for p, pinfo in o.render_passes.items(): + res |= pinfo.used_taxonomy_terms + return res class TransitionalBakeRecord(TransitionalRecord): def __init__(self, previous_path=None): super(TransitionalBakeRecord, self).__init__(BakeRecord, previous_path) + self.dirty_source_names = set() def addEntry(self, entry): if (self.previous.bake_time and entry.path_mtime >= self.previous.bake_time): - entry.flags |= FLAG_SOURCE_MODIFIED + entry.flags |= BakeRecordPageEntry.FLAG_SOURCE_MODIFIED + self.dirty_source_names.add(entry.source_name) super(TransitionalBakeRecord, self).addEntry(entry) def getTransitionKey(self, entry): @@ -91,18 +149,13 @@ def getOverrideEntry(self, factory, uri): for pair in self.transitions.values(): - prev = pair[0] cur = pair[1] if (cur and (cur.source_name != factory.source.name or - cur.rel_path != factory.rel_path) and - len(cur.out_uris) > 0 and cur.out_uris[0] == uri): - return cur - if (prev and - (prev.source_name != factory.source.name or - prev.rel_path != factory.rel_path) and - len(prev.out_uris) > 0 and prev.out_uris[0] == uri): - return prev + cur.rel_path != factory.rel_path)): + for o in cur.subs: + if o.out_uri == uri: + return cur return None def getPreviousEntry(self, source_name, rel_path, taxonomy_info=None): @@ -112,32 +165,25 @@ return pair[0] return None - def getCurrentEntries(self, source_name): - return [e for e in self.current.entries - if e.source_name == source_name] - - def collapseRecords(self): - for prev, cur in self.transitions.values(): - if prev and cur and not cur.was_baked: - # This page wasn't baked, so the information from last - # time is still valid (we didn't get any information - # since we didn't bake). - cur.flags = prev.flags - if prev.config: - cur.config = prev.config.copy() - cur.out_uris = list(prev.out_uris) - cur.out_paths = list(prev.out_paths) - cur.errors = list(prev.errors) - cur.used_source_names = set(prev.used_source_names) - cur.used_taxonomy_terms = set(prev.used_taxonomy_terms) + def collapseEntry(self, prev_entry): + cur_entry = copy.deepcopy(prev_entry) + cur_entry.flags = BakeRecordPageEntry.FLAG_NONE + for o in cur_entry.subs: + o.flags = BakeRecordSubPageEntry.FLAG_NONE + self.addEntry(cur_entry) def getDeletions(self): for prev, cur in self.transitions.values(): if prev and not cur: - for p in prev.out_paths: - yield (p, 'previous source file was removed') - elif prev and cur and cur.was_baked_successfully: - diff = set(prev.out_paths) - set(cur.out_paths) + for sub in prev.subs: + yield (sub.out_path, 'previous source file was removed') + elif prev and cur: + prev_out_paths = [o.out_path for o in prev.subs] + cur_out_paths = [o.out_path for o in cur.subs] + diff = set(prev_out_paths) - set(cur_out_paths) for p in diff: yield (p, 'source file changed outputs') + def _onNewEntryAdded(self, entry): + entry.flags |= BakeRecordPageEntry.FLAG_NEW +