api.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. # -*- coding: utf-8 -*-
  2. """
  3. jinja2.testsuite.api
  4. ~~~~~~~~~~~~~~~~~~~~
  5. Tests the public API and related stuff.
  6. :copyright: (c) 2010 by the Jinja Team.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. import unittest
  10. import os
  11. import tempfile
  12. import shutil
  13. from jinja2.testsuite import JinjaTestCase
  14. from jinja2._compat import next
  15. from jinja2 import Environment, Undefined, DebugUndefined, \
  16. StrictUndefined, UndefinedError, meta, \
  17. is_undefined, Template, DictLoader
  18. from jinja2.utils import Cycler
  19. env = Environment()
  20. class ExtendedAPITestCase(JinjaTestCase):
  21. def test_item_and_attribute(self):
  22. from jinja2.sandbox import SandboxedEnvironment
  23. for env in Environment(), SandboxedEnvironment():
  24. # the |list is necessary for python3
  25. tmpl = env.from_string('{{ foo.items()|list }}')
  26. assert tmpl.render(foo={'items': 42}) == "[('items', 42)]"
  27. tmpl = env.from_string('{{ foo|attr("items")()|list }}')
  28. assert tmpl.render(foo={'items': 42}) == "[('items', 42)]"
  29. tmpl = env.from_string('{{ foo["items"] }}')
  30. assert tmpl.render(foo={'items': 42}) == '42'
  31. def test_finalizer(self):
  32. def finalize_none_empty(value):
  33. if value is None:
  34. value = u''
  35. return value
  36. env = Environment(finalize=finalize_none_empty)
  37. tmpl = env.from_string('{% for item in seq %}|{{ item }}{% endfor %}')
  38. assert tmpl.render(seq=(None, 1, "foo")) == '||1|foo'
  39. tmpl = env.from_string('<{{ none }}>')
  40. assert tmpl.render() == '<>'
  41. def test_cycler(self):
  42. items = 1, 2, 3
  43. c = Cycler(*items)
  44. for item in items + items:
  45. assert c.current == item
  46. assert next(c) == item
  47. next(c)
  48. assert c.current == 2
  49. c.reset()
  50. assert c.current == 1
  51. def test_expressions(self):
  52. expr = env.compile_expression("foo")
  53. assert expr() is None
  54. assert expr(foo=42) == 42
  55. expr2 = env.compile_expression("foo", undefined_to_none=False)
  56. assert is_undefined(expr2())
  57. expr = env.compile_expression("42 + foo")
  58. assert expr(foo=42) == 84
  59. def test_template_passthrough(self):
  60. t = Template('Content')
  61. assert env.get_template(t) is t
  62. assert env.select_template([t]) is t
  63. assert env.get_or_select_template([t]) is t
  64. assert env.get_or_select_template(t) is t
  65. def test_autoescape_autoselect(self):
  66. def select_autoescape(name):
  67. if name is None or '.' not in name:
  68. return False
  69. return name.endswith('.html')
  70. env = Environment(autoescape=select_autoescape,
  71. loader=DictLoader({
  72. 'test.txt': '{{ foo }}',
  73. 'test.html': '{{ foo }}'
  74. }))
  75. t = env.get_template('test.txt')
  76. assert t.render(foo='<foo>') == '<foo>'
  77. t = env.get_template('test.html')
  78. assert t.render(foo='<foo>') == '&lt;foo&gt;'
  79. t = env.from_string('{{ foo }}')
  80. assert t.render(foo='<foo>') == '<foo>'
  81. class MetaTestCase(JinjaTestCase):
  82. def test_find_undeclared_variables(self):
  83. ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
  84. x = meta.find_undeclared_variables(ast)
  85. assert x == set(['bar'])
  86. ast = env.parse('{% set foo = 42 %}{{ bar + foo }}'
  87. '{% macro meh(x) %}{{ x }}{% endmacro %}'
  88. '{% for item in seq %}{{ muh(item) + meh(seq) }}{% endfor %}')
  89. x = meta.find_undeclared_variables(ast)
  90. assert x == set(['bar', 'seq', 'muh'])
  91. def test_find_refererenced_templates(self):
  92. ast = env.parse('{% extends "layout.html" %}{% include helper %}')
  93. i = meta.find_referenced_templates(ast)
  94. assert next(i) == 'layout.html'
  95. assert next(i) is None
  96. assert list(i) == []
  97. ast = env.parse('{% extends "layout.html" %}'
  98. '{% from "test.html" import a, b as c %}'
  99. '{% import "meh.html" as meh %}'
  100. '{% include "muh.html" %}')
  101. i = meta.find_referenced_templates(ast)
  102. assert list(i) == ['layout.html', 'test.html', 'meh.html', 'muh.html']
  103. def test_find_included_templates(self):
  104. ast = env.parse('{% include ["foo.html", "bar.html"] %}')
  105. i = meta.find_referenced_templates(ast)
  106. assert list(i) == ['foo.html', 'bar.html']
  107. ast = env.parse('{% include ("foo.html", "bar.html") %}')
  108. i = meta.find_referenced_templates(ast)
  109. assert list(i) == ['foo.html', 'bar.html']
  110. ast = env.parse('{% include ["foo.html", "bar.html", foo] %}')
  111. i = meta.find_referenced_templates(ast)
  112. assert list(i) == ['foo.html', 'bar.html', None]
  113. ast = env.parse('{% include ("foo.html", "bar.html", foo) %}')
  114. i = meta.find_referenced_templates(ast)
  115. assert list(i) == ['foo.html', 'bar.html', None]
  116. class StreamingTestCase(JinjaTestCase):
  117. def test_basic_streaming(self):
  118. tmpl = env.from_string("<ul>{% for item in seq %}<li>{{ loop.index "
  119. "}} - {{ item }}</li>{%- endfor %}</ul>")
  120. stream = tmpl.stream(seq=list(range(4)))
  121. self.assert_equal(next(stream), '<ul>')
  122. self.assert_equal(next(stream), '<li>1 - 0</li>')
  123. self.assert_equal(next(stream), '<li>2 - 1</li>')
  124. self.assert_equal(next(stream), '<li>3 - 2</li>')
  125. self.assert_equal(next(stream), '<li>4 - 3</li>')
  126. self.assert_equal(next(stream), '</ul>')
  127. def test_buffered_streaming(self):
  128. tmpl = env.from_string("<ul>{% for item in seq %}<li>{{ loop.index "
  129. "}} - {{ item }}</li>{%- endfor %}</ul>")
  130. stream = tmpl.stream(seq=list(range(4)))
  131. stream.enable_buffering(size=3)
  132. self.assert_equal(next(stream), u'<ul><li>1 - 0</li><li>2 - 1</li>')
  133. self.assert_equal(next(stream), u'<li>3 - 2</li><li>4 - 3</li></ul>')
  134. def test_streaming_behavior(self):
  135. tmpl = env.from_string("")
  136. stream = tmpl.stream()
  137. assert not stream.buffered
  138. stream.enable_buffering(20)
  139. assert stream.buffered
  140. stream.disable_buffering()
  141. assert not stream.buffered
  142. def test_dump_stream(self):
  143. tmp = tempfile.mkdtemp()
  144. try:
  145. tmpl = env.from_string(u"\u2713")
  146. stream = tmpl.stream()
  147. stream.dump(os.path.join(tmp, 'dump.txt'), 'utf-8')
  148. with open(os.path.join(tmp, 'dump.txt'), 'rb') as f:
  149. self.assertEqual(f.read(), b'\xe2\x9c\x93')
  150. finally:
  151. shutil.rmtree(tmp)
  152. class UndefinedTestCase(JinjaTestCase):
  153. def test_stopiteration_is_undefined(self):
  154. def test():
  155. raise StopIteration()
  156. t = Template('A{{ test() }}B')
  157. assert t.render(test=test) == 'AB'
  158. t = Template('A{{ test().missingattribute }}B')
  159. self.assert_raises(UndefinedError, t.render, test=test)
  160. def test_undefined_and_special_attributes(self):
  161. try:
  162. Undefined('Foo').__dict__
  163. except AttributeError:
  164. pass
  165. else:
  166. assert False, "Expected actual attribute error"
  167. def test_default_undefined(self):
  168. env = Environment(undefined=Undefined)
  169. self.assert_equal(env.from_string('{{ missing }}').render(), u'')
  170. self.assert_raises(UndefinedError,
  171. env.from_string('{{ missing.attribute }}').render)
  172. self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]')
  173. self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True')
  174. self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42), '')
  175. self.assert_equal(env.from_string('{{ not missing }}').render(), 'True')
  176. def test_debug_undefined(self):
  177. env = Environment(undefined=DebugUndefined)
  178. self.assert_equal(env.from_string('{{ missing }}').render(), '{{ missing }}')
  179. self.assert_raises(UndefinedError,
  180. env.from_string('{{ missing.attribute }}').render)
  181. self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]')
  182. self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True')
  183. self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42),
  184. u"{{ no such element: int object['missing'] }}")
  185. self.assert_equal(env.from_string('{{ not missing }}').render(), 'True')
  186. def test_strict_undefined(self):
  187. env = Environment(undefined=StrictUndefined)
  188. self.assert_raises(UndefinedError, env.from_string('{{ missing }}').render)
  189. self.assert_raises(UndefinedError, env.from_string('{{ missing.attribute }}').render)
  190. self.assert_raises(UndefinedError, env.from_string('{{ missing|list }}').render)
  191. self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True')
  192. self.assert_raises(UndefinedError, env.from_string('{{ foo.missing }}').render, foo=42)
  193. self.assert_raises(UndefinedError, env.from_string('{{ not missing }}').render)
  194. self.assert_equal(env.from_string('{{ missing|default("default", true) }}').render(), 'default')
  195. def test_indexing_gives_undefined(self):
  196. t = Template("{{ var[42].foo }}")
  197. self.assert_raises(UndefinedError, t.render, var=0)
  198. def test_none_gives_proper_error(self):
  199. try:
  200. Environment().getattr(None, 'split')()
  201. except UndefinedError as e:
  202. assert e.message == "'None' has no attribute 'split'"
  203. else:
  204. assert False, 'expected exception'
  205. def test_object_repr(self):
  206. try:
  207. Undefined(obj=42, name='upper')()
  208. except UndefinedError as e:
  209. assert e.message == "'int object' has no attribute 'upper'"
  210. else:
  211. assert False, 'expected exception'
  212. def suite():
  213. suite = unittest.TestSuite()
  214. suite.addTest(unittest.makeSuite(ExtendedAPITestCase))
  215. suite.addTest(unittest.makeSuite(MetaTestCase))
  216. suite.addTest(unittest.makeSuite(StreamingTestCase))
  217. suite.addTest(unittest.makeSuite(UndefinedTestCase))
  218. return suite