From 9eaa22cc05d9bab1ee05f74ac2fdfe2ee1b61798 Mon Sep 17 00:00:00 2001 From: Mark McClain Date: Sun, 13 Mar 2011 11:39:30 -0400 Subject: [PATCH] template error traceback parity for jinja & genshi --- pecan/templating.py | 46 +++++++++++++++++++++++++-------- tests/templates/genshi_bad.html | 18 +++++++++++++ tests/templates/jinja_bad.html | 1 + tests/test_base.py | 16 ++++++++++-- 4 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 tests/templates/genshi_bad.html diff --git a/pecan/templating.py b/pecan/templating.py index d76ddcc..4b1fb6d 100644 --- a/pecan/templating.py +++ b/pecan/templating.py @@ -1,3 +1,5 @@ +import cgi + __all__ = ['RendererFactory'] _builtin_renderers = {} @@ -23,7 +25,8 @@ _builtin_renderers['json'] = JsonRenderer # try: - from genshi.template import TemplateLoader + from genshi.template import (TemplateLoader, + TemplateError as gTemplateError) class GenshiRenderer(object): def __init__(self, path, extra_vars): @@ -36,7 +39,13 @@ try: return stream.render('html') _builtin_renderers['genshi'] = GenshiRenderer - # TODO: add error formatter for genshi + + def format_genshi_error(exc_value): + if isinstance(exc_value, (gTemplateError)): + retval = '

Genshi error %s

' % cgi.escape(exc_value.message) + retval += format_line_context(exc_value.filename, exc_value.lineno) + return retval + error_formatters.append(format_genshi_error) except ImportError: #pragma no cover pass @@ -96,7 +105,7 @@ except ImportError: # pragma no cover # try: from jinja2 import Environment, FileSystemLoader - from jinja2 import exceptions as jinja_exceptions + from jinja2.exceptions import TemplateSyntaxError as jTemplateSyntaxError class JinjaRenderer(object): def __init__(self, path, extra_vars): @@ -109,19 +118,34 @@ try: _builtin_renderers['jinja'] = JinjaRenderer def format_jinja_error(exc_value): - import cgi - if isinstance(exc_value, (jinja_exceptions.TemplateSyntaxError)): - retval = '

Jinja2 template syntax error in \'%s\' on line %d

' % (exc_value.name, exc_value.lineno) - lines = [cgi.escape(x) for x in open(exc_value.filename)] - lineno = exc_value.lineno - 1 # files are 1 not 0 index - lines[lineno] = '%s' % lines[lineno] - - retval +='
%s
' % '
'.join(lines) + if isinstance(exc_value, (jTemplateSyntaxError)): + retval = '

Jinja2 template syntax error in \'%s\' on line %d

%s
' % (exc_value.name, exc_value.lineno, exc_value.message) + retval += format_line_context(exc_value.filename, exc_value.lineno) return retval error_formatters.append(format_jinja_error) except ImportError: # pragma no cover pass +# +# format helper function +# +def format_line_context(filename, lineno, context=10): + lines = open(filename).readlines() + + lineno = lineno - 1 # files are indexed by 1 not 0 + if lineno > 0: + start_lineno = max(lineno-context, 0) + end_lineno = lineno+context + + lines = [cgi.escape(l) for l in lines[start_lineno:end_lineno]] + i = lineno-start_lineno + lines[i] = '%s' % lines[i] + + else: + lines = [cgi.escape(l) for l in lines[:context]] + + return '
%s
' % ''.join(lines) + # # Extra Vars Rendering # diff --git a/tests/templates/genshi_bad.html b/tests/templates/genshi_bad.html new file mode 100644 index 0000000..557bc55 --- /dev/null +++ b/tests/templates/genshi_bad.html @@ -0,0 +1,18 @@ + + + + + + Hello, ${name}! + + + +

Hello, ${name}!

+ + + diff --git a/tests/templates/jinja_bad.html b/tests/templates/jinja_bad.html index db01676..3513871 100644 --- a/tests/templates/jinja_bad.html +++ b/tests/templates/jinja_bad.html @@ -7,6 +7,7 @@

Hello, {{name}}!

+{# open a block without and name #} {% block %} diff --git a/tests/test_base.py b/tests/test_base.py index b1b8039..b9ef95f 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -677,16 +677,29 @@ class TestEngines(object): @expose('genshi:genshi.html') def index(self, name='Jonathan'): return dict(name=name) + + @expose('genshi:genshi_bad.html') + def badtemplate(self): + return dict() app = TestApp(Pecan(RootController(), template_path=self.template_path)) r = app.get('/') assert r.status_int == 200 assert "

Hello, Jonathan!

" in r.body - app = TestApp(Pecan(RootController(), template_path=self.template_path)) r = app.get('/index.html?name=World') assert r.status_int == 200 assert "

Hello, World!

" in r.body + + error_msg = None + try: + r = app.get('/badtemplate.html') + except Exception, e: + for error_f in error_formatters: + error_msg = error_f(e) + if error_msg: + break + assert error_msg is not None def test_kajiki(self): if 'kajiki' not in builtin_renderers: @@ -702,7 +715,6 @@ class TestEngines(object): assert r.status_int == 200 assert "

Hello, Jonathan!

" in r.body - app = TestApp(Pecan(RootController(), template_path=self.template_path)) r = app.get('/index.html?name=World') assert r.status_int == 200 assert "

Hello, World!

" in r.body