Mercurial > piecrust2
comparison piecrust/templating/jinjaengine.py @ 128:28444014ce7d
Fix error reporting and counting of lines.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Fri, 14 Nov 2014 22:49:50 +0100 |
parents | e771c202583a |
children | 27d623a241c6 |
comparison
equal
deleted
inserted
replaced
127:bc63dc20baa0 | 128:28444014ce7d |
---|---|
1 import re | 1 import re |
2 import time | 2 import time |
3 import os.path | |
3 import logging | 4 import logging |
4 import threading | 5 import threading |
5 import strict_rfc3339 | 6 import strict_rfc3339 |
6 from jinja2 import Environment, FileSystemLoader, TemplateNotFound | 7 from jinja2 import Environment, FileSystemLoader, TemplateNotFound |
7 from jinja2.exceptions import TemplateSyntaxError | 8 from jinja2.exceptions import TemplateSyntaxError |
13 from pygments.formatters import HtmlFormatter | 14 from pygments.formatters import HtmlFormatter |
14 from pygments.lexers import get_lexer_by_name, guess_lexer | 15 from pygments.lexers import get_lexer_by_name, guess_lexer |
15 from piecrust.data.paginator import Paginator | 16 from piecrust.data.paginator import Paginator |
16 from piecrust.rendering import format_text | 17 from piecrust.rendering import format_text |
17 from piecrust.routing import CompositeRouteFunction | 18 from piecrust.routing import CompositeRouteFunction |
18 from piecrust.templating.base import TemplateEngine, TemplateNotFoundError | 19 from piecrust.templating.base import (TemplateEngine, TemplateNotFoundError, |
20 TemplatingError) | |
19 from piecrust.uriutil import multi_replace, get_first_sub_uri | 21 from piecrust.uriutil import multi_replace, get_first_sub_uri |
20 | 22 |
21 | 23 |
22 logger = logging.getLogger(__name__) | 24 logger = logging.getLogger(__name__) |
23 | 25 |
28 EXTENSIONS = ['jinja', 'jinja2', 'twig'] | 30 EXTENSIONS = ['jinja', 'jinja2', 'twig'] |
29 | 31 |
30 def __init__(self): | 32 def __init__(self): |
31 self.env = None | 33 self.env = None |
32 | 34 |
33 def renderString(self, txt, data, filename=None, line_offset=0): | 35 def renderString(self, txt, data, filename=None): |
34 self._ensureLoaded() | 36 self._ensureLoaded() |
35 tpl = self.env.from_string(txt) | 37 |
38 try: | |
39 tpl = self.env.from_string(txt) | |
40 except TemplateSyntaxError as tse: | |
41 raise self._getTemplatingError(tse, filename=filename) | |
42 except TemplateNotFound: | |
43 raise TemplateNotFoundError() | |
44 | |
36 try: | 45 try: |
37 return tpl.render(data) | 46 return tpl.render(data) |
38 except TemplateSyntaxError as tse: | 47 except TemplateSyntaxError as tse: |
39 tse.lineno += line_offset | 48 raise self._getTemplatingError(tse) |
40 if filename: | |
41 tse.filename = filename | |
42 import sys | |
43 _, __, traceback = sys.exc_info() | |
44 raise tse.with_traceback(traceback) | |
45 | 49 |
46 def renderFile(self, paths, data): | 50 def renderFile(self, paths, data): |
47 self._ensureLoaded() | 51 self._ensureLoaded() |
48 tpl = None | 52 tpl = None |
49 logger.debug("Looking for template: %s" % paths) | 53 logger.debug("Looking for template: %s" % paths) |
50 for p in paths: | 54 for p in paths: |
51 try: | 55 try: |
52 tpl = self.env.get_template(p) | 56 tpl = self.env.get_template(p) |
53 break | 57 break |
58 except TemplateSyntaxError as tse: | |
59 raise self._getTemplatingError(tse) | |
54 except TemplateNotFound: | 60 except TemplateNotFound: |
55 pass | 61 pass |
62 | |
56 if tpl is None: | 63 if tpl is None: |
57 raise TemplateNotFoundError() | 64 raise TemplateNotFoundError() |
58 return tpl.render(data) | 65 |
66 try: | |
67 return tpl.render(data) | |
68 except TemplateSyntaxError as tse: | |
69 raise self._getTemplatingError(tse) | |
70 | |
71 def _getTemplatingError(self, tse, filename=None): | |
72 filename = tse.filename or filename | |
73 if filename and os.path.isabs(filename): | |
74 filename = os.path.relpath(filename, self.env.app.root_dir) | |
75 err = TemplatingError(str(tse), filename, tse.lineno) | |
76 raise err from tse | |
59 | 77 |
60 def _ensureLoaded(self): | 78 def _ensureLoaded(self): |
61 if self.env: | 79 if self.env: |
62 return | 80 return |
63 | 81 |
72 loader = FileSystemLoader(self.app.templates_dirs) | 90 loader = FileSystemLoader(self.app.templates_dirs) |
73 extensions = [ | 91 extensions = [ |
74 PieCrustHighlightExtension, | 92 PieCrustHighlightExtension, |
75 PieCrustCacheExtension, | 93 PieCrustCacheExtension, |
76 PieCrustSpacelessExtension] | 94 PieCrustSpacelessExtension] |
95 twig_compatibility_mode = self.app.config.get('jinja/twig_compatibility') | |
96 if twig_compatibility_mode is None or twig_compatibility_mode is True: | |
97 extensions.append(PieCrustFormatExtension) | |
77 if autoescape: | 98 if autoescape: |
78 extensions.append('jinja2.ext.autoescape') | 99 extensions.append('jinja2.ext.autoescape') |
79 self.env = PieCrustEnvironment( | 100 self.env = PieCrustEnvironment( |
80 self.app, | 101 self.app, |
81 loader=loader, | 102 loader=loader, |
208 "#strftime-and-strptime-behavior" % | 229 "#strftime-and-strptime-behavior" % |
209 suggest) | 230 suggest) |
210 return time.strftime(fmt, time.localtime(value)) | 231 return time.strftime(fmt, time.localtime(value)) |
211 | 232 |
212 | 233 |
234 class PieCrustFormatExtension(Extension): | |
235 tags = set(['pcformat']) | |
236 | |
237 def __init__(self, environment): | |
238 super(PieCrustFormatExtension, self).__init__(environment) | |
239 | |
240 def parse(self, parser): | |
241 lineno = next(parser.stream).lineno | |
242 args = [parser.parse_expression()] | |
243 body = parser.parse_statements(['name:endpcformat'], drop_needle=True) | |
244 return CallBlock(self.call_method('_format', args), | |
245 [], [], body).set_lineno(lineno) | |
246 | |
247 def _format(self, format_name, caller=None): | |
248 body = caller() | |
249 text = format_text(self.environment.app, | |
250 format_name, | |
251 Markup(body.rstrip()).unescape(), | |
252 exact_format=True) | |
253 return text | |
254 | |
255 | |
213 class PieCrustHighlightExtension(Extension): | 256 class PieCrustHighlightExtension(Extension): |
214 tags = set(['highlight', 'geshi']) | 257 tags = set(['highlight', 'geshi']) |
215 | 258 |
216 def __init__(self, environment): | 259 def __init__(self, environment): |
217 super(PieCrustHighlightExtension, self).__init__(environment) | 260 super(PieCrustHighlightExtension, self).__init__(environment) |
238 # body of the block | 281 # body of the block |
239 body = parser.parse_statements(['name:endhighlight', 'name:endgeshi'], | 282 body = parser.parse_statements(['name:endhighlight', 'name:endgeshi'], |
240 drop_needle=True) | 283 drop_needle=True) |
241 | 284 |
242 return CallBlock(self.call_method('_highlight', args, kwargs), | 285 return CallBlock(self.call_method('_highlight', args, kwargs), |
243 [], [], body).set_lineno(lineno) | 286 [], [], body).set_lineno(lineno) |
244 | 287 |
245 def _highlight(self, lang, line_numbers=False, use_classes=False, | 288 def _highlight(self, lang, line_numbers=False, use_classes=False, |
246 css_class=None, css_id=None, caller=None): | 289 css_class=None, css_id=None, caller=None): |
247 # Try to be mostly compatible with Jinja2-highlight's settings. | 290 # Try to be mostly compatible with Jinja2-highlight's settings. |
248 body = caller() | 291 body = caller() |