comparison piecrust/baking/single.py @ 334:b034f6f15e22

bake: Several bug taxonomy-related fixes for incorrect incremental bakes. * Improve how the baker processes taxonomy terms and figures out what needs to be re-baked or not. * Create bake entries for clean taxnomy terms so they're not deleted by an incremental bake. * Add more information to bake records. * Slugify taxonomy terms is now done by the route in one place. * Fix a bug where the cache key for invalidating rendered segments was not computed the same way as when the caching was done. * Fix how term combinations are passed around, rendered, printed, parsed, etc. (TODO: more word needed in the routing functions) * Expose to the template whether a taxonomy term is a combination or not. * Display term combinations better in the built-in theme. * Rename `route.taxonomy` to `route.taxonomy_name` to prevent confusion. * Add options to show bake records for previous bakes.
author Ludovic Chabant <ludovic@chabant.com>
date Fri, 03 Apr 2015 10:59:50 -0700
parents 422052d2e978
children 938be93215cb
comparison
equal deleted inserted replaced
333:91b07f9efdc1 334:b034f6f15e22
1 import os.path 1 import os.path
2 import shutil 2 import shutil
3 import codecs 3 import codecs
4 import logging 4 import logging
5 import urllib.parse 5 import urllib.parse
6 from piecrust.baking.records import FLAG_OVERRIDEN, FLAG_SOURCE_MODIFIED 6 from piecrust.baking.records import (
7 FLAG_OVERRIDEN, FLAG_SOURCE_MODIFIED, FLAG_FORCED_BY_SOURCE)
7 from piecrust.data.filters import (PaginationFilter, HasFilterClause, 8 from piecrust.data.filters import (PaginationFilter, HasFilterClause,
8 IsFilterClause, AndBooleanClause, 9 IsFilterClause, AndBooleanClause,
9 page_value_accessor) 10 page_value_accessor)
10 from piecrust.rendering import (PageRenderingContext, render_page, 11 from piecrust.rendering import (PageRenderingContext, render_page,
11 PASS_FORMATTING, PASS_RENDERING) 12 PASS_FORMATTING, PASS_RENDERING)
54 else: 55 else:
55 bake_path.append(decoded_uri) 56 bake_path.append(decoded_uri)
56 57
57 return os.path.normpath(os.path.join(*bake_path)) 58 return os.path.normpath(os.path.join(*bake_path))
58 59
59 def bake(self, factory, route, record_entry, 60 def bake(self, factory, route, record_entry):
60 taxonomy_name=None, taxonomy_term=None): 61 bake_taxonomy_info = None
61 taxonomy = None
62 route_metadata = dict(factory.metadata) 62 route_metadata = dict(factory.metadata)
63 if taxonomy_name and taxonomy_term: 63 if record_entry.taxonomy_info:
64 # TODO: add options for combining and slugifying terms 64 tax_name, tax_term, tax_source_name = record_entry.taxonomy_info
65 taxonomy = self.app.getTaxonomy(taxonomy_name) 65 taxonomy = self.app.getTaxonomy(tax_name)
66 if taxonomy.is_multiple: 66 slugified_term = route.slugifyTaxonomyTerm(tax_term)
67 if isinstance(taxonomy_term, tuple): 67 route_metadata[taxonomy.term_name] = slugified_term
68 slugified_term = '/'.join(taxonomy_term) 68 bake_taxonomy_info = (taxonomy, tax_term)
69 else:
70 slugified_term = taxonomy_term
71 else:
72 slugified_term = taxonomy_term
73 route_metadata.update({taxonomy.setting_name: slugified_term})
74 69
75 # Generate the URL using the route. 70 # Generate the URL using the route.
76 page = factory.buildPage() 71 page = factory.buildPage()
77 uri = route.getUri(route_metadata, provider=page) 72 uri = route.getUri(route_metadata, provider=page)
78 73
81 override_source = self.app.getSource(override.source_name) 76 override_source = self.app.getSource(override.source_name)
82 if override_source.realm == factory.source.realm: 77 if override_source.realm == factory.source.realm:
83 raise BakingError( 78 raise BakingError(
84 "Page '%s' maps to URL '%s' but is overriden by page" 79 "Page '%s' maps to URL '%s' but is overriden by page"
85 "'%s:%s'." % (factory.ref_spec, uri, 80 "'%s:%s'." % (factory.ref_spec, uri,
86 override.source_name, override.rel_path)) 81 override.source_name,
82 override.rel_path))
87 logger.debug("'%s' [%s] is overriden by '%s:%s'. Skipping" % 83 logger.debug("'%s' [%s] is overriden by '%s:%s'. Skipping" %
88 (factory.ref_spec, uri, override.source_name, 84 (factory.ref_spec, uri, override.source_name,
89 override.rel_path)) 85 override.rel_path))
90 record_entry.flags |= FLAG_OVERRIDEN 86 record_entry.flags |= FLAG_OVERRIDEN
91 return 87 return
92 88
93 cur_sub = 1 89 cur_sub = 1
94 has_more_subs = True 90 has_more_subs = True
95 force_this = self.force 91 force_this = self.force
96 invalidate_formatting = False 92 invalidate_formatting = False
97 record_entry.config = copy_public_page_config(page.config) 93 record_entry.config = copy_public_page_config(page.config)
98 prev_record_entry = self.record.getPreviousEntry( 94 prev_record_entry = self.record.getPreviousEntry(
99 factory.source.name, factory.rel_path, 95 factory.source.name, factory.rel_path,
100 taxonomy_name, taxonomy_term) 96 record_entry.taxonomy_info)
101 97
102 logger.debug("Baking '%s'..." % uri) 98 logger.debug("Baking '%s'..." % uri)
103 99
104 # If the current page is known to use pages from other sources, 100 # If the current page is known to use pages from other sources,
105 # see if any of those got baked, or are going to be baked for some 101 # see if any of those got baked, or are going to be baked for some
116 break 112 break
117 if len(invalidated_render_passes) > 0: 113 if len(invalidated_render_passes) > 0:
118 logger.debug("'%s' is known to use sources %s, at least one " 114 logger.debug("'%s' is known to use sources %s, at least one "
119 "of which got baked. Will force bake this page. " 115 "of which got baked. Will force bake this page. "
120 % (uri, used_src_names)) 116 % (uri, used_src_names))
117 record_entry.flags |= FLAG_FORCED_BY_SOURCE
121 force_this = True 118 force_this = True
119
122 if PASS_FORMATTING in invalidated_render_passes: 120 if PASS_FORMATTING in invalidated_render_passes:
123 logger.debug("Will invalidate cached formatting for '%s' " 121 logger.debug("Will invalidate cached formatting for '%s' "
124 "since sources were using during that pass." 122 "since sources were using during that pass."
125 % uri) 123 % uri)
126 invalidate_formatting = True 124 invalidate_formatting = True
132 130
133 # Check for up-to-date outputs. 131 # Check for up-to-date outputs.
134 do_bake = True 132 do_bake = True
135 if not force_this: 133 if not force_this:
136 try: 134 try:
137 in_path_time = record_entry.path_mtime 135 in_path_time = page.path_mtime
138 out_path_time = os.path.getmtime(out_path) 136 out_path_time = os.path.getmtime(out_path)
139 if out_path_time >= in_path_time: 137 if out_path_time >= in_path_time:
140 do_bake = False 138 do_bake = False
141 except OSError: 139 except OSError:
142 # File doesn't exist, we'll need to bake. 140 # File doesn't exist, we'll need to bake.
149 prev_record_entry.num_subs < cur_sub): 147 prev_record_entry.num_subs < cur_sub):
150 logger.debug("") 148 logger.debug("")
151 cur_sub += 1 149 cur_sub += 1
152 has_more_subs = True 150 has_more_subs = True
153 logger.debug(" %s is up to date, skipping to next " 151 logger.debug(" %s is up to date, skipping to next "
154 "sub-page." % out_path) 152 "sub-page." % out_path)
153 record_entry.clean_uris.append(sub_uri)
154 record_entry.clean_out_paths.append(out_path)
155 continue 155 continue
156 156
157 # We don't know how many subs to expect... just skip. 157 # We don't know how many subs to expect... just skip.
158 logger.debug(" %s is up to date, skipping bake." % out_path) 158 logger.debug(" %s is up to date, skipping bake." % out_path)
159 break 159 break
160 160
161 # All good, proceed. 161 # All good, proceed.
162 try: 162 try:
163 if invalidate_formatting: 163 if invalidate_formatting:
164 cache_key = '%s:%s' % (uri, cur_sub) 164 cache_key = sub_uri
165 self.app.env.rendered_segments_repository.invalidate( 165 self.app.env.rendered_segments_repository.invalidate(
166 cache_key) 166 cache_key)
167 167
168 logger.debug(" p%d -> %s" % (cur_sub, out_path)) 168 logger.debug(" p%d -> %s" % (cur_sub, out_path))
169 ctx, rp = self._bakeSingle(page, sub_uri, cur_sub, out_path, 169 ctx, rp = self._bakeSingle(page, sub_uri, cur_sub, out_path,
170 taxonomy, taxonomy_term) 170 bake_taxonomy_info)
171 except Exception as ex: 171 except Exception as ex:
172 if self.app.debug: 172 if self.app.debug:
173 logger.exception(ex) 173 logger.exception(ex)
174 page_rel_path = os.path.relpath(page.path, self.app.root_dir) 174 page_rel_path = os.path.relpath(page.path, self.app.root_dir)
175 raise BakingError("%s: error baking '%s'." % 175 raise BakingError("%s: error baking '%s'." %
176 (page_rel_path, uri)) from ex 176 (page_rel_path, uri)) from ex
177 177
178 # Copy page assets. 178 # Copy page assets.
179 if (cur_sub == 1 and self.copy_assets and 179 if (cur_sub == 1 and self.copy_assets and
180 ctx.used_assets is not None): 180 ctx.used_assets is not None):
181 if self.pretty_urls: 181 if self.pretty_urls:
199 record_entry.out_paths.append(out_path) 199 record_entry.out_paths.append(out_path)
200 record_entry.used_source_names |= ctx.used_source_names 200 record_entry.used_source_names |= ctx.used_source_names
201 record_entry.used_taxonomy_terms |= ctx.used_taxonomy_terms 201 record_entry.used_taxonomy_terms |= ctx.used_taxonomy_terms
202 202
203 has_more_subs = False 203 has_more_subs = False
204 if (ctx.used_pagination is not None and 204 if ctx.used_pagination is not None:
205 ctx.used_pagination.has_more): 205 if cur_sub == 1:
206 cur_sub += 1 206 record_entry.used_pagination_item_count = \
207 has_more_subs = True 207 ctx.used_pagination.total_item_count
208 if ctx.used_pagination.has_more:
209 cur_sub += 1
210 has_more_subs = True
208 211
209 def _bakeSingle(self, page, sub_uri, num, out_path, 212 def _bakeSingle(self, page, sub_uri, num, out_path,
210 taxonomy=None, taxonomy_term=None): 213 taxonomy_info=None):
211 ctx = PageRenderingContext(page, sub_uri) 214 ctx = PageRenderingContext(page, sub_uri)
212 ctx.page_num = num 215 ctx.page_num = num
213 if taxonomy and taxonomy_term: 216 if taxonomy_info:
214 ctx.setTaxonomyFilter(taxonomy, taxonomy_term) 217 ctx.setTaxonomyFilter(taxonomy_info[0], taxonomy_info[1])
215 218
216 rp = render_page(ctx) 219 rp = render_page(ctx)
217 220
218 out_dir = os.path.dirname(out_path) 221 out_dir = os.path.dirname(out_path)
219 if not os.path.isdir(out_dir): 222 if not os.path.isdir(out_dir):