1. from django.template.base import TemplateSyntaxError
    
  2. from django.template.context import Context
    
  3. from django.template.loader_tags import BlockContext, BlockNode
    
  4. from django.test import SimpleTestCase
    
  5. 
    
  6. from ..utils import SilentAttrClass, SilentGetItemClass, SomeClass, setup
    
  7. 
    
  8. basic_templates = {
    
  9.     "basic-syntax01": "something cool",
    
  10.     "basic-syntax02": "{{ headline }}",
    
  11.     "basic-syntax03": "{{ first }} --- {{ second }}",
    
  12. }
    
  13. 
    
  14. 
    
  15. class BasicSyntaxTests(SimpleTestCase):
    
  16.     @setup(basic_templates)
    
  17.     def test_basic_syntax01(self):
    
  18.         """
    
  19.         Plain text should go through the template parser untouched.
    
  20.         """
    
  21.         output = self.engine.render_to_string("basic-syntax01")
    
  22.         self.assertEqual(output, "something cool")
    
  23. 
    
  24.     @setup(basic_templates)
    
  25.     def test_basic_syntax02(self):
    
  26.         """
    
  27.         Variables should be replaced with their value in the current
    
  28.         context
    
  29.         """
    
  30.         output = self.engine.render_to_string("basic-syntax02", {"headline": "Success"})
    
  31.         self.assertEqual(output, "Success")
    
  32. 
    
  33.     @setup(basic_templates)
    
  34.     def test_basic_syntax03(self):
    
  35.         """
    
  36.         More than one replacement variable is allowed in a template
    
  37.         """
    
  38.         output = self.engine.render_to_string(
    
  39.             "basic-syntax03", {"first": 1, "second": 2}
    
  40.         )
    
  41.         self.assertEqual(output, "1 --- 2")
    
  42. 
    
  43.     @setup({"basic-syntax04": "as{{ missing }}df"})
    
  44.     def test_basic_syntax04(self):
    
  45.         """
    
  46.         Fail silently when a variable is not found in the current context
    
  47.         """
    
  48.         output = self.engine.render_to_string("basic-syntax04")
    
  49.         if self.engine.string_if_invalid:
    
  50.             self.assertEqual(output, "asINVALIDdf")
    
  51.         else:
    
  52.             self.assertEqual(output, "asdf")
    
  53. 
    
  54.     @setup({"basic-syntax06": "{{ multi word variable }}"})
    
  55.     def test_basic_syntax06(self):
    
  56.         """
    
  57.         A variable may not contain more than one word
    
  58.         """
    
  59.         with self.assertRaises(TemplateSyntaxError):
    
  60.             self.engine.get_template("basic-syntax06")
    
  61. 
    
  62.     @setup({"basic-syntax07": "{{ }}"})
    
  63.     def test_basic_syntax07(self):
    
  64.         """
    
  65.         Raise TemplateSyntaxError for empty variable tags.
    
  66.         """
    
  67.         with self.assertRaisesMessage(
    
  68.             TemplateSyntaxError, "Empty variable tag on line 1"
    
  69.         ):
    
  70.             self.engine.get_template("basic-syntax07")
    
  71. 
    
  72.     @setup({"basic-syntax08": "{{        }}"})
    
  73.     def test_basic_syntax08(self):
    
  74.         """
    
  75.         Raise TemplateSyntaxError for empty variable tags.
    
  76.         """
    
  77.         with self.assertRaisesMessage(
    
  78.             TemplateSyntaxError, "Empty variable tag on line 1"
    
  79.         ):
    
  80.             self.engine.get_template("basic-syntax08")
    
  81. 
    
  82.     @setup({"basic-syntax09": "{{ var.method }}"})
    
  83.     def test_basic_syntax09(self):
    
  84.         """
    
  85.         Attribute syntax allows a template to call an object's attribute
    
  86.         """
    
  87.         output = self.engine.render_to_string("basic-syntax09", {"var": SomeClass()})
    
  88.         self.assertEqual(output, "SomeClass.method")
    
  89. 
    
  90.     @setup({"basic-syntax10": "{{ var.otherclass.method }}"})
    
  91.     def test_basic_syntax10(self):
    
  92.         """
    
  93.         Multiple levels of attribute access are allowed.
    
  94.         """
    
  95.         output = self.engine.render_to_string("basic-syntax10", {"var": SomeClass()})
    
  96.         self.assertEqual(output, "OtherClass.method")
    
  97. 
    
  98.     @setup({"basic-syntax11": "{{ var.blech }}"})
    
  99.     def test_basic_syntax11(self):
    
  100.         """
    
  101.         Fail silently when a variable's attribute isn't found.
    
  102.         """
    
  103.         output = self.engine.render_to_string("basic-syntax11", {"var": SomeClass()})
    
  104. 
    
  105.         if self.engine.string_if_invalid:
    
  106.             self.assertEqual(output, "INVALID")
    
  107.         else:
    
  108.             self.assertEqual(output, "")
    
  109. 
    
  110.     @setup({"basic-syntax12": "{{ var.__dict__ }}"})
    
  111.     def test_basic_syntax12(self):
    
  112.         """
    
  113.         Raise TemplateSyntaxError when trying to access a variable
    
  114.         beginning with an underscore.
    
  115.         """
    
  116.         with self.assertRaises(TemplateSyntaxError):
    
  117.             self.engine.get_template("basic-syntax12")
    
  118. 
    
  119.     # Raise TemplateSyntaxError when trying to access a variable
    
  120.     # containing an illegal character.
    
  121.     @setup({"basic-syntax13": "{{ va>r }}"})
    
  122.     def test_basic_syntax13(self):
    
  123.         with self.assertRaises(TemplateSyntaxError):
    
  124.             self.engine.get_template("basic-syntax13")
    
  125. 
    
  126.     @setup({"basic-syntax14": "{{ (var.r) }}"})
    
  127.     def test_basic_syntax14(self):
    
  128.         with self.assertRaises(TemplateSyntaxError):
    
  129.             self.engine.get_template("basic-syntax14")
    
  130. 
    
  131.     @setup({"basic-syntax15": "{{ sp%am }}"})
    
  132.     def test_basic_syntax15(self):
    
  133.         with self.assertRaises(TemplateSyntaxError):
    
  134.             self.engine.get_template("basic-syntax15")
    
  135. 
    
  136.     @setup({"basic-syntax16": "{{ eggs! }}"})
    
  137.     def test_basic_syntax16(self):
    
  138.         with self.assertRaises(TemplateSyntaxError):
    
  139.             self.engine.get_template("basic-syntax16")
    
  140. 
    
  141.     @setup({"basic-syntax17": "{{ moo? }}"})
    
  142.     def test_basic_syntax17(self):
    
  143.         with self.assertRaises(TemplateSyntaxError):
    
  144.             self.engine.get_template("basic-syntax17")
    
  145. 
    
  146.     @setup({"basic-syntax18": "{{ foo.bar }}"})
    
  147.     def test_basic_syntax18(self):
    
  148.         """
    
  149.         Attribute syntax allows a template to call a dictionary key's
    
  150.         value.
    
  151.         """
    
  152.         output = self.engine.render_to_string("basic-syntax18", {"foo": {"bar": "baz"}})
    
  153.         self.assertEqual(output, "baz")
    
  154. 
    
  155.     @setup({"basic-syntax19": "{{ foo.spam }}"})
    
  156.     def test_basic_syntax19(self):
    
  157.         """
    
  158.         Fail silently when a variable's dictionary key isn't found.
    
  159.         """
    
  160.         output = self.engine.render_to_string("basic-syntax19", {"foo": {"bar": "baz"}})
    
  161. 
    
  162.         if self.engine.string_if_invalid:
    
  163.             self.assertEqual(output, "INVALID")
    
  164.         else:
    
  165.             self.assertEqual(output, "")
    
  166. 
    
  167.     @setup({"basic-syntax20": "{{ var.method2 }}"})
    
  168.     def test_basic_syntax20(self):
    
  169.         """
    
  170.         Fail silently when accessing a non-simple method
    
  171.         """
    
  172.         output = self.engine.render_to_string("basic-syntax20", {"var": SomeClass()})
    
  173. 
    
  174.         if self.engine.string_if_invalid:
    
  175.             self.assertEqual(output, "INVALID")
    
  176.         else:
    
  177.             self.assertEqual(output, "")
    
  178. 
    
  179.     @setup({"basic-syntax20b": "{{ var.method5 }}"})
    
  180.     def test_basic_syntax20b(self):
    
  181.         """
    
  182.         Don't silence a TypeError if it was raised inside a callable.
    
  183.         """
    
  184.         template = self.engine.get_template("basic-syntax20b")
    
  185. 
    
  186.         with self.assertRaises(TypeError):
    
  187.             template.render(Context({"var": SomeClass()}))
    
  188. 
    
  189.     # Don't get confused when parsing something that is almost, but not
    
  190.     # quite, a template tag.
    
  191.     @setup({"basic-syntax21": "a {{ moo %} b"})
    
  192.     def test_basic_syntax21(self):
    
  193.         output = self.engine.render_to_string("basic-syntax21")
    
  194.         self.assertEqual(output, "a {{ moo %} b")
    
  195. 
    
  196.     @setup({"basic-syntax22": "{{ moo #}"})
    
  197.     def test_basic_syntax22(self):
    
  198.         output = self.engine.render_to_string("basic-syntax22")
    
  199.         self.assertEqual(output, "{{ moo #}")
    
  200. 
    
  201.     @setup({"basic-syntax23": "{{ moo #} {{ cow }}"})
    
  202.     def test_basic_syntax23(self):
    
  203.         """
    
  204.         Treat "moo #} {{ cow" as the variable. Not ideal, but costly to work
    
  205.         around, so this triggers an error.
    
  206.         """
    
  207.         with self.assertRaises(TemplateSyntaxError):
    
  208.             self.engine.get_template("basic-syntax23")
    
  209. 
    
  210.     @setup({"basic-syntax24": "{{ moo\n }}"})
    
  211.     def test_basic_syntax24(self):
    
  212.         """
    
  213.         Embedded newlines make it not-a-tag.
    
  214.         """
    
  215.         output = self.engine.render_to_string("basic-syntax24")
    
  216.         self.assertEqual(output, "{{ moo\n }}")
    
  217. 
    
  218.     # Literal strings are permitted inside variables, mostly for i18n
    
  219.     # purposes.
    
  220.     @setup({"basic-syntax25": '{{ "fred" }}'})
    
  221.     def test_basic_syntax25(self):
    
  222.         output = self.engine.render_to_string("basic-syntax25")
    
  223.         self.assertEqual(output, "fred")
    
  224. 
    
  225.     @setup({"basic-syntax26": r'{{ "\"fred\"" }}'})
    
  226.     def test_basic_syntax26(self):
    
  227.         output = self.engine.render_to_string("basic-syntax26")
    
  228.         self.assertEqual(output, '"fred"')
    
  229. 
    
  230.     @setup({"basic-syntax27": r'{{ _("\"fred\"") }}'})
    
  231.     def test_basic_syntax27(self):
    
  232.         output = self.engine.render_to_string("basic-syntax27")
    
  233.         self.assertEqual(output, '"fred"')
    
  234. 
    
  235.     # #12554 -- Make sure a silent_variable_failure Exception is
    
  236.     # suppressed on dictionary and attribute lookup.
    
  237.     @setup({"basic-syntax28": "{{ a.b }}"})
    
  238.     def test_basic_syntax28(self):
    
  239.         output = self.engine.render_to_string(
    
  240.             "basic-syntax28", {"a": SilentGetItemClass()}
    
  241.         )
    
  242.         if self.engine.string_if_invalid:
    
  243.             self.assertEqual(output, "INVALID")
    
  244.         else:
    
  245.             self.assertEqual(output, "")
    
  246. 
    
  247.     @setup({"basic-syntax29": "{{ a.b }}"})
    
  248.     def test_basic_syntax29(self):
    
  249.         output = self.engine.render_to_string(
    
  250.             "basic-syntax29", {"a": SilentAttrClass()}
    
  251.         )
    
  252.         if self.engine.string_if_invalid:
    
  253.             self.assertEqual(output, "INVALID")
    
  254.         else:
    
  255.             self.assertEqual(output, "")
    
  256. 
    
  257.     # Something that starts like a number but has an extra lookup works
    
  258.     # as a lookup.
    
  259.     @setup({"basic-syntax30": "{{ 1.2.3 }}"})
    
  260.     def test_basic_syntax30(self):
    
  261.         output = self.engine.render_to_string(
    
  262.             "basic-syntax30", {"1": {"2": {"3": "d"}}}
    
  263.         )
    
  264.         self.assertEqual(output, "d")
    
  265. 
    
  266.     @setup({"basic-syntax31": "{{ 1.2.3 }}"})
    
  267.     def test_basic_syntax31(self):
    
  268.         output = self.engine.render_to_string(
    
  269.             "basic-syntax31",
    
  270.             {"1": {"2": ("a", "b", "c", "d")}},
    
  271.         )
    
  272.         self.assertEqual(output, "d")
    
  273. 
    
  274.     @setup({"basic-syntax32": "{{ 1.2.3 }}"})
    
  275.     def test_basic_syntax32(self):
    
  276.         output = self.engine.render_to_string(
    
  277.             "basic-syntax32",
    
  278.             {"1": (("x", "x", "x", "x"), ("y", "y", "y", "y"), ("a", "b", "c", "d"))},
    
  279.         )
    
  280.         self.assertEqual(output, "d")
    
  281. 
    
  282.     @setup({"basic-syntax33": "{{ 1.2.3 }}"})
    
  283.     def test_basic_syntax33(self):
    
  284.         output = self.engine.render_to_string(
    
  285.             "basic-syntax33",
    
  286.             {"1": ("xxxx", "yyyy", "abcd")},
    
  287.         )
    
  288.         self.assertEqual(output, "d")
    
  289. 
    
  290.     @setup({"basic-syntax34": "{{ 1.2.3 }}"})
    
  291.     def test_basic_syntax34(self):
    
  292.         output = self.engine.render_to_string(
    
  293.             "basic-syntax34", {"1": ({"x": "x"}, {"y": "y"}, {"z": "z", "3": "d"})}
    
  294.         )
    
  295.         self.assertEqual(output, "d")
    
  296. 
    
  297.     # Numbers are numbers even if their digits are in the context.
    
  298.     @setup({"basic-syntax35": "{{ 1 }}"})
    
  299.     def test_basic_syntax35(self):
    
  300.         output = self.engine.render_to_string("basic-syntax35", {"1": "abc"})
    
  301.         self.assertEqual(output, "1")
    
  302. 
    
  303.     @setup({"basic-syntax36": "{{ 1.2 }}"})
    
  304.     def test_basic_syntax36(self):
    
  305.         output = self.engine.render_to_string("basic-syntax36", {"1": "abc"})
    
  306.         self.assertEqual(output, "1.2")
    
  307. 
    
  308.     @setup({"basic-syntax37": "{{ callable }}"})
    
  309.     def test_basic_syntax37(self):
    
  310.         """
    
  311.         Call methods in the top level of the context.
    
  312.         """
    
  313.         output = self.engine.render_to_string(
    
  314.             "basic-syntax37", {"callable": lambda: "foo bar"}
    
  315.         )
    
  316.         self.assertEqual(output, "foo bar")
    
  317. 
    
  318.     @setup({"basic-syntax38": "{{ var.callable }}"})
    
  319.     def test_basic_syntax38(self):
    
  320.         """
    
  321.         Call methods returned from dictionary lookups.
    
  322.         """
    
  323.         output = self.engine.render_to_string(
    
  324.             "basic-syntax38", {"var": {"callable": lambda: "foo bar"}}
    
  325.         )
    
  326.         self.assertEqual(output, "foo bar")
    
  327. 
    
  328.     @setup({"template": "{% block content %}"})
    
  329.     def test_unclosed_block(self):
    
  330.         msg = "Unclosed tag on line 1: 'block'. Looking for one of: endblock."
    
  331.         with self.assertRaisesMessage(TemplateSyntaxError, msg):
    
  332.             self.engine.render_to_string("template")
    
  333. 
    
  334.     @setup({"template": "{% if a %}"})
    
  335.     def test_unclosed_block2(self):
    
  336.         msg = "Unclosed tag on line 1: 'if'. Looking for one of: elif, else, endif."
    
  337.         with self.assertRaisesMessage(TemplateSyntaxError, msg):
    
  338.             self.engine.render_to_string("template")
    
  339. 
    
  340.     @setup({"tpl-str": "%s", "tpl-percent": "%%", "tpl-weird-percent": "% %s"})
    
  341.     def test_ignores_strings_that_look_like_format_interpolation(self):
    
  342.         output = self.engine.render_to_string("tpl-str")
    
  343.         self.assertEqual(output, "%s")
    
  344.         output = self.engine.render_to_string("tpl-percent")
    
  345.         self.assertEqual(output, "%%")
    
  346.         output = self.engine.render_to_string("tpl-weird-percent")
    
  347.         self.assertEqual(output, "% %s")
    
  348. 
    
  349. 
    
  350. class BlockContextTests(SimpleTestCase):
    
  351.     def test_repr(self):
    
  352.         block_context = BlockContext()
    
  353.         block_context.add_blocks({"content": BlockNode("content", [])})
    
  354.         self.assertEqual(
    
  355.             repr(block_context),
    
  356.             "<BlockContext: blocks=defaultdict(<class 'list'>, "
    
  357.             "{'content': [<Block Node: content. Contents: []>]})>",
    
  358.         )