inheritance.py 8.1 KB

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