Mercurial > piecrust2
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): |