comparison piecrust/rendering.py @ 719:a066f4ac9094

rendering: Use `fastpickle` serialization before JSON. This is because JSON loses information about stuff like tuples.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 28 May 2016 15:43:24 -0700
parents a14371c5cda7
children 606f6d57b5df
comparison
equal deleted inserted replaced
718:d4408fbbbc7d 719:a066f4ac9094
5 from werkzeug.utils import cached_property 5 from werkzeug.utils import cached_property
6 from piecrust.data.builder import ( 6 from piecrust.data.builder import (
7 DataBuildingContext, build_page_data, build_layout_data) 7 DataBuildingContext, build_page_data, build_layout_data)
8 from piecrust.data.filters import ( 8 from piecrust.data.filters import (
9 PaginationFilter, SettingFilterClause, page_value_accessor) 9 PaginationFilter, SettingFilterClause, page_value_accessor)
10 from piecrust.fastpickle import _pickle_object, _unpickle_object
10 from piecrust.sources.base import PageSource 11 from piecrust.sources.base import PageSource
11 from piecrust.templating.base import TemplateNotFoundError, TemplatingError 12 from piecrust.templating.base import TemplateNotFoundError, TemplatingError
12 13
13 14
14 logger = logging.getLogger(__name__) 15 logger = logging.getLogger(__name__)
89 90
90 def getCustomInfo(self, key, default=None, create_if_missing=False): 91 def getCustomInfo(self, key, default=None, create_if_missing=False):
91 if create_if_missing: 92 if create_if_missing:
92 return self._custom_info.setdefault(key, default) 93 return self._custom_info.setdefault(key, default)
93 return self._custom_info.get(key, default) 94 return self._custom_info.get(key, default)
94
95
96 def _toJson(self):
97 data = {
98 'used_source_names': list(self.used_source_names),
99 'used_pagination': self.used_pagination,
100 'pagination_has_more': self.pagination_has_more,
101 'used_assets': self.used_assets,
102 'custom_info': self._custom_info}
103 return data
104
105 @staticmethod
106 def _fromJson(data):
107 assert data is not None
108 rpi = RenderPassInfo()
109 rpi.used_source_names = set(data['used_source_names'])
110 rpi.used_pagination = data['used_pagination']
111 rpi.pagination_has_more = data['pagination_has_more']
112 rpi.used_assets = data['used_assets']
113 rpi._custom_info = data['custom_info']
114 return rpi
115 95
116 96
117 class PageRenderingContext(object): 97 class PageRenderingContext(object):
118 def __init__(self, qualified_page, page_num=1, 98 def __init__(self, qualified_page, page_num=1,
119 force_render=False, is_from_request=False): 99 force_render=False, is_from_request=False):
216 'pass_info': None} 196 'pass_info': None}
217 197
218 rp = RenderedPage(page, ctx.uri, ctx.page_num) 198 rp = RenderedPage(page, ctx.uri, ctx.page_num)
219 rp.data = page_data 199 rp.data = page_data
220 rp.content = layout_result['content'] 200 rp.content = layout_result['content']
221 rp.render_info[PASS_FORMATTING] = RenderPassInfo._fromJson( 201 rp.render_info[PASS_FORMATTING] = _unpickle_object(
222 render_result['pass_info']) 202 render_result['pass_info'])
223 if layout_result['pass_info'] is not None: 203 if layout_result['pass_info'] is not None:
224 rp.render_info[PASS_RENDERING] = RenderPassInfo._fromJson( 204 rp.render_info[PASS_RENDERING] = _unpickle_object(
225 layout_result['pass_info']) 205 layout_result['pass_info'])
226 return rp 206 return rp
227 except Exception as ex: 207 except Exception as ex:
228 if ctx.app.debug: 208 if ctx.app.debug:
229 raise 209 raise
230 logger.exception(ex) 210 logger.exception(ex)
258 ctx.setCurrentPass(PASS_NONE) 238 ctx.setCurrentPass(PASS_NONE)
259 eis.popPage() 239 eis.popPage()
260 240
261 rs = RenderedSegments( 241 rs = RenderedSegments(
262 render_result['segments'], 242 render_result['segments'],
263 RenderPassInfo._fromJson(render_result['pass_info'])) 243 _unpickle_object(render_result['pass_info']))
264 return rs 244 return rs
265 245
266 246
267 def _build_render_data(ctx): 247 def _build_render_data(ctx):
268 with ctx.app.env.timerScope("PageDataBuild"): 248 with ctx.app.env.timerScope("PageDataBuild"):
317 formatted_segments['content.abstract'] = content_abstract 297 formatted_segments['content.abstract'] = content_abstract
318 298
319 pass_info = cpi.render_ctx.render_passes[PASS_FORMATTING] 299 pass_info = cpi.render_ctx.render_passes[PASS_FORMATTING]
320 res = { 300 res = {
321 'segments': formatted_segments, 301 'segments': formatted_segments,
322 'pass_info': pass_info._toJson()} 302 'pass_info': _pickle_object(pass_info)}
323 return res 303 return res
324 304
325 305
326 def _do_render_layout(layout_name, page, layout_data): 306 def _do_render_layout(layout_name, page, layout_data):
327 cpi = page.app.env.exec_info_stack.current_page_info 307 cpi = page.app.env.exec_info_stack.current_page_info
349 msg = "Can't find template for page: %s\n" % page.path 329 msg = "Can't find template for page: %s\n" % page.path
350 msg += "Looked for: %s" % ', '.join(full_names) 330 msg += "Looked for: %s" % ', '.join(full_names)
351 raise Exception(msg) from ex 331 raise Exception(msg) from ex
352 332
353 pass_info = cpi.render_ctx.render_passes[PASS_RENDERING] 333 pass_info = cpi.render_ctx.render_passes[PASS_RENDERING]
354 res = {'content': output, 'pass_info': pass_info._toJson()} 334 res = {'content': output, 'pass_info': _pickle_object(pass_info)}
355 return res 335 return res
356 336
357 337
358 def get_template_engine(app, engine_name): 338 def get_template_engine(app, engine_name):
359 if engine_name == 'html': 339 if engine_name == 'html':