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()