Mercurial > piecrust2
comparison piecrust/rendering.py @ 411:e7b865f8f335
bake: Enable multiprocess baking.
Baking is now done by running a worker per CPU, and sending jobs to them.
This changes several things across the codebase:
* Ability to not cache things related to pages other than the 'main' page
(i.e. the page at the bottom of the execution stack).
* Decouple the baking process from the bake records, so only the main process
keeps track (and modifies) the bake record.
* Remove the need for 'batch page getters' and loading a page directly from
the page factories.
There are various smaller changes too included here, including support for
scope performance timers that are saved with the bake record and can be
printed out to the console. Yes I got carried away.
For testing, the in-memory 'mock' file-system doesn't work anymore, since
we're spawning processes, so this is replaced by a 'tmpfs' file-system which
is saved in temporary files on disk and deleted after tests have run.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Fri, 12 Jun 2015 17:09:19 -0700 |
parents | 4b1019bb2533 |
children | 0e9a94b7fdfa |
comparison
equal
deleted
inserted
replaced
410:d1a472464e57 | 411:e7b865f8f335 |
---|---|
1 import re | 1 import re |
2 import os.path | 2 import os.path |
3 import logging | 3 import logging |
4 from werkzeug.utils import cached_property | 4 from werkzeug.utils import cached_property |
5 from piecrust.data.builder import (DataBuildingContext, build_page_data, | 5 from piecrust.data.builder import ( |
6 build_layout_data) | 6 DataBuildingContext, build_page_data, build_layout_data) |
7 from piecrust.data.filters import ( | 7 from piecrust.data.filters import ( |
8 PaginationFilter, HasFilterClause, IsFilterClause, AndBooleanClause, | 8 PaginationFilter, HasFilterClause, IsFilterClause, AndBooleanClause, |
9 page_value_accessor) | 9 page_value_accessor) |
10 from piecrust.sources.base import PageSource | 10 from piecrust.sources.base import PageSource |
11 from piecrust.templating.base import TemplateNotFoundError, TemplatingError | 11 from piecrust.templating.base import TemplateNotFoundError, TemplatingError |
90 return self.page.source_metadata | 90 return self.page.source_metadata |
91 | 91 |
92 @cached_property | 92 @cached_property |
93 def uri(self): | 93 def uri(self): |
94 return self.page.getUri(self.page_num) | 94 return self.page.getUri(self.page_num) |
95 | |
96 @property | |
97 def pagination_has_more(self): | |
98 if self.used_pagination is None: | |
99 return False | |
100 return self.used_pagination.has_more | |
95 | 101 |
96 @property | 102 @property |
97 def current_pass_info(self): | 103 def current_pass_info(self): |
98 return self.render_passes.get(self._current_pass) | 104 return self.render_passes.get(self._current_pass) |
99 | 105 |
155 page_data.update(ctx.custom_data) | 161 page_data.update(ctx.custom_data) |
156 | 162 |
157 # Render content segments. | 163 # Render content segments. |
158 ctx.setCurrentPass(PASS_FORMATTING) | 164 ctx.setCurrentPass(PASS_FORMATTING) |
159 repo = ctx.app.env.rendered_segments_repository | 165 repo = ctx.app.env.rendered_segments_repository |
166 save_to_fs = True | |
167 if ctx.app.env.fs_cache_only_for_main_page and not eis.is_main_page: | |
168 save_to_fs = False | |
160 if repo and not ctx.force_render: | 169 if repo and not ctx.force_render: |
161 cache_key = ctx.uri | |
162 page_time = page.path_mtime | |
163 contents = repo.get( | 170 contents = repo.get( |
164 cache_key, | 171 ctx.uri, |
165 lambda: _do_render_page_segments(page, page_data), | 172 lambda: _do_render_page_segments(page, page_data), |
166 fs_cache_time=page_time) | 173 fs_cache_time=page.path_mtime, |
174 save_to_fs=save_to_fs) | |
167 else: | 175 else: |
168 contents = _do_render_page_segments(page, page_data) | 176 contents = _do_render_page_segments(page, page_data) |
177 if repo: | |
178 repo.put(ctx.uri, contents, save_to_fs) | |
169 | 179 |
170 # Render layout. | 180 # Render layout. |
171 ctx.setCurrentPass(PASS_RENDERING) | 181 ctx.setCurrentPass(PASS_RENDERING) |
172 layout_name = page.config.get('layout') | 182 layout_name = page.config.get('layout') |
173 if layout_name is None: | 183 if layout_name is None: |
224 for seg_name, seg in page.raw_content.items(): | 234 for seg_name, seg in page.raw_content.items(): |
225 seg_text = '' | 235 seg_text = '' |
226 for seg_part in seg.parts: | 236 for seg_part in seg.parts: |
227 part_format = seg_part.fmt or format_name | 237 part_format = seg_part.fmt or format_name |
228 try: | 238 try: |
229 part_text = engine.renderString( | 239 with app.env.timerScope(engine.__class__.__name__): |
230 seg_part.content, page_data, | 240 part_text = engine.renderString( |
231 filename=page.path) | 241 seg_part.content, page_data, |
242 filename=page.path) | |
232 except TemplatingError as err: | 243 except TemplatingError as err: |
233 err.lineno += seg_part.line | 244 err.lineno += seg_part.line |
234 raise err | 245 raise err |
235 | 246 |
236 part_text = format_text(app, part_format, part_text) | 247 part_text = format_text(app, part_format, part_text) |
289 | 300 |
290 format_count = 0 | 301 format_count = 0 |
291 format_name = format_name or app.config.get('site/default_format') | 302 format_name = format_name or app.config.get('site/default_format') |
292 for fmt in app.plugin_loader.getFormatters(): | 303 for fmt in app.plugin_loader.getFormatters(): |
293 if fmt.FORMAT_NAMES is None or format_name in fmt.FORMAT_NAMES: | 304 if fmt.FORMAT_NAMES is None or format_name in fmt.FORMAT_NAMES: |
294 txt = fmt.render(format_name, txt) | 305 with app.env.timerScope(fmt.__class__.__name__): |
306 txt = fmt.render(format_name, txt) | |
295 format_count += 1 | 307 format_count += 1 |
296 if fmt.OUTPUT_FORMAT is not None: | 308 if fmt.OUTPUT_FORMAT is not None: |
297 format_name = fmt.OUTPUT_FORMAT | 309 format_name = fmt.OUTPUT_FORMAT |
298 if exact_format and format_count == 0: | 310 if exact_format and format_count == 0: |
299 raise Exception("No such format: %s" % format_name) | 311 raise Exception("No such format: %s" % format_name) |