test_debug.py 3.6 KB

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