Mercurial > piecrust2
comparison piecrust/templating/jinjaengine.py @ 65:071cc99b1779
Jinja templating now has `spaceless`, `|keys` and `|values`.
| author | Ludovic Chabant <ludovic@chabant.com> |
|---|---|
| date | Fri, 29 Aug 2014 08:06:43 -0700 |
| parents | e3e3de44377c |
| children | e771c202583a |
comparison
equal
deleted
inserted
replaced
| 64:9ae3237365eb | 65:071cc99b1779 |
|---|---|
| 3 import logging | 3 import logging |
| 4 import strict_rfc3339 | 4 import strict_rfc3339 |
| 5 from jinja2 import Environment, FileSystemLoader, TemplateNotFound | 5 from jinja2 import Environment, FileSystemLoader, TemplateNotFound |
| 6 from jinja2.exceptions import TemplateSyntaxError | 6 from jinja2.exceptions import TemplateSyntaxError |
| 7 from jinja2.ext import Extension, Markup | 7 from jinja2.ext import Extension, Markup |
| 8 from jinja2.lexer import Token, describe_token | |
| 8 from jinja2.nodes import CallBlock, Const | 9 from jinja2.nodes import CallBlock, Const |
| 10 from compressinja.html import HtmlCompressor, StreamProcessContext | |
| 9 from pygments import highlight | 11 from pygments import highlight |
| 10 from pygments.formatters import HtmlFormatter | 12 from pygments.formatters import HtmlFormatter |
| 11 from pygments.lexers import get_lexer_by_name, guess_lexer | 13 from pygments.lexers import get_lexer_by_name, guess_lexer |
| 12 from piecrust.data.paginator import Paginator | 14 from piecrust.data.paginator import Paginator |
| 13 from piecrust.rendering import format_text | 15 from piecrust.rendering import format_text |
| 67 logger.debug("Creating Jinja environment with folders: %s" % | 69 logger.debug("Creating Jinja environment with folders: %s" % |
| 68 self.app.templates_dirs) | 70 self.app.templates_dirs) |
| 69 loader = FileSystemLoader(self.app.templates_dirs) | 71 loader = FileSystemLoader(self.app.templates_dirs) |
| 70 extensions = [ | 72 extensions = [ |
| 71 PieCrustHighlightExtension, | 73 PieCrustHighlightExtension, |
| 72 PieCrustCacheExtension] | 74 PieCrustCacheExtension, |
| 75 PieCrustSpacelessExtension] | |
| 73 if autoescape: | 76 if autoescape: |
| 74 extensions.append('jinja2.ext.autoescape') | 77 extensions.append('jinja2.ext.autoescape') |
| 75 self.env = PieCrustEnvironment( | 78 self.env = PieCrustEnvironment( |
| 76 self.app, | 79 self.app, |
| 77 loader=loader, | 80 loader=loader, |
| 84 self.app = app | 87 self.app = app |
| 85 self.auto_reload = True | 88 self.auto_reload = True |
| 86 self.globals.update({ | 89 self.globals.update({ |
| 87 'fail': raise_exception}) | 90 'fail': raise_exception}) |
| 88 self.filters.update({ | 91 self.filters.update({ |
| 92 'keys': get_dict_keys, | |
| 93 'values': get_dict_values, | |
| 89 'paginate': self._paginate, | 94 'paginate': self._paginate, |
| 90 'formatwith': self._formatWith, | 95 'formatwith': self._formatWith, |
| 91 'markdown': lambda v: self._formatWith(v, 'markdown'), | 96 'markdown': lambda v: self._formatWith(v, 'markdown'), |
| 92 'textile': lambda v: self._formatWith(v, 'textile'), | 97 'textile': lambda v: self._formatWith(v, 'textile'), |
| 93 'nocache': add_no_cache_parameter, | 98 'nocache': add_no_cache_parameter, |
| 138 | 143 |
| 139 def raise_exception(msg): | 144 def raise_exception(msg): |
| 140 raise Exception(msg) | 145 raise Exception(msg) |
| 141 | 146 |
| 142 | 147 |
| 148 def get_dict_keys(value): | |
| 149 if isinstance(value, list): | |
| 150 return [i[0] for i in value] | |
| 151 return value.keys() | |
| 152 | |
| 153 | |
| 154 def get_dict_values(value): | |
| 155 if isinstance(value, list): | |
| 156 return [i[1] for i in value] | |
| 157 return value.values() | |
| 158 | |
| 159 | |
| 143 def add_no_cache_parameter(value, param_name='t', param_value=None): | 160 def add_no_cache_parameter(value, param_name='t', param_value=None): |
| 144 if not param_value: | 161 if not param_value: |
| 145 param_value = time.time() | 162 param_value = time.time() |
| 146 if '?' in value: | 163 if '?' in value: |
| 147 value += '&' | 164 value += '&' |
| 249 code = highlight(Markup(body.rstrip()).unescape(), lexer, formatter) | 266 code = highlight(Markup(body.rstrip()).unescape(), lexer, formatter) |
| 250 return code | 267 return code |
| 251 | 268 |
| 252 | 269 |
| 253 class PieCrustCacheExtension(Extension): | 270 class PieCrustCacheExtension(Extension): |
| 254 tags = set(['pccache']) | 271 tags = set(['pccache', 'cache']) |
| 255 | 272 |
| 256 def __init__(self, environment): | 273 def __init__(self, environment): |
| 257 super(PieCrustCacheExtension, self).__init__(environment) | 274 super(PieCrustCacheExtension, self).__init__(environment) |
| 258 | 275 |
| 259 environment.extend( | 276 environment.extend( |
| 271 # now we parse a single expression that is used as cache key. | 288 # now we parse a single expression that is used as cache key. |
| 272 args = [parser.parse_expression()] | 289 args = [parser.parse_expression()] |
| 273 | 290 |
| 274 # now we parse the body of the cache block up to `endpccache` and | 291 # now we parse the body of the cache block up to `endpccache` and |
| 275 # drop the needle (which would always be `endpccache` in that case) | 292 # drop the needle (which would always be `endpccache` in that case) |
| 276 body = parser.parse_statements(['name:endpccache'], drop_needle=True) | 293 body = parser.parse_statements(['name:endpccache', 'name:endcache'], |
| 294 drop_needle=True) | |
| 277 | 295 |
| 278 # now return a `CallBlock` node that calls our _cache_support | 296 # now return a `CallBlock` node that calls our _cache_support |
| 279 # helper method on this extension. | 297 # helper method on this extension. |
| 280 return CallBlock(self.call_method('_cache_support', args), | 298 return CallBlock(self.call_method('_cache_support', args), |
| 281 [], [], body).set_lineno(lineno) | 299 [], [], body).set_lineno(lineno) |
| 290 if rv is not None: | 308 if rv is not None: |
| 291 return rv | 309 return rv |
| 292 rv = caller() | 310 rv = caller() |
| 293 self.environment.piecrust_cache[key] = rv | 311 self.environment.piecrust_cache[key] = rv |
| 294 return rv | 312 return rv |
| 313 | |
| 314 | |
| 315 class PieCrustSpacelessExtension(HtmlCompressor): | |
| 316 """ A re-implementation of `SelectiveHtmlCompressor` so that we can | |
| 317 both use `strip` or `spaceless` in templates. | |
| 318 """ | |
| 319 def filter_stream(self, stream): | |
| 320 ctx = StreamProcessContext(stream) | |
| 321 strip_depth = 0 | |
| 322 while 1: | |
| 323 if stream.current.type == 'block_begin': | |
| 324 for tk in ['strip', 'spaceless']: | |
| 325 change = self._processToken(ctx, stream, tk) | |
| 326 if change != 0: | |
| 327 strip_depth += change | |
| 328 if strip_depth < 0: | |
| 329 ctx.fail('Unexpected tag end%s' % tk) | |
| 330 break | |
| 331 if strip_depth > 0 and stream.current.type == 'data': | |
| 332 ctx.token = stream.current | |
| 333 value = self.normalize(ctx) | |
| 334 yield Token(stream.current.lineno, 'data', value) | |
| 335 else: | |
| 336 yield stream.current | |
| 337 next(stream) | |
| 338 | |
| 339 def _processToken(self, ctx, stream, test_token): | |
| 340 change = 0 | |
| 341 if (stream.look().test('name:%s' % test_token) or | |
| 342 stream.look().test('name:end%s' % test_token)): | |
| 343 stream.skip() | |
| 344 if stream.current.value == test_token: | |
| 345 change = 1 | |
| 346 else: | |
| 347 change = -1 | |
| 348 stream.skip() | |
| 349 if stream.current.type != 'block_end': | |
| 350 ctx.fail('expected end of block, got %s' % | |
| 351 describe_token(stream.current)) | |
| 352 stream.skip() | |
| 353 return change | |
| 295 | 354 |
| 296 | 355 |
| 297 def php_format_to_strftime_format(fmt): | 356 def php_format_to_strftime_format(fmt): |
| 298 replacements = { | 357 replacements = { |
| 299 'd': '%d', | 358 'd': '%d', |
