1import pickle 2import re 3from traceback import format_exception 4 5import pytest 6 7from jinja2 import ChoiceLoader 8from jinja2 import DictLoader 9from jinja2 import Environment 10from jinja2 import TemplateSyntaxError 11 12 13@pytest.fixture 14def fs_env(filesystem_loader): 15 """returns a new environment.""" 16 return Environment(loader=filesystem_loader) 17 18 19class TestDebug: 20 def assert_traceback_matches(self, callback, expected_tb): 21 with pytest.raises(Exception) as exc_info: 22 callback() 23 24 tb = format_exception(exc_info.type, exc_info.value, exc_info.tb) 25 m = re.search(expected_tb.strip(), "".join(tb)) 26 assert ( 27 m is not None 28 ), "Traceback did not match:\n\n{''.join(tb)}\nexpected:\n{expected_tb}" 29 30 def test_runtime_error(self, fs_env): 31 def test(): 32 tmpl.render(fail=lambda: 1 / 0) 33 34 tmpl = fs_env.get_template("broken.html") 35 self.assert_traceback_matches( 36 test, 37 r""" 38 File ".*?broken.html", line 2, in (top-level template code|<module>) 39 \{\{ fail\(\) \}\} 40 File ".*debug?.pyc?", line \d+, in <lambda> 41 tmpl\.render\(fail=lambda: 1 / 0\) 42ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero 43""", 44 ) 45 46 def test_syntax_error(self, fs_env): 47 # The trailing .*? is for PyPy 2 and 3, which don't seem to 48 # clear the exception's original traceback, leaving the syntax 49 # error in the middle of other compiler frames. 50 self.assert_traceback_matches( 51 lambda: fs_env.get_template("syntaxerror.html"), 52 """(?sm) 53 File ".*?syntaxerror.html", line 4, in (template|<module>) 54 \\{% endif %\\}.*? 55(jinja2\\.exceptions\\.)?TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja \ 56was looking for the following tags: 'endfor' or 'else'. The innermost block that needs \ 57to be closed is 'for'. 58 """, 59 ) 60 61 def test_regular_syntax_error(self, fs_env): 62 def test(): 63 raise TemplateSyntaxError("wtf", 42) 64 65 self.assert_traceback_matches( 66 test, 67 r""" 68 File ".*debug.pyc?", line \d+, in test 69 raise TemplateSyntaxError\("wtf", 42\) 70(jinja2\.exceptions\.)?TemplateSyntaxError: wtf 71 line 42""", 72 ) 73 74 def test_pickleable_syntax_error(self, fs_env): 75 original = TemplateSyntaxError("bad template", 42, "test", "test.txt") 76 unpickled = pickle.loads(pickle.dumps(original)) 77 assert str(original) == str(unpickled) 78 assert original.name == unpickled.name 79 80 def test_include_syntax_error_source(self, filesystem_loader): 81 e = Environment( 82 loader=ChoiceLoader( 83 [ 84 filesystem_loader, 85 DictLoader({"inc": "a\n{% include 'syntaxerror.html' %}\nb"}), 86 ] 87 ) 88 ) 89 t = e.get_template("inc") 90 91 with pytest.raises(TemplateSyntaxError) as exc_info: 92 t.render() 93 94 assert exc_info.value.source is not None 95 96 def test_local_extraction(self): 97 from jinja2.debug import get_template_locals 98 from jinja2.runtime import missing 99 100 locals = get_template_locals( 101 { 102 "l_0_foo": 42, 103 "l_1_foo": 23, 104 "l_2_foo": 13, 105 "l_0_bar": 99, 106 "l_1_bar": missing, 107 "l_0_baz": missing, 108 } 109 ) 110 assert locals == {"foo": 13, "bar": 99} 111 112 def test_get_corresponding_lineno_traceback(self, fs_env): 113 tmpl = fs_env.get_template("test.html") 114 assert tmpl.get_corresponding_lineno(1) == 1 115