from django.template.base import TemplateSyntaxError from django.template.context import Context from django.template.loader_tags import BlockContext, BlockNode from django.test import SimpleTestCase from ..utils import SilentAttrClass, SilentGetItemClass, SomeClass, setup basic_templates = { 'basic-syntax01': 'something cool', 'basic-syntax02': '{{ headline }}', 'basic-syntax03': '{{ first }} --- {{ second }}', } class BasicSyntaxTests(SimpleTestCase): @setup(basic_templates) def test_basic_syntax01(self): """ Plain text should go through the template parser untouched. """ output = self.engine.render_to_string('basic-syntax01') self.assertEqual(output, "something cool") @setup(basic_templates) def test_basic_syntax02(self): """ Variables should be replaced with their value in the current context """ output = self.engine.render_to_string('basic-syntax02', {'headline': 'Success'}) self.assertEqual(output, 'Success') @setup(basic_templates) def test_basic_syntax03(self): """ More than one replacement variable is allowed in a template """ output = self.engine.render_to_string('basic-syntax03', {"first": 1, "second": 2}) self.assertEqual(output, '1 --- 2') @setup({'basic-syntax04': 'as{{ missing }}df'}) def test_basic_syntax04(self): """ Fail silently when a variable is not found in the current context """ output = self.engine.render_to_string('basic-syntax04') if self.engine.string_if_invalid: self.assertEqual(output, 'asINVALIDdf') else: self.assertEqual(output, 'asdf') @setup({'basic-syntax06': '{{ multi word variable }}'}) def test_basic_syntax06(self): """ A variable may not contain more than one word """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax06') @setup({'basic-syntax07': '{{ }}'}) def test_basic_syntax07(self): """ Raise TemplateSyntaxError for empty variable tags. """ with self.assertRaisesMessage(TemplateSyntaxError, 'Empty variable tag on line 1'): self.engine.get_template('basic-syntax07') @setup({'basic-syntax08': '{{ }}'}) def test_basic_syntax08(self): """ Raise TemplateSyntaxError for empty variable tags. """ with self.assertRaisesMessage(TemplateSyntaxError, 'Empty variable tag on line 1'): self.engine.get_template('basic-syntax08') @setup({'basic-syntax09': '{{ var.method }}'}) def test_basic_syntax09(self): """ Attribute syntax allows a template to call an object's attribute """ output = self.engine.render_to_string('basic-syntax09', {'var': SomeClass()}) self.assertEqual(output, 'SomeClass.method') @setup({'basic-syntax10': '{{ var.otherclass.method }}'}) def test_basic_syntax10(self): """ Multiple levels of attribute access are allowed. """ output = self.engine.render_to_string('basic-syntax10', {'var': SomeClass()}) self.assertEqual(output, 'OtherClass.method') @setup({'basic-syntax11': '{{ var.blech }}'}) def test_basic_syntax11(self): """ Fail silently when a variable's attribute isn't found. """ output = self.engine.render_to_string('basic-syntax11', {'var': SomeClass()}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'basic-syntax12': '{{ var.__dict__ }}'}) def test_basic_syntax12(self): """ Raise TemplateSyntaxError when trying to access a variable beginning with an underscore. """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax12') # Raise TemplateSyntaxError when trying to access a variable # containing an illegal character. @setup({'basic-syntax13': "{{ va>r }}"}) def test_basic_syntax13(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax13') @setup({'basic-syntax14': "{{ (var.r) }}"}) def test_basic_syntax14(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax14') @setup({'basic-syntax15': "{{ sp%am }}"}) def test_basic_syntax15(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax15') @setup({'basic-syntax16': "{{ eggs! }}"}) def test_basic_syntax16(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax16') @setup({'basic-syntax17': "{{ moo? }}"}) def test_basic_syntax17(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax17') @setup({'basic-syntax18': "{{ foo.bar }}"}) def test_basic_syntax18(self): """ Attribute syntax allows a template to call a dictionary key's value. """ output = self.engine.render_to_string('basic-syntax18', {"foo": {"bar": "baz"}}) self.assertEqual(output, "baz") @setup({'basic-syntax19': "{{ foo.spam }}"}) def test_basic_syntax19(self): """ Fail silently when a variable's dictionary key isn't found. """ output = self.engine.render_to_string('basic-syntax19', {"foo": {"bar": "baz"}}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'basic-syntax20': "{{ var.method2 }}"}) def test_basic_syntax20(self): """ Fail silently when accessing a non-simple method """ output = self.engine.render_to_string('basic-syntax20', {'var': SomeClass()}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'basic-syntax20b': "{{ var.method5 }}"}) def test_basic_syntax20b(self): """ Don't silence a TypeError if it was raised inside a callable. """ template = self.engine.get_template('basic-syntax20b') with self.assertRaises(TypeError): template.render(Context({'var': SomeClass()})) # Don't get confused when parsing something that is almost, but not # quite, a template tag. @setup({'basic-syntax21': "a {{ moo %} b"}) def test_basic_syntax21(self): output = self.engine.render_to_string('basic-syntax21') self.assertEqual(output, "a {{ moo %} b") @setup({'basic-syntax22': "{{ moo #}"}) def test_basic_syntax22(self): output = self.engine.render_to_string('basic-syntax22') self.assertEqual(output, "{{ moo #}") @setup({'basic-syntax23': "{{ moo #} {{ cow }}"}) def test_basic_syntax23(self): """ Treat "moo #} {{ cow" as the variable. Not ideal, but costly to work around, so this triggers an error. """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax23') @setup({'basic-syntax24': "{{ moo\n }}"}) def test_basic_syntax24(self): """ Embedded newlines make it not-a-tag. """ output = self.engine.render_to_string('basic-syntax24') self.assertEqual(output, "{{ moo\n }}") # Literal strings are permitted inside variables, mostly for i18n # purposes. @setup({'basic-syntax25': '{{ "fred" }}'}) def test_basic_syntax25(self): output = self.engine.render_to_string('basic-syntax25') self.assertEqual(output, "fred") @setup({'basic-syntax26': r'{{ "\"fred\"" }}'}) def test_basic_syntax26(self): output = self.engine.render_to_string('basic-syntax26') self.assertEqual(output, "\"fred\"") @setup({'basic-syntax27': r'{{ _("\"fred\"") }}'}) def test_basic_syntax27(self): output = self.engine.render_to_string('basic-syntax27') self.assertEqual(output, "\"fred\"") # #12554 -- Make sure a silent_variable_failure Exception is # suppressed on dictionary and attribute lookup. @setup({'basic-syntax28': "{{ a.b }}"}) def test_basic_syntax28(self): output = self.engine.render_to_string('basic-syntax28', {'a': SilentGetItemClass()}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'basic-syntax29': "{{ a.b }}"}) def test_basic_syntax29(self): output = self.engine.render_to_string('basic-syntax29', {'a': SilentAttrClass()}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') # Something that starts like a number but has an extra lookup works # as a lookup. @setup({'basic-syntax30': "{{ 1.2.3 }}"}) def test_basic_syntax30(self): output = self.engine.render_to_string( 'basic-syntax30', {"1": {"2": {"3": "d"}}} ) self.assertEqual(output, 'd') @setup({'basic-syntax31': "{{ 1.2.3 }}"}) def test_basic_syntax31(self): output = self.engine.render_to_string( 'basic-syntax31', {"1": {"2": ("a", "b", "c", "d")}}, ) self.assertEqual(output, 'd') @setup({'basic-syntax32': "{{ 1.2.3 }}"}) def test_basic_syntax32(self): output = self.engine.render_to_string( 'basic-syntax32', {"1": (("x", "x", "x", "x"), ("y", "y", "y", "y"), ("a", "b", "c", "d"))}, ) self.assertEqual(output, 'd') @setup({'basic-syntax33': "{{ 1.2.3 }}"}) def test_basic_syntax33(self): output = self.engine.render_to_string( 'basic-syntax33', {"1": ("xxxx", "yyyy", "abcd")}, ) self.assertEqual(output, 'd') @setup({'basic-syntax34': "{{ 1.2.3 }}"}) def test_basic_syntax34(self): output = self.engine.render_to_string( 'basic-syntax34', {"1": ({"x": "x"}, {"y": "y"}, {"z": "z", "3": "d"})} ) self.assertEqual(output, 'd') # Numbers are numbers even if their digits are in the context. @setup({'basic-syntax35': "{{ 1 }}"}) def test_basic_syntax35(self): output = self.engine.render_to_string('basic-syntax35', {"1": "abc"}) self.assertEqual(output, '1') @setup({'basic-syntax36': "{{ 1.2 }}"}) def test_basic_syntax36(self): output = self.engine.render_to_string('basic-syntax36', {"1": "abc"}) self.assertEqual(output, '1.2') @setup({'basic-syntax37': '{{ callable }}'}) def test_basic_syntax37(self): """ Call methods in the top level of the context. """ output = self.engine.render_to_string('basic-syntax37', {"callable": lambda: "foo bar"}) self.assertEqual(output, 'foo bar') @setup({'basic-syntax38': '{{ var.callable }}'}) def test_basic_syntax38(self): """ Call methods returned from dictionary lookups. """ output = self.engine.render_to_string('basic-syntax38', {"var": {"callable": lambda: "foo bar"}}) self.assertEqual(output, 'foo bar') @setup({'template': '{% block content %}'}) def test_unclosed_block(self): msg = "Unclosed tag on line 1: 'block'. Looking for one of: endblock." with self.assertRaisesMessage(TemplateSyntaxError, msg): self.engine.render_to_string('template') @setup({'template': '{% if a %}'}) def test_unclosed_block2(self): msg = "Unclosed tag on line 1: 'if'. Looking for one of: elif, else, endif." with self.assertRaisesMessage(TemplateSyntaxError, msg): self.engine.render_to_string('template') @setup({'tpl-str': '%s', 'tpl-percent': '%%', 'tpl-weird-percent': '% %s'}) def test_ignores_strings_that_look_like_format_interpolation(self): output = self.engine.render_to_string('tpl-str') self.assertEqual(output, '%s') output = self.engine.render_to_string('tpl-percent') self.assertEqual(output, '%%') output = self.engine.render_to_string('tpl-weird-percent') self.assertEqual(output, '% %s') class BlockContextTests(SimpleTestCase): def test_repr(self): block_context = BlockContext() block_context.add_blocks({'content': BlockNode('content', [])}) self.assertEqual( repr(block_context), ", " "{'content': []})>", )