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