From 10228fad9797a85a879286eb51ec4ea287225c97 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Mon, 12 Mar 2012 14:22:46 -0400 Subject: - [feature] The html_error_template() will now apply Pygments highlighting to the source code displayed in the traceback, if Pygments if available. Courtesy Ben Trofatter [ticket:95] --- CHANGES | 6 +++++ mako/exceptions.py | 51 ++++++++++++++++++++++++++++++++++--- mako/ext/pygmentplugin.py | 33 +++++++++++++++--------- test/test_exceptions.py | 65 +++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 131 insertions(+), 24 deletions(-) diff --git a/CHANGES b/CHANGES index c84fb92..b07a295 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,10 @@ 0.6.3 +- [feature] The html_error_template() will now + apply Pygments highlighting to the source + code displayed in the traceback, if Pygments + if available. Courtesy Ben Trofatter + [ticket:95] + - [feature] Added support for context managers, i.e. "% with x as e:/ % endwith" support. Courtesy Ben Trofatter [ticket:147] diff --git a/mako/exceptions.py b/mako/exceptions.py index bce99b7..c9e04ea 100644 --- a/mako/exceptions.py +++ b/mako/exceptions.py @@ -227,6 +227,15 @@ Traceback (most recent call last): ${tback.errorname}: ${tback.message} """) + +try: + from mako.ext.pygmentplugin import syntax_highlight, pygments_html_formatter +except ImportError: + from mako.filters import html_escape + pygments_html_formatter = None + def syntax_highlight(filename='', language=None): + return html_escape + def html_error_template(): """Provides a template that renders a stack trace in an HTML format, providing an excerpt of code as well as substituting source template @@ -242,7 +251,7 @@ def html_error_template(): import mako.template return mako.template.Template(r""" <%! - from mako.exceptions import RichTraceback + from mako.exceptions import RichTraceback, syntax_highlight, pygments_html_formatter %> <%page args="full=True, css=True, error=None, traceback=None"/> % if full: @@ -262,6 +271,21 @@ def html_error_template(): .location { font-size:80%; } .highlight { white-space:pre; } .sampleline { white-space:pre; } + + % if pygments_html_formatter: + ${pygments_html_formatter.get_style_defs()} + .linenos { min-width: 2.5em; text-align: right; } + pre { margin: 0; } + .syntax-highlighted { padding: 0 10px; } + .syntax-highlightedtable { border-spacing: 1px; } + .nonhighlight { border-top: 1px solid #DFDFDF; border-bottom: 1px solid #DFDFDF; } + .stacktrace .nonhighlight { margin: 5px 15px 10px; } + .sourceline { margin: 0 0; font-family:monospace; } + .code { background-color: #F8F8F8; width: 100%; } + .error .code { background-color: #FFBDBD; } + .error .syntax-highlighted { background-color: #FFBDBD; } + % endif + % endif % if full: @@ -285,10 +309,23 @@ def html_error_template():
% for index in range(max(0, line-4),min(len(lines), line+5)): + <% + if pygments_html_formatter: + pygments_html_formatter.linenostart = index + 1 + %> % if index + 1 == line: -
${index + 1} ${lines[index] | h}
+ <% + if pygments_html_formatter: + old_cssclass = pygments_html_formatter.cssclass + pygments_html_formatter.cssclass = 'error ' + old_cssclass + %> + ${lines[index] | syntax_highlight(language='mako')} + <% + if pygments_html_formatter: + pygments_html_formatter.cssclass = old_cssclass + %> % else: -
${index + 1} ${lines[index] | h}
+ ${lines[index] | syntax_highlight(language='mako')} % endif % endfor
@@ -298,7 +335,13 @@ def html_error_template():
% for (filename, lineno, function, line) in tback.reverse_traceback:
${filename}, line ${lineno}:
-
${line | h}
+
+ <% + if pygments_html_formatter: + pygments_html_formatter.linenostart = lineno + %> +
${line | syntax_highlight(filename)}
+
% endfor
diff --git a/mako/ext/pygmentplugin.py b/mako/ext/pygmentplugin.py index 0b15126..98e0c5d 100644 --- a/mako/ext/pygmentplugin.py +++ b/mako/ext/pygmentplugin.py @@ -4,20 +4,16 @@ # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -import re -try: - set -except NameError: - from sets import Set as set - from pygments.lexers.web import \ HtmlLexer, XmlLexer, JavascriptLexer, CssLexer -from pygments.lexers.agile import PythonLexer -from pygments.lexer import Lexer, DelegatingLexer, RegexLexer, bygroups, \ - include, using, this -from pygments.token import Error, Punctuation, \ - Text, Comment, Operator, Keyword, Name, String, Number, Other, Literal -from pygments.util import html_doctype_matches, looks_like_xml +from pygments.lexers.agile import PythonLexer, Python3Lexer +from pygments.lexer import DelegatingLexer, RegexLexer, bygroups, \ + include, using +from pygments.token import \ + Text, Comment, Operator, Keyword, Name, String, Other +from pygments.formatters.html import HtmlFormatter +from pygments import highlight +from mako import util class MakoLexer(RegexLexer): name = 'Mako' @@ -105,3 +101,16 @@ class MakoCssLexer(DelegatingLexer): def __init__(self, **options): super(MakoCssLexer, self).__init__(CssLexer, MakoLexer, **options) + + +pygments_html_formatter = HtmlFormatter(cssclass='syntax-highlighted', linenos=True) +def syntax_highlight(filename='', language=None): + mako_lexer = MakoLexer() + if util.py3k: + python_lexer = Python3Lexer() + else: + python_lexer = PythonLexer() + if filename.startswith('memory:') or language == 'mako': + return lambda string: highlight(string, mako_lexer, pygments_html_formatter) + return lambda string: highlight(string, python_lexer, pygments_html_formatter) + diff --git a/test/test_exceptions.py b/test/test_exceptions.py index 97987e6..ea27dda 100644 --- a/test/test_exceptions.py +++ b/test/test_exceptions.py @@ -87,10 +87,25 @@ ${u'привет'} html_error if util.py3k: - assert u"3 ${'привет'}".encode(sys.getdefaultencoding(), + try: + import pygments + assert u"".encode(sys.getdefaultencoding(), + 'htmlentityreplace') in html_error + except ImportError: + assert u"3 ${'привет'}".encode(sys.getdefaultencoding(), 'htmlentityreplace') in html_error else: - assert u"3 ${u'привет'}".encode(sys.getdefaultencoding(), + try: + import pygments + assert u'
3
'\ + '
${u''\
+                            'привет'\
+                            ''}'.encode(
+                                    sys.getdefaultencoding(),
+                                    'htmlentityreplace') in html_error
+                except ImportError:
+                    assert u"3 ${u'привет'}".encode(sys.getdefaultencoding(),
                                             'htmlentityreplace') in html_error
         else:
             assert False, ("This function should trigger a CompileException, "
@@ -138,7 +153,16 @@ ${foobar}
         ${self.body()}
         """)
 
