test_inheritance.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. # -*- coding: utf-8 -*-
  2. import pytest
  3. from jinja2 import DictLoader
  4. from jinja2 import Environment
  5. from jinja2 import TemplateRuntimeError
  6. LAYOUTTEMPLATE = """\
  7. |{% block block1 %}block 1 from layout{% endblock %}
  8. |{% block block2 %}block 2 from layout{% endblock %}
  9. |{% block block3 %}
  10. {% block block4 %}nested block 4 from layout{% endblock %}
  11. {% endblock %}|"""
  12. LEVEL1TEMPLATE = """\
  13. {% extends "layout" %}
  14. {% block block1 %}block 1 from level1{% endblock %}"""
  15. LEVEL2TEMPLATE = """\
  16. {% extends "level1" %}
  17. {% block block2 %}{% block block5 %}nested block 5 from level2{%
  18. endblock %}{% endblock %}"""
  19. LEVEL3TEMPLATE = """\
  20. {% extends "level2" %}
  21. {% block block5 %}block 5 from level3{% endblock %}
  22. {% block block4 %}block 4 from level3{% endblock %}
  23. """
  24. LEVEL4TEMPLATE = """\
  25. {% extends "level3" %}
  26. {% block block3 %}block 3 from level4{% endblock %}
  27. """
  28. WORKINGTEMPLATE = """\
  29. {% extends "layout" %}
  30. {% block block1 %}
  31. {% if false %}
  32. {% block block2 %}
  33. this should workd
  34. {% endblock %}
  35. {% endif %}
  36. {% endblock %}
  37. """
  38. DOUBLEEXTENDS = """\
  39. {% extends "layout" %}
  40. {% extends "layout" %}
  41. {% block block1 %}
  42. {% if false %}
  43. {% block block2 %}
  44. this should workd
  45. {% endblock %}
  46. {% endif %}
  47. {% endblock %}
  48. """
  49. @pytest.fixture
  50. def env():
  51. return Environment(
  52. loader=DictLoader(
  53. {
  54. "layout": LAYOUTTEMPLATE,
  55. "level1": LEVEL1TEMPLATE,
  56. "level2": LEVEL2TEMPLATE,
  57. "level3": LEVEL3TEMPLATE,
  58. "level4": LEVEL4TEMPLATE,
  59. "working": WORKINGTEMPLATE,
  60. "doublee": DOUBLEEXTENDS,
  61. }
  62. ),
  63. trim_blocks=True,
  64. )
  65. class TestInheritance(object):
  66. def test_layout(self, env):
  67. tmpl = env.get_template("layout")
  68. assert tmpl.render() == (
  69. "|block 1 from layout|block 2 from layout|nested block 4 from layout|"
  70. )
  71. def test_level1(self, env):
  72. tmpl = env.get_template("level1")
  73. assert tmpl.render() == (
  74. "|block 1 from level1|block 2 from layout|nested block 4 from layout|"
  75. )
  76. def test_level2(self, env):
  77. tmpl = env.get_template("level2")
  78. assert tmpl.render() == (
  79. "|block 1 from level1|nested block 5 from "
  80. "level2|nested block 4 from layout|"
  81. )
  82. def test_level3(self, env):
  83. tmpl = env.get_template("level3")
  84. assert tmpl.render() == (
  85. "|block 1 from level1|block 5 from level3|block 4 from level3|"
  86. )
  87. def test_level4(self, env):
  88. tmpl = env.get_template("level4")
  89. assert tmpl.render() == (
  90. "|block 1 from level1|block 5 from level3|block 3 from level4|"
  91. )
  92. def test_super(self, env):
  93. env = Environment(
  94. loader=DictLoader(
  95. {
  96. "a": "{% block intro %}INTRO{% endblock %}|"
  97. "BEFORE|{% block data %}INNER{% endblock %}|AFTER",
  98. "b": '{% extends "a" %}{% block data %}({{ '
  99. "super() }}){% endblock %}",
  100. "c": '{% extends "b" %}{% block intro %}--{{ '
  101. "super() }}--{% endblock %}\n{% block data "
  102. "%}[{{ super() }}]{% endblock %}",
  103. }
  104. )
  105. )
  106. tmpl = env.get_template("c")
  107. assert tmpl.render() == "--INTRO--|BEFORE|[(INNER)]|AFTER"
  108. def test_working(self, env):
  109. env.get_template("working")
  110. def test_reuse_blocks(self, env):
  111. tmpl = env.from_string(
  112. "{{ self.foo() }}|{% block foo %}42{% endblock %}|{{ self.foo() }}"
  113. )
  114. assert tmpl.render() == "42|42|42"
  115. def test_preserve_blocks(self, env):
  116. env = Environment(
  117. loader=DictLoader(
  118. {
  119. "a": "{% if false %}{% block x %}A{% endblock %}"
  120. "{% endif %}{{ self.x() }}",
  121. "b": '{% extends "a" %}{% block x %}B{{ super() }}{% endblock %}',
  122. }
  123. )
  124. )
  125. tmpl = env.get_template("b")
  126. assert tmpl.render() == "BA"
  127. def test_dynamic_inheritance(self, env):
  128. env = Environment(
  129. loader=DictLoader(
  130. {
  131. "master1": "MASTER1{% block x %}{% endblock %}",
  132. "master2": "MASTER2{% block x %}{% endblock %}",
  133. "child": "{% extends master %}{% block x %}CHILD{% endblock %}",
  134. }
  135. )
  136. )
  137. tmpl = env.get_template("child")
  138. for m in range(1, 3):
  139. assert tmpl.render(master="master%d" % m) == "MASTER%dCHILD" % m
  140. def test_multi_inheritance(self, env):
  141. env = Environment(
  142. loader=DictLoader(
  143. {
  144. "master1": "MASTER1{% block x %}{% endblock %}",
  145. "master2": "MASTER2{% block x %}{% endblock %}",
  146. "child": """{% if master %}{% extends master %}{% else %}{% extends
  147. 'master1' %}{% endif %}{% block x %}CHILD{% endblock %}""",
  148. }
  149. )
  150. )
  151. tmpl = env.get_template("child")
  152. assert tmpl.render(master="master2") == "MASTER2CHILD"
  153. assert tmpl.render(master="master1") == "MASTER1CHILD"
  154. assert tmpl.render() == "MASTER1CHILD"
  155. def test_scoped_block(self, env):
  156. env = Environment(
  157. loader=DictLoader(
  158. {
  159. "master.html": "{% for item in seq %}[{% block item scoped %}"
  160. "{% endblock %}]{% endfor %}"
  161. }
  162. )
  163. )
  164. t = env.from_string(
  165. "{% extends 'master.html' %}{% block item %}{{ item }}{% endblock %}"
  166. )
  167. assert t.render(seq=list(range(5))) == "[0][1][2][3][4]"
  168. def test_super_in_scoped_block(self, env):
  169. env = Environment(
  170. loader=DictLoader(
  171. {
  172. "master.html": "{% for item in seq %}[{% block item scoped %}"
  173. "{{ item }}{% endblock %}]{% endfor %}"
  174. }
  175. )
  176. )
  177. t = env.from_string(
  178. '{% extends "master.html" %}{% block item %}'
  179. "{{ super() }}|{{ item * 2 }}{% endblock %}"
  180. )
  181. assert t.render(seq=list(range(5))) == "[0|0][1|2][2|4][3|6][4|8]"
  182. def test_scoped_block_after_inheritance(self, env):
  183. env = Environment(
  184. loader=DictLoader(
  185. {
  186. "layout.html": """
  187. {% block useless %}{% endblock %}
  188. """,
  189. "index.html": """
  190. {%- extends 'layout.html' %}
  191. {% from 'helpers.html' import foo with context %}
  192. {% block useless %}
  193. {% for x in [1, 2, 3] %}
  194. {% block testing scoped %}
  195. {{ foo(x) }}
  196. {% endblock %}
  197. {% endfor %}
  198. {% endblock %}
  199. """,
  200. "helpers.html": """
  201. {% macro foo(x) %}{{ the_foo + x }}{% endmacro %}
  202. """,
  203. }
  204. )
  205. )
  206. rv = env.get_template("index.html").render(the_foo=42).split()
  207. assert rv == ["43", "44", "45"]
  208. class TestBugFix(object):
  209. def test_fixed_macro_scoping_bug(self, env):
  210. assert (
  211. Environment(
  212. loader=DictLoader(
  213. {
  214. "test.html": """\
  215. {% extends 'details.html' %}
  216. {% macro my_macro() %}
  217. my_macro
  218. {% endmacro %}
  219. {% block inner_box %}
  220. {{ my_macro() }}
  221. {% endblock %}
  222. """,
  223. "details.html": """\
  224. {% extends 'standard.html' %}
  225. {% macro my_macro() %}
  226. my_macro
  227. {% endmacro %}
  228. {% block content %}
  229. {% block outer_box %}
  230. outer_box
  231. {% block inner_box %}
  232. inner_box
  233. {% endblock %}
  234. {% endblock %}
  235. {% endblock %}
  236. """,
  237. "standard.html": """
  238. {% block content %} {% endblock %}
  239. """,
  240. }
  241. )
  242. )
  243. .get_template("test.html")
  244. .render()
  245. .split()
  246. == [u"outer_box", u"my_macro"]
  247. )
  248. def test_double_extends(self, env):
  249. """Ensures that a template with more than 1 {% extends ... %} usage
  250. raises a ``TemplateError``.
  251. """
  252. with pytest.raises(TemplateRuntimeError, match="extended multiple times"):
  253. env.get_template("doublee").render()