1import sys
2
3from mako import exceptions
4from mako.lookup import TemplateLookup
5from mako.template import Template
6from mako.testing.exclusions import requires_no_pygments_exceptions
7from mako.testing.exclusions import requires_pygments_14
8from mako.testing.fixtures import TemplateTest
9from mako.testing.helpers import result_lines
10
11
12class ExceptionsTest(TemplateTest):
13    def test_html_error_template(self):
14        """test the html_error_template"""
15        code = """
16% i = 0
17"""
18        try:
19            template = Template(code)
20            template.render_unicode()
21            assert False
22        except exceptions.CompileException:
23            html_error = exceptions.html_error_template().render_unicode()
24            assert (
25                "CompileException: Fragment 'i = 0' is not "
26                "a partial control statement at line: 2 char: 1"
27            ) in html_error
28            assert "<style>" in html_error
29            html_error_stripped = html_error.strip()
30            assert html_error_stripped.startswith("<html>")
31            assert html_error_stripped.endswith("</html>")
32
33            not_full = exceptions.html_error_template().render_unicode(
34                full=False
35            )
36            assert "<html>" not in not_full
37            assert "<style>" in not_full
38
39            no_css = exceptions.html_error_template().render_unicode(css=False)
40            assert "<style>" not in no_css
41        else:
42            assert False, (
43                "This function should trigger a CompileException, "
44                "but didn't"
45            )
46
47    def test_text_error_template(self):
48        code = """
49% i = 0
50"""
51        try:
52            template = Template(code)
53            template.render_unicode()
54            assert False
55        except exceptions.CompileException:
56            text_error = exceptions.text_error_template().render_unicode()
57            assert "Traceback (most recent call last):" in text_error
58            assert (
59                "CompileException: Fragment 'i = 0' is not a partial "
60                "control statement"
61            ) in text_error
62
63    @requires_pygments_14
64    def test_utf8_html_error_template_pygments(self):
65        """test the html_error_template with a Template containing UTF-8
66        chars"""
67
68        code = """# -*- coding: utf-8 -*-
69% if 2 == 2: /an error
70${'привет'}
71% endif
72"""
73        try:
74            template = Template(code)
75            template.render_unicode()
76        except exceptions.CompileException:
77            html_error = exceptions.html_error_template().render()
78            assert (
79                "CompileException: Fragment &#39;if 2 == 2: /an "
80                "error&#39; is not a partial control statement "
81                "at line: 2 char: 1"
82            ).encode(
83                sys.getdefaultencoding(), "htmlentityreplace"
84            ) in html_error
85
86            assert (
87                "".encode(sys.getdefaultencoding(), "htmlentityreplace")
88                in html_error
89            )
90        else:
91            assert False, (
92                "This function should trigger a CompileException, "
93                "but didn't"
94            )
95
96    @requires_no_pygments_exceptions
97    def test_utf8_html_error_template_no_pygments(self):
98        """test the html_error_template with a Template containing UTF-8
99        chars"""
100
101        code = """# -*- coding: utf-8 -*-
102% if 2 == 2: /an error
103${'привет'}
104% endif
105"""
106        try:
107            template = Template(code)
108            template.render_unicode()
109        except exceptions.CompileException:
110            html_error = exceptions.html_error_template().render()
111            assert (
112                "CompileException: Fragment &#39;if 2 == 2: /an "
113                "error&#39; is not a partial control statement "
114                "at line: 2 char: 1"
115            ).encode(
116                sys.getdefaultencoding(), "htmlentityreplace"
117            ) in html_error
118            assert (
119                "${&#39;привет&#39;}".encode(
120                    sys.getdefaultencoding(), "htmlentityreplace"
121                )
122                in html_error
123            )
124        else:
125            assert False, (
126                "This function should trigger a CompileException, "
127                "but didn't"
128            )
129
130    def test_format_closures(self):
131        try:
132            exec("def foo():" "    raise RuntimeError('test')", locals())
133            foo()  # noqa
134        except:
135            html_error = exceptions.html_error_template().render()
136            assert "RuntimeError: test" in str(html_error)
137
138    def test_py_utf8_html_error_template(self):
139        try:
140            foo = "日本"  # noqa
141            raise RuntimeError("test")
142        except:
143            html_error = exceptions.html_error_template().render()
144            assert "RuntimeError: test" in html_error.decode("utf-8")
145            assert "foo = &quot;日本&quot;" in html_error.decode(
146                "utf-8"
147            ) or "foo = &#34;日本&#34;" in html_error.decode("utf-8")
148
149    def test_py_unicode_error_html_error_template(self):
150        try:
151            raise RuntimeError("日本")
152        except:
153            html_error = exceptions.html_error_template().render()
154            assert "RuntimeError: 日本".encode("ascii", "ignore") in html_error
155
156    @requires_pygments_14
157    def test_format_exceptions_pygments(self):
158        l = TemplateLookup(format_exceptions=True)
159
160        l.put_string(
161            "foo.html",
162            """
163<%inherit file="base.html"/>
164${foobar}
165        """,
166        )
167
168        l.put_string(
169            "base.html",
170            """
171        ${self.body()}
172        """,
173        )
174
175        assert (
176            '<table class="syntax-highlightedtable">'
177            in l.get_template("foo.html").render_unicode()
178        )
179
180    @requires_no_pygments_exceptions
181    def test_format_exceptions_no_pygments(self):
182        l = TemplateLookup(format_exceptions=True)
183
184        l.put_string(
185            "foo.html",
186            """
187<%inherit file="base.html"/>
188${foobar}
189        """,
190        )
191
192        l.put_string(
193            "base.html",
194            """
195        ${self.body()}
196        """,
197        )
198
199        assert '<div class="sourceline">${foobar}</div>' in result_lines(
200            l.get_template("foo.html").render_unicode()
201        )
202
203    @requires_pygments_14
204    def test_utf8_format_exceptions_pygments(self):
205        """test that htmlentityreplace formatting is applied to
206        exceptions reported with format_exceptions=True"""
207
208        l = TemplateLookup(format_exceptions=True)
209        l.put_string(
210            "foo.html", """# -*- coding: utf-8 -*-\n${'привет' + foobar}"""
211        )
212
213        assert "&#39;привет&#39;</span>" in l.get_template(
214            "foo.html"
215        ).render().decode("utf-8")
216
217    @requires_no_pygments_exceptions
218    def test_utf8_format_exceptions_no_pygments(self):
219        """test that htmlentityreplace formatting is applied to
220        exceptions reported with format_exceptions=True"""
221
222        l = TemplateLookup(format_exceptions=True)
223        l.put_string(
224            "foo.html", """# -*- coding: utf-8 -*-\n${'привет' + foobar}"""
225        )
226
227        assert (
228            '<div class="sourceline">${&#39;привет&#39; + foobar}</div>'
229            in result_lines(
230                l.get_template("foo.html").render().decode("utf-8")
231            )
232        )
233
234    def test_mod_no_encoding(self):
235        mod = __import__("test.foo.mod_no_encoding").foo.mod_no_encoding
236        try:
237            mod.run()
238        except:
239            t, v, tback = sys.exc_info()
240            exceptions.html_error_template().render_unicode(
241                error=v, traceback=tback
242            )
243
244    def test_custom_tback(self):
245        try:
246            raise RuntimeError("error 1")
247            foo("bar")  # noqa
248        except:
249            t, v, tback = sys.exc_info()
250
251        try:
252            raise RuntimeError("error 2")
253        except:
254            html_error = exceptions.html_error_template().render_unicode(
255                error=v, traceback=tback
256            )
257
258        # obfuscate the text so that this text
259        # isn't in the 'wrong' exception
260        assert (
261            "".join(reversed(");touq&rab;touq&(oof")) in html_error
262            or "".join(reversed(");43#&rab;43#&(oof")) in html_error
263        )
264
265    def test_tback_no_trace_from_py_file(self):
266        try:
267            t = self._file_template("runtimeerr.html")
268            t.render()
269        except:
270            t, v, tback = sys.exc_info()
271
272        # and don't even send what we have.
273        html_error = exceptions.html_error_template().render_unicode(
274            error=v, traceback=None
275        )
276
277        assert self.indicates_unbound_local_error(html_error, "y")
278
279    def test_tback_trace_from_py_file(self):
280        t = self._file_template("runtimeerr.html")
281        try:
282            t.render()
283            assert False
284        except:
285            html_error = exceptions.html_error_template().render_unicode()
286
287        assert self.indicates_unbound_local_error(html_error, "y")
288
289    def test_code_block_line_number(self):
290        l = TemplateLookup()
291        l.put_string(
292            "foo.html",
293            """
294<%
295msg = "Something went wrong."
296raise RuntimeError(msg)  # This is the line.
297%>
298            """,
299        )
300        t = l.get_template("foo.html")
301        try:
302            t.render()
303        except:
304            text_error = exceptions.text_error_template().render_unicode()
305            assert 'File "foo.html", line 4, in render_body' in text_error
306            assert "raise RuntimeError(msg)  # This is the line." in text_error
307        else:
308            assert False
309
310    def test_module_block_line_number(self):
311        l = TemplateLookup()
312        l.put_string(
313            "foo.html",
314            """
315<%!
316def foo():
317    msg = "Something went wrong."
318    raise RuntimeError(msg)  # This is the line.
319%>
320${foo()}
321            """,
322        )
323        t = l.get_template("foo.html")
324        try:
325            t.render()
326        except:
327            text_error = exceptions.text_error_template().render_unicode()
328            assert 'File "foo.html", line 7, in render_body' in text_error
329            assert 'File "foo.html", line 5, in foo' in text_error
330            assert "raise RuntimeError(msg)  # This is the line." in text_error
331        else:
332            assert False
333
334    def test_alternating_file_names(self):
335        l = TemplateLookup()
336        l.put_string(
337            "base.html",
338            """
339<%!
340def broken():
341    raise RuntimeError("Something went wrong.")
342%> body starts here
343<%block name="foo">
344    ${broken()}
345</%block>
346            """,
347        )
348        l.put_string(
349            "foo.html",
350            """
351<%inherit file="base.html"/>
352<%block name="foo">
353    ${parent.foo()}
354</%block>
355            """,
356        )
357        t = l.get_template("foo.html")
358        try:
359            t.render()
360        except:
361            text_error = exceptions.text_error_template().render_unicode()
362            assert (
363                """
364  File "base.html", line 5, in render_body
365    %> body starts here
366  File "foo.html", line 4, in render_foo
367    ${parent.foo()}
368  File "base.html", line 7, in render_foo
369    ${broken()}
370  File "base.html", line 4, in broken
371    raise RuntimeError("Something went wrong.")
372"""
373                in text_error
374            )
375        else:
376            assert False
377