-        assert '
${foobar}
' in \ + try: + import pygments + assert '
'\ + '
3
'\ + '
'\ + '
${'\
+                    'foobar}' in \
+                result_lines(l.get_template("foo.html").render_unicode())
+        except ImportError:
+            assert '
${foobar}
' in \ result_lines(l.get_template("foo.html").render_unicode()) def test_utf8_format_exceptions(self): @@ -152,12 +176,37 @@ ${foobar} l.put_string("foo.html", """# -*- coding: utf-8 -*-\n${u'привет' + foobar}""") if util.py3k: - assert u'
${'привет' + foobar}
'\ - in result_lines(l.get_template("foo.html").render().decode('utf-8')) + try: + import pygments + assert '
2
'\ + '
${'\
+                        ''привет' + '\
+                        'foobar}'\
+                        '' in \
+                    result_lines(l.get_template("foo.html").render().decode('utf-8'))
+            except ImportError:
+                assert u'
${'привет' + foobar}
'\ + in result_lines(l.get_template("foo.html").render().decode('utf-8')) else: - assert '
2 ${u'пр'\ - 'ивет' + foobar}
' \ - in result_lines(l.get_template("foo.html").render().decode('utf-8')) + try: + import pygments + + assert '
2
'\ + '
${'\
+                        'u'прив'\
+                        'ет' + '\
+                        'foobar}'\
+                        '' in \
+                    result_lines(l.get_template("foo.html").render().decode('utf-8'))
+
+            except ImportError:
+                assert '
2 ${u'пр'\ + 'ивет' + foobar}
' \ + in result_lines(l.get_template("foo.html").render().decode('utf-8')) def test_custom_tback(self): -- cgit v1.2.1