comparison piecrust/page.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 e293f08d954e
children b6ec402d32bb
comparison
equal deleted inserted replaced
95:cb6eadea0845 96:0445a2232de7
9 import dateutil.parser 9 import dateutil.parser
10 import collections 10 import collections
11 from werkzeug.utils import cached_property 11 from werkzeug.utils import cached_property
12 from piecrust.configuration import (Configuration, ConfigurationError, 12 from piecrust.configuration import (Configuration, ConfigurationError,
13 parse_config_header) 13 parse_config_header)
14 from piecrust.environment import PHASE_PAGE_PARSING
15 14
16 15
17 logger = logging.getLogger(__name__) 16 logger = logging.getLogger(__name__)
18 17
19 18
31 if pf is not None: 30 if pf is not None:
32 values.setdefault('items_filters', pf) 31 values.setdefault('items_filters', pf)
33 return values 32 return values
34 33
35 34
35 FLAG_NONE = 0
36 FLAG_RAW_CACHE_VALID = 2**0
37
38
36 class Page(object): 39 class Page(object):
37 def __init__(self, source, source_metadata, rel_path): 40 def __init__(self, source, source_metadata, rel_path):
38 self.source = source 41 self.source = source
39 self.source_metadata = source_metadata 42 self.source_metadata = source_metadata
40 self.rel_path = rel_path 43 self.rel_path = rel_path
41 self._config = None 44 self._config = None
42 self._raw_content = None 45 self._raw_content = None
46 self._flags = FLAG_NONE
43 self._datetime = None 47 self._datetime = None
44 48
45 @property 49 @property
46 def app(self): 50 def app(self):
47 return self.source.app 51 return self.source.app
55 return self.source.resolveRef(self.rel_path) 59 return self.source.resolveRef(self.rel_path)
56 60
57 @cached_property 61 @cached_property
58 def path_mtime(self): 62 def path_mtime(self):
59 return os.path.getmtime(self.path) 63 return os.path.getmtime(self.path)
64
65 @property
66 def flags(self):
67 return self._flags
60 68
61 @property 69 @property
62 def config(self): 70 def config(self):
63 self._load() 71 self._load()
64 return self._config 72 return self._config
121 129
122 def _load(self): 130 def _load(self):
123 if self._config is not None: 131 if self._config is not None:
124 return 132 return
125 133
126 eis = self.app.env.exec_info_stack 134 config, content, was_cache_valid = load_page(self.app, self.path,
127 eis.pushPage(self, PHASE_PAGE_PARSING, None) 135 self.path_mtime)
128 try: 136 self._config = config
129 config, content = load_page(self.app, self.path, self.path_mtime) 137 self._raw_content = content
130 self._config = config 138 if was_cache_valid:
131 self._raw_content = content 139 self._flags |= FLAG_RAW_CACHE_VALID
132 finally:
133 eis.popPage()
134 140
135 141
136 class PageLoadingError(Exception): 142 class PageLoadingError(Exception):
137 def __init__(self, path, inner=None): 143 def __init__(self, path, inner=None):
138 super(PageLoadingError, self).__init__( 144 super(PageLoadingError, self).__init__(
193 _, __, traceback = sys.exc_info() 199 _, __, traceback = sys.exc_info()
194 raise PageLoadingError(path, e).with_traceback(traceback) 200 raise PageLoadingError(path, e).with_traceback(traceback)
195 201
196 202
197 def _do_load_page(app, path, path_mtime): 203 def _do_load_page(app, path, path_mtime):
198 exec_info = app.env.exec_info_stack.current_page_info
199 if exec_info is None:
200 raise Exception("Loading page '%s' but not execution context has "
201 "been created for it." % path)
202
203 # Check the cache first. 204 # Check the cache first.
204 cache = app.cache.getCache('pages') 205 cache = app.cache.getCache('pages')
205 cache_path = "%s.json" % hashlib.md5(path.encode('utf8')).hexdigest() 206 cache_path = "%s.json" % hashlib.md5(path.encode('utf8')).hexdigest()
206 page_time = path_mtime or os.path.getmtime(path) 207 page_time = path_mtime or os.path.getmtime(path)
207 if cache.isValid(cache_path, page_time): 208 if cache.isValid(cache_path, page_time):
208 exec_info.was_cache_valid = True
209 cache_data = json.loads(cache.read(cache_path), 209 cache_data = json.loads(cache.read(cache_path),
210 object_pairs_hook=collections.OrderedDict) 210 object_pairs_hook=collections.OrderedDict)
211 config = PageConfiguration(values=cache_data['config'], 211 config = PageConfiguration(values=cache_data['config'],
212 validate=False) 212 validate=False)
213 content = json_load_segments(cache_data['content']) 213 content = json_load_segments(cache_data['content'])
214 return config, content 214 return config, content, True
215 215
216 # Nope, load the page from the source file. 216 # Nope, load the page from the source file.
217 exec_info.was_cache_valid = False
218 logger.debug("Loading page configuration from: %s" % path) 217 logger.debug("Loading page configuration from: %s" % path)
219 with codecs.open(path, 'r', 'utf-8') as fp: 218 with codecs.open(path, 'r', 'utf-8') as fp:
220 raw = fp.read() 219 raw = fp.read()
221 header, offset = parse_config_header(raw) 220 header, offset = parse_config_header(raw)
222 221
233 cache_data = { 232 cache_data = {
234 'config': config.get(), 233 'config': config.get(),
235 'content': json_save_segments(content)} 234 'content': json_save_segments(content)}
236 cache.write(cache_path, json.dumps(cache_data)) 235 cache.write(cache_path, json.dumps(cache_data))
237 236
238 return config, content 237 return config, content, False
239 238
240 239
241 segment_pattern = re.compile( 240 segment_pattern = re.compile(
242 r"""^\-\-\-\s*(?P<name>\w+)(\:(?P<fmt>\w+))?\s*\-\-\-\s*$""", 241 r"""^\-\-\-\s*(?P<name>\w+)(\:(?P<fmt>\w+))?\s*\-\-\-\s*$""",
243 re.M) 242 re.M)