Mercurial > piecrust2
comparison piecrust/templating/jinjaengine.py @ 454:96d363e2da4b
templating: Let Jinja2 cache the parsed template for page contents.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Mon, 06 Jul 2015 21:32:40 -0700 |
parents | 4cdf6c2157a0 |
children | c40b7923c474 |
comparison
equal
deleted
inserted
replaced
453:8351a77e13f5 | 454:96d363e2da4b |
---|---|
31 EXTENSIONS = ['html', 'jinja', 'jinja2', 'twig'] | 31 EXTENSIONS = ['html', 'jinja', 'jinja2', 'twig'] |
32 | 32 |
33 def __init__(self): | 33 def __init__(self): |
34 self.env = None | 34 self.env = None |
35 | 35 |
36 def renderString(self, txt, data, filename=None): | 36 def renderSegmentPart(self, path, seg_part, data): |
37 self._ensureLoaded() | 37 self._ensureLoaded() |
38 | 38 |
39 do_render = False | 39 if not _string_needs_render(seg_part.content): |
40 index = txt.find('{') | 40 return seg_part.content |
41 while index >= 0: | 41 |
42 ch = txt[index + 1] | 42 part_path = _make_segment_part_path(path, seg_part.offset) |
43 if ch == '{' or ch == '%': | 43 self.env.loader.segment_parts_cache[part_path] = ( |
44 do_render = True | 44 path, seg_part.content) |
45 break | |
46 index = txt.find('{', index + 1) | |
47 | |
48 if not do_render: | |
49 return txt | |
50 | |
51 try: | 45 try: |
52 tpl = self.env.from_string(txt) | 46 tpl = self.env.get_template(part_path) |
53 except TemplateSyntaxError as tse: | 47 except TemplateSyntaxError as tse: |
54 raise self._getTemplatingError(tse, filename=filename) | 48 raise self._getTemplatingError(tse, filename=path) |
55 except TemplateNotFound: | 49 except TemplateNotFound: |
56 raise TemplateNotFoundError() | 50 raise TemplateNotFoundError() |
57 | 51 |
58 try: | 52 try: |
59 return tpl.render(data) | 53 return tpl.render(data) |
97 autoescape = self.app.config.get('twig/auto_escape') | 91 autoescape = self.app.config.get('twig/auto_escape') |
98 if autoescape is None: | 92 if autoescape is None: |
99 autoescape = True | 93 autoescape = True |
100 | 94 |
101 logger.debug("Creating Jinja environment with folders: %s" % | 95 logger.debug("Creating Jinja environment with folders: %s" % |
102 self.app.templates_dirs) | 96 self.app.templates_dirs) |
103 loader = FileSystemLoader(self.app.templates_dirs) | 97 loader = PieCrustLoader(self.app.templates_dirs) |
104 extensions = [ | 98 extensions = [ |
105 PieCrustHighlightExtension, | 99 PieCrustHighlightExtension, |
106 PieCrustCacheExtension, | 100 PieCrustCacheExtension, |
107 PieCrustSpacelessExtension, | 101 PieCrustSpacelessExtension, |
108 PieCrustFormatExtension] | 102 PieCrustFormatExtension] |
110 extensions.append('jinja2.ext.autoescape') | 104 extensions.append('jinja2.ext.autoescape') |
111 self.env = PieCrustEnvironment( | 105 self.env = PieCrustEnvironment( |
112 self.app, | 106 self.app, |
113 loader=loader, | 107 loader=loader, |
114 extensions=extensions) | 108 extensions=extensions) |
109 | |
110 | |
111 def _string_needs_render(txt): | |
112 index = txt.find('{') | |
113 while index >= 0: | |
114 ch = txt[index + 1] | |
115 if ch == '{' or ch == '%': | |
116 return True | |
117 index = txt.find('{', index + 1) | |
118 return False | |
119 | |
120 | |
121 def _make_segment_part_path(path, start): | |
122 return '$part=%s:%d' % (path, start) | |
123 | |
124 | |
125 class PieCrustLoader(FileSystemLoader): | |
126 def __init__(self, searchpath, encoding='utf-8'): | |
127 super(PieCrustLoader, self).__init__(searchpath, encoding) | |
128 self.segment_parts_cache = {} | |
129 | |
130 def get_source(self, environment, template): | |
131 if template.startswith('$part='): | |
132 filename, seg_part = self.segment_parts_cache[template] | |
133 | |
134 mtime = os.path.getmtime(filename) | |
135 | |
136 def uptodate(): | |
137 try: | |
138 return os.path.getmtime(filename) == mtime | |
139 except OSError: | |
140 return False | |
141 | |
142 return seg_part, filename, uptodate | |
143 | |
144 return super(PieCrustLoader, self).get_source(environment, template) | |
115 | 145 |
116 | 146 |
117 class PieCrustEnvironment(Environment): | 147 class PieCrustEnvironment(Environment): |
118 def __init__(self, app, *args, **kwargs): | 148 def __init__(self, app, *args, **kwargs): |
119 self.app = app | 149 self.app = app |
311 | 341 |
312 # Extract the language name. | 342 # Extract the language name. |
313 args = [parser.parse_expression()] | 343 args = [parser.parse_expression()] |
314 | 344 |
315 # Extract optional arguments. | 345 # Extract optional arguments. |
316 kwarg_names = {'line_numbers': 0, 'use_classes': 0, 'class': 1, 'id': 1} | 346 kwarg_names = {'line_numbers': 0, 'use_classes': 0, 'class': 1, |
347 'id': 1} | |
317 kwargs = {} | 348 kwargs = {} |
318 while not parser.stream.current.test('block_end'): | 349 while not parser.stream.current.test('block_end'): |
319 name = parser.stream.expect('name') | 350 name = parser.stream.expect('name') |
320 if name.value not in kwarg_names: | 351 if name.value not in kwarg_names: |
321 raise Exception("'%s' is not a valid argument for the code " | 352 raise Exception("'%s' is not a valid argument for the code " |
331 | 362 |
332 return CallBlock(self.call_method('_highlight', args, kwargs), | 363 return CallBlock(self.call_method('_highlight', args, kwargs), |
333 [], [], body).set_lineno(lineno) | 364 [], [], body).set_lineno(lineno) |
334 | 365 |
335 def _highlight(self, lang, line_numbers=False, use_classes=False, | 366 def _highlight(self, lang, line_numbers=False, use_classes=False, |
336 css_class=None, css_id=None, caller=None): | 367 css_class=None, css_id=None, caller=None): |
337 # Try to be mostly compatible with Jinja2-highlight's settings. | 368 # Try to be mostly compatible with Jinja2-highlight's settings. |
338 body = caller() | 369 body = caller() |
339 | 370 |
340 if lang is None: | 371 if lang is None: |
341 lexer = guess_lexer(body) | 372 lexer = guess_lexer(body) |
385 args = [parser.parse_expression()] | 416 args = [parser.parse_expression()] |
386 | 417 |
387 # now we parse the body of the cache block up to `endpccache` and | 418 # now we parse the body of the cache block up to `endpccache` and |
388 # drop the needle (which would always be `endpccache` in that case) | 419 # drop the needle (which would always be `endpccache` in that case) |
389 body = parser.parse_statements(['name:endpccache', 'name:endcache'], | 420 body = parser.parse_statements(['name:endpccache', 'name:endcache'], |
390 drop_needle=True) | 421 drop_needle=True) |
391 | 422 |
392 # now return a `CallBlock` node that calls our _cache_support | 423 # now return a `CallBlock` node that calls our _cache_support |
393 # helper method on this extension. | 424 # helper method on this extension. |
394 return CallBlock(self.call_method('_cache_support', args), | 425 return CallBlock(self.call_method('_cache_support', args), |
395 [], [], body).set_lineno(lineno) | 426 [], [], body).set_lineno(lineno) |
396 | 427 |
397 def _cache_support(self, name, caller): | 428 def _cache_support(self, name, caller): |
398 key = self.environment.piecrust_cache_prefix + name | 429 key = self.environment.piecrust_cache_prefix + name |
399 | 430 |
400 exc_stack = self.environment.app.env.exec_info_stack | 431 exc_stack = self.environment.app.env.exec_info_stack |