comparison piecrust/rendering.py @ 853:f070a4fc033c

core: Continue PieCrust3 refactor, simplify pages. The asset pipeline is still the only function pipeline at this point. * No more `QualifiedPage`, and several other pieces of code deleted. * Data providers are simpler and more focused. For instance, the page iterator doesn't try to support other types of items. * Route parameters are proper known source metadata to remove the confusion between the two. * Make the baker and pipeline more correctly manage records and record histories. * Add support for record collapsing and deleting stale outputs in the asset pipeline.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 21 May 2017 00:06:59 -0700
parents 4850f8c21b6e
children 08e02c2a2a1a
comparison
equal deleted inserted replaced
852:4850f8c21b6e 853:f070a4fc033c
35 self.content = content 35 self.content = content
36 self.render_pass_info = render_pass_info 36 self.render_pass_info = render_pass_info
37 37
38 38
39 class RenderedPage(object): 39 class RenderedPage(object):
40 def __init__(self, qualified_page): 40 def __init__(self, page, sub_num):
41 self.qualified_page = qualified_page 41 self.page = page
42 self.sub_num = sub_num
42 self.data = None 43 self.data = None
43 self.content = None 44 self.content = None
44 self.render_info = [None, None] 45 self.render_info = [None, None]
45 46
46 @property 47 @property
47 def app(self): 48 def app(self):
48 return self.qualified_page.app 49 return self.page.app
49 50
50 def copyRenderInfo(self): 51 def copyRenderInfo(self):
51 return copy.deepcopy(self.render_info) 52 return copy.deepcopy(self.render_info)
52 53
53 54
75 return self._custom_info.setdefault(key, default) 76 return self._custom_info.setdefault(key, default)
76 return self._custom_info.get(key, default) 77 return self._custom_info.get(key, default)
77 78
78 79
79 class RenderingContext(object): 80 class RenderingContext(object):
80 def __init__(self, qualified_page, force_render=False): 81 def __init__(self, page, *, sub_num=1, force_render=False):
81 self.qualified_page = qualified_page 82 self.page = page
83 self.sub_num = sub_num
82 self.force_render = force_render 84 self.force_render = force_render
83 self.pagination_source = None 85 self.pagination_source = None
84 self.pagination_filter = None 86 self.pagination_filter = None
85 self.custom_data = {} 87 self.custom_data = {}
86 self.render_passes = [None, None] # Same length as RENDER_PASSES 88 self.render_passes = [None, None] # Same length as RENDER_PASSES
87 self._current_pass = PASS_NONE 89 self._current_pass = PASS_NONE
88 90
89 @property 91 @property
90 def app(self): 92 def app(self):
91 return self.qualified_page.app 93 return self.page.app
92 94
93 @property 95 @property
94 def current_pass_info(self): 96 def current_pass_info(self):
95 if self._current_pass != PASS_NONE: 97 if self._current_pass != PASS_NONE:
96 return self.render_passes[self._current_pass] 98 return self.render_passes[self._current_pass]
136 def is_main_ctx(self): 138 def is_main_ctx(self):
137 return len(self._ctx_stack) == 1 139 return len(self._ctx_stack) == 1
138 140
139 def hasPage(self, page): 141 def hasPage(self, page):
140 for ei in self._ctx_stack: 142 for ei in self._ctx_stack:
141 if ei.qualified_page.page == page: 143 if ei.page == page:
142 return True 144 return True
143 return False 145 return False
144 146
145 def pushCtx(self, render_ctx): 147 def pushCtx(self, render_ctx):
146 for ctx in self._ctx_stack: 148 for ctx in self._ctx_stack:
147 if ctx.qualified_page.page == render_ctx.qualified_page.page: 149 if ctx.page == render_ctx.page:
148 raise Exception("Loop detected during rendering!") 150 raise Exception("Loop detected during rendering!")
149 self._ctx_stack.append(render_ctx) 151 self._ctx_stack.append(render_ctx)
150 152
151 def popCtx(self): 153 def popCtx(self):
152 del self._ctx_stack[-1] 154 del self._ctx_stack[-1]
159 env = ctx.app.env 161 env = ctx.app.env
160 162
161 stack = env.render_ctx_stack 163 stack = env.render_ctx_stack
162 stack.pushCtx(ctx) 164 stack.pushCtx(ctx)
163 165
164 qpage = ctx.qualified_page 166 page = ctx.page
167 page_uri = page.getUri(ctx.sub_num)
165 168
166 try: 169 try:
167 # Build the data for both segment and layout rendering. 170 # Build the data for both segment and layout rendering.
168 with env.timerScope("BuildRenderData"): 171 with env.timerScope("BuildRenderData"):
169 page_data = _build_render_data(ctx) 172 page_data = _build_render_data(ctx)
175 if env.fs_cache_only_for_main_page and not stack.is_main_ctx: 178 if env.fs_cache_only_for_main_page and not stack.is_main_ctx:
176 save_to_fs = False 179 save_to_fs = False
177 with env.timerScope("PageRenderSegments"): 180 with env.timerScope("PageRenderSegments"):
178 if repo is not None and not ctx.force_render: 181 if repo is not None and not ctx.force_render:
179 render_result = repo.get( 182 render_result = repo.get(
180 qpage.uri, 183 page_uri,
181 lambda: _do_render_page_segments(ctx, page_data), 184 lambda: _do_render_page_segments(ctx, page_data),
182 fs_cache_time=qpage.page.content_mtime, 185 fs_cache_time=page.content_mtime,
183 save_to_fs=save_to_fs) 186 save_to_fs=save_to_fs)
184 else: 187 else:
185 render_result = _do_render_page_segments(ctx, page_data) 188 render_result = _do_render_page_segments(ctx, page_data)
186 if repo: 189 if repo:
187 repo.put(qpage.uri, render_result, save_to_fs) 190 repo.put(page_uri, render_result, save_to_fs)
188 191
189 # Render layout. 192 # Render layout.
190 ctx.setCurrentPass(PASS_RENDERING) 193 ctx.setCurrentPass(PASS_RENDERING)
191 layout_name = qpage.page.config.get('layout') 194 layout_name = page.config.get('layout')
192 if layout_name is None: 195 if layout_name is None:
193 layout_name = qpage.page.source.config.get( 196 layout_name = page.source.config.get(
194 'default_layout', 'default') 197 'default_layout', 'default')
195 null_names = ['', 'none', 'nil'] 198 null_names = ['', 'none', 'nil']
196 if layout_name not in null_names: 199 if layout_name not in null_names:
197 with ctx.app.env.timerScope("BuildRenderData"): 200 with ctx.app.env.timerScope("BuildRenderData"):
198 add_layout_data(page_data, render_result['segments']) 201 add_layout_data(page_data, render_result['segments'])
199 202
200 with ctx.app.env.timerScope("PageRenderLayout"): 203 with ctx.app.env.timerScope("PageRenderLayout"):
201 layout_result = _do_render_layout( 204 layout_result = _do_render_layout(
202 layout_name, qpage, page_data) 205 layout_name, page, page_data)
203 else: 206 else:
204 layout_result = { 207 layout_result = {
205 'content': render_result['segments']['content'], 208 'content': render_result['segments']['content'],
206 'pass_info': None} 209 'pass_info': None}
207 210
208 rp = RenderedPage(qpage) 211 rp = RenderedPage(page, ctx.sub_num)
209 rp.data = page_data 212 rp.data = page_data
210 rp.content = layout_result['content'] 213 rp.content = layout_result['content']
211 rp.render_info[PASS_FORMATTING] = _unpickle_object( 214 rp.render_info[PASS_FORMATTING] = _unpickle_object(
212 render_result['pass_info']) 215 render_result['pass_info'])
213 if layout_result['pass_info'] is not None: 216 if layout_result['pass_info'] is not None:
231 env = ctx.app.env 234 env = ctx.app.env
232 235
233 stack = env.render_ctx_stack 236 stack = env.render_ctx_stack
234 stack.pushCtx(ctx) 237 stack.pushCtx(ctx)
235 238
236 qpage = ctx.qualified_page 239 page = ctx.page
240 page_uri = page.getUri(ctx.sub_num)
237 241
238 try: 242 try:
239 ctx.setCurrentPass(PASS_FORMATTING) 243 ctx.setCurrentPass(PASS_FORMATTING)
240 repo = ctx.app.env.rendered_segments_repository 244 repo = ctx.app.env.rendered_segments_repository
241 save_to_fs = True 245 save_to_fs = True
242 if ctx.app.env.fs_cache_only_for_main_page and not stack.is_main_ctx: 246 if ctx.app.env.fs_cache_only_for_main_page and not stack.is_main_ctx:
243 save_to_fs = False 247 save_to_fs = False
244 with ctx.app.env.timerScope("PageRenderSegments"): 248 with ctx.app.env.timerScope("PageRenderSegments"):
245 if repo is not None and not ctx.force_render: 249 if repo is not None and not ctx.force_render:
246 render_result = repo.get( 250 render_result = repo.get(
247 qpage.uri, 251 page_uri,
248 lambda: _do_render_page_segments_from_ctx(ctx), 252 lambda: _do_render_page_segments_from_ctx(ctx),
249 fs_cache_time=qpage.page.content_mtime, 253 fs_cache_time=page.content_mtime,
250 save_to_fs=save_to_fs) 254 save_to_fs=save_to_fs)
251 else: 255 else:
252 render_result = _do_render_page_segments_from_ctx(ctx) 256 render_result = _do_render_page_segments_from_ctx(ctx)
253 if repo: 257 if repo:
254 repo.put(qpage.uri, render_result, save_to_fs) 258 repo.put(page_uri, render_result, save_to_fs)
255 finally: 259 finally:
256 ctx.setCurrentPass(PASS_NONE) 260 ctx.setCurrentPass(PASS_NONE)
257 stack.popCtx() 261 stack.popCtx()
258 262
259 rs = RenderedSegments( 263 rs = RenderedSegments(
262 return rs 266 return rs
263 267
264 268
265 def _build_render_data(ctx): 269 def _build_render_data(ctx):
266 with ctx.app.env.timerScope("PageDataBuild"): 270 with ctx.app.env.timerScope("PageDataBuild"):
267 data_ctx = DataBuildingContext(ctx.qualified_page) 271 data_ctx = DataBuildingContext(ctx.page, ctx.sub_num)
268 data_ctx.pagination_source = ctx.pagination_source 272 data_ctx.pagination_source = ctx.pagination_source
269 data_ctx.pagination_filter = ctx.pagination_filter 273 data_ctx.pagination_filter = ctx.pagination_filter
270 page_data = build_page_data(data_ctx) 274 page_data = build_page_data(data_ctx)
271 if ctx.custom_data: 275 if ctx.custom_data:
272 page_data._appendMapping(ctx.custom_data) 276 page_data._appendMapping(ctx.custom_data)
277 page_data = _build_render_data(ctx) 281 page_data = _build_render_data(ctx)
278 return _do_render_page_segments(ctx, page_data) 282 return _do_render_page_segments(ctx, page_data)
279 283
280 284
281 def _do_render_page_segments(ctx, page_data): 285 def _do_render_page_segments(ctx, page_data):
282 page = ctx.qualified_page.page 286 page = ctx.page
283 app = page.app 287 app = page.app
284 288
285 engine_name = page.config.get('template_engine') 289 engine_name = page.config.get('template_engine')
286 format_name = page.config.get('format') 290 format_name = page.config.get('format')
287 291