comparison piecrust/rendering.py @ 96:0445a2232de7

Improvements and fixes to incremental baking. * Better handling of the render pass during page rendering. * Used sources are paired with the pass they were used in. * Proper use and invalidation of the rendered segments cache based on render passes. * The `Assetor` is also better tracking what was used in a page. * Add flags on a page to get better caching information for the debug window. * Property invalidation of the previous bake record when needed. * Better information about why pages are delayed.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 07 Sep 2014 23:48:57 -0700
parents 2fec3ee1298f
children 28444014ce7d
comparison
equal deleted inserted replaced
95:cb6eadea0845 96:0445a2232de7
1 import re 1 import re
2 import os.path 2 import os.path
3 import logging 3 import logging
4 from piecrust.data.builder import (DataBuildingContext, build_page_data, 4 from piecrust.data.builder import (DataBuildingContext, build_page_data,
5 build_layout_data) 5 build_layout_data)
6 from piecrust.environment import PHASE_PAGE_FORMATTING, PHASE_PAGE_RENDERING
7 from piecrust.sources.base import PageSource 6 from piecrust.sources.base import PageSource
8 from piecrust.uriutil import get_slug 7 from piecrust.uriutil import get_slug
9 8
10 9
11 logger = logging.getLogger(__name__) 10 logger = logging.getLogger(__name__)
29 self.execution_info = None 28 self.execution_info = None
30 29
31 @property 30 @property
32 def app(self): 31 def app(self):
33 return self.page.app 32 return self.page.app
33
34
35 PASS_NONE = 0
36 PASS_FORMATTING = 1
37 PASS_RENDERING = 2
34 38
35 39
36 class PageRenderingContext(object): 40 class PageRenderingContext(object):
37 def __init__(self, page, uri, page_num=1): 41 def __init__(self, page, uri, page_num=1):
38 self.page = page 42 self.page = page
44 self.use_cache = False 48 self.use_cache = False
45 self.used_assets = None 49 self.used_assets = None
46 self.used_pagination = None 50 self.used_pagination = None
47 self.used_source_names = set() 51 self.used_source_names = set()
48 self.used_taxonomy_terms = set() 52 self.used_taxonomy_terms = set()
53 self.current_pass = PASS_NONE
49 54
50 @property 55 @property
51 def app(self): 56 def app(self):
52 return self.page.app 57 return self.page.app
53 58
64 69
65 def setPagination(self, paginator): 70 def setPagination(self, paginator):
66 if self.used_pagination is not None: 71 if self.used_pagination is not None:
67 raise Exception("Pagination has already been used.") 72 raise Exception("Pagination has already been used.")
68 self.used_pagination = paginator 73 self.used_pagination = paginator
74 self.addUsedSource(paginator._source)
69 75
70 def addUsedSource(self, source): 76 def addUsedSource(self, source):
71 if isinstance(source, PageSource): 77 if isinstance(source, PageSource):
72 self.used_source_names.add(source.name) 78 self.used_source_names.add((source.name, self.current_pass))
73 79
74 80
75 def render_page(ctx): 81 def render_page(ctx):
76 eis = ctx.app.env.exec_info_stack 82 eis = ctx.app.env.exec_info_stack
77 eis.pushPage(ctx.page, PHASE_PAGE_RENDERING, ctx) 83 eis.pushPage(ctx.page, ctx)
78 try: 84 try:
79 page = ctx.page 85 page = ctx.page
80 86
81 # Build the data for both segment and layout rendering. 87 # Build the data for both segment and layout rendering.
82 data_ctx = DataBuildingContext(page, ctx.uri, ctx.page_num) 88 data_ctx = DataBuildingContext(page, ctx.uri, ctx.page_num)
85 page_data = build_page_data(data_ctx) 91 page_data = build_page_data(data_ctx)
86 if ctx.custom_data: 92 if ctx.custom_data:
87 page_data.update(ctx.custom_data) 93 page_data.update(ctx.custom_data)
88 94
89 # Render content segments. 95 # Render content segments.
96 ctx.current_pass = PASS_FORMATTING
90 repo = ctx.app.env.rendered_segments_repository 97 repo = ctx.app.env.rendered_segments_repository
91 if repo: 98 if repo:
92 cache_key = '%s:%s' % (ctx.uri, ctx.page_num) 99 cache_key = '%s:%s' % (ctx.uri, ctx.page_num)
93 page_time = page.path_mtime 100 page_time = page.path_mtime
94 contents = repo.get(cache_key, 101 contents = repo.get(cache_key,
96 fs_cache_time=page_time) 103 fs_cache_time=page_time)
97 else: 104 else:
98 contents = _do_render_page_segments(page, page_data) 105 contents = _do_render_page_segments(page, page_data)
99 106
100 # Render layout. 107 # Render layout.
108 ctx.current_pass = PASS_RENDERING
101 layout_name = page.config.get('layout') 109 layout_name = page.config.get('layout')
102 if layout_name is None: 110 if layout_name is None:
103 layout_name = page.source.config.get('default_layout', 'default') 111 layout_name = page.source.config.get('default_layout', 'default')
104 null_names = ['', 'none', 'nil'] 112 null_names = ['', 'none', 'nil']
105 if layout_name not in null_names: 113 if layout_name not in null_names:
112 rp.data = page_data 120 rp.data = page_data
113 rp.content = output 121 rp.content = output
114 rp.execution_info = eis.current_page_info 122 rp.execution_info = eis.current_page_info
115 return rp 123 return rp
116 finally: 124 finally:
125 ctx.current_pass = PASS_NONE
117 eis.popPage() 126 eis.popPage()
118 127
119 128
120 def render_page_segments(ctx): 129 def render_page_segments(ctx):
121 repo = ctx.app.env.rendered_segments_repository 130 repo = ctx.app.env.rendered_segments_repository
128 return _do_render_page_segments_from_ctx(ctx) 137 return _do_render_page_segments_from_ctx(ctx)
129 138
130 139
131 def _do_render_page_segments_from_ctx(ctx): 140 def _do_render_page_segments_from_ctx(ctx):
132 eis = ctx.app.env.exec_info_stack 141 eis = ctx.app.env.exec_info_stack
133 eis.pushPage(ctx.page, PHASE_PAGE_FORMATTING, ctx) 142 eis.pushPage(ctx.page, ctx)
143 ctx.current_pass = PASS_FORMATTING
134 try: 144 try:
135 data_ctx = DataBuildingContext(ctx.page, ctx.uri, ctx.page_num) 145 data_ctx = DataBuildingContext(ctx.page, ctx.uri, ctx.page_num)
136 page_data = build_page_data(data_ctx) 146 page_data = build_page_data(data_ctx)
137 return _do_render_page_segments(ctx.page, page_data) 147 return _do_render_page_segments(ctx.page, page_data)
138 finally: 148 finally:
149 ctx.current_pass = PASS_NONE
139 eis.popPage() 150 eis.popPage()
140 151
141 152
142 def _do_render_page_segments(page, page_data): 153 def _do_render_page_segments(page, page_data):
143 app = page.app 154 app = page.app