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