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