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