from mako import compat from mako import lookup from mako.template import Template from test import assert_raises from test import eq_ from test import requires_python_3 from test import TemplateTest from test.util import flatten_result from test.util import result_lines class DefTest(TemplateTest): def test_def_noargs(self): template = Template( """ ${mycomp()} <%def name="mycomp()"> hello mycomp ${variable} """ ) eq_(template.render(variable="hi").strip(), """hello mycomp hi""") def test_def_blankargs(self): template = Template( """ <%def name="mycomp()"> hello mycomp ${variable} ${mycomp()}""" ) eq_(template.render(variable="hi").strip(), "hello mycomp hi") def test_def_args(self): template = Template( """ <%def name="mycomp(a, b)"> hello mycomp ${variable}, ${a}, ${b} ${mycomp(5, 6)}""" ) eq_( template.render(variable="hi", a=5, b=6).strip(), """hello mycomp hi, 5, 6""", ) @requires_python_3 def test_def_py3k_args(self): template = Template( """ <%def name="kwonly(one, two, *three, four, five=5, **six)"> look at all these args: ${one} ${two} ${three[0]} """ """${four} ${five} ${six['seven']} ${kwonly('one', 'two', 'three', four='four', seven='seven')}""" ) eq_( template.render(one=1, two=2, three=(3,), six=6).strip(), """look at all these args: one two three four 5 seven""", ) def test_inter_def(self): """test defs calling each other""" template = Template( """ ${b()} <%def name="a()">\ im a <%def name="b()"> im b and heres a: ${a()} <%def name="c()"> im c """ ) # check that "a" is declared in "b", but not in "c" if compat.py3k: assert "a" not in template.module.render_c.__code__.co_varnames assert "a" in template.module.render_b.__code__.co_varnames else: assert "a" not in template.module.render_c.func_code.co_varnames assert "a" in template.module.render_b.func_code.co_varnames # then test output eq_(flatten_result(template.render()), "im b and heres a: im a") def test_toplevel(self): """test calling a def from the top level""" template = Template( """ this is the body <%def name="a()"> this is a <%def name="b(x, y)"> this is b, ${x} ${y} """ ) self._do_test( template.get_def("a"), "this is a", filters=flatten_result ) self._do_test( template.get_def("b"), "this is b, 10 15", template_args={"x": 10, "y": 15}, filters=flatten_result, ) self._do_test( template.get_def("body"), "this is the body", filters=flatten_result, ) # test that args outside of the dict can be used self._do_test( template.get_def("a"), "this is a", filters=flatten_result, template_args={"q": 5, "zq": "test"}, ) def test_def_operations(self): """test get/list/has def""" template = Template( """ this is the body <%def name="a()"> this is a <%def name="b(x, y)"> this is b, ${x} ${y} """ ) assert template.get_def("a") assert template.get_def("b") assert_raises(AttributeError, template.get_def, ("c")) assert template.has_def("a") assert template.has_def("b") assert not template.has_def("c") defs = template.list_defs() assert "a" in defs assert "b" in defs assert "body" in defs assert "c" not in defs class ScopeTest(TemplateTest): """test scoping rules. The key is, enclosing scope always takes precedence over contextual scope.""" def test_scope_one(self): self._do_memory_test( """ <%def name="a()"> this is a, and y is ${y} ${a()} <% y = 7 %> ${a()} """, "this is a, and y is None this is a, and y is 7", filters=flatten_result, template_args={"y": None}, ) def test_scope_two(self): t = Template( """ y is ${y} <% y = 7 %> y is ${y} """ ) try: t.render(y=None) assert False except UnboundLocalError: assert True def test_scope_four(self): """test that variables are pulled from 'enclosing' scope before context.""" t = Template( """ <% x = 5 %> <%def name="a()"> this is a. x is ${x}. <%def name="b()"> <% x = 9 %> this is b. x is ${x}. calling a. ${a()} ${b()} """ ) eq_( flatten_result(t.render()), "this is b. x is 9. calling a. this is a. x is 5.", ) def test_scope_five(self): """test that variables are pulled from 'enclosing' scope before context.""" # same as test four, but adds a scope around it. t = Template( """ <%def name="enclosing()"> <% x = 5 %> <%def name="a()"> this is a. x is ${x}. <%def name="b()"> <% x = 9 %> this is b. x is ${x}. calling a. ${a()} ${b()} ${enclosing()} """ ) eq_( flatten_result(t.render()), "this is b. x is 9. calling a. this is a. x is 5.", ) def test_scope_six(self): """test that the initial context counts as 'enclosing' scope, for plain defs""" t = Template( """ <%def name="a()"> a: x is ${x} <%def name="b()"> <% x = 10 %> b. x is ${x}. ${a()} ${b()} """ ) eq_(flatten_result(t.render(x=5)), "b. x is 10. a: x is 5") def test_scope_seven(self): """test that the initial context counts as 'enclosing' scope, for nested defs""" t = Template( """ <%def name="enclosing()"> <%def name="a()"> a: x is ${x} <%def name="b()"> <% x = 10 %> b. x is ${x}. ${a()} ${b()} ${enclosing()} """ ) eq_(flatten_result(t.render(x=5)), "b. x is 10. a: x is 5") def test_scope_eight(self): """test that the initial context counts as 'enclosing' scope, for nested defs""" t = Template( """ <%def name="enclosing()"> <%def name="a()"> a: x is ${x} <%def name="b()"> <% x = 10 %> b. x is ${x}. ${a()} ${b()} ${enclosing()} """ ) eq_(flatten_result(t.render(x=5)), "b. x is 10. a: x is 5") def test_scope_nine(self): """test that 'enclosing scope' doesnt get exported to other templates""" l = lookup.TemplateLookup() l.put_string( "main", """ <% x = 5 %> this is main. <%include file="secondary"/> """, ) l.put_string( "secondary", """ this is secondary. x is ${x} """, ) eq_( flatten_result(l.get_template("main").render(x=2)), "this is main. this is secondary. x is 2", ) def test_scope_ten(self): t = Template( """ <%def name="a()"> <%def name="b()"> <% y = 19 %> b/c: ${c()} b/y: ${y} <%def name="c()"> c/y: ${y} <% # we assign to "y". but the 'enclosing # scope' of "b" and "c" is from # the "y" on the outside y = 10 %> a/y: ${y} a/b: ${b()} <% y = 7 %> main/a: ${a()} main/y: ${y} """ ) eq_( flatten_result(t.render()), "main/a: a/y: 10 a/b: b/c: c/y: 10 b/y: 19 main/y: 7", ) def test_scope_eleven(self): t = Template( """ x is ${x} <%def name="a(x)"> this is a, ${b()} <%def name="b()"> this is b, x is ${x} ${a(x=5)} """ ) eq_( result_lines(t.render(x=10)), ["x is 10", "this is a,", "this is b, x is 5"], ) def test_unbound_scope(self): t = Template( """ <% y = 10 %> <%def name="a()"> y is: ${y} <% # should raise error ? y = 15 %> y is ${y} ${a()} """ ) assert_raises(UnboundLocalError, t.render) def test_unbound_scope_two(self): t = Template( """ <%def name="enclosing()"> <% y = 10 %> <%def name="a()"> y is: ${y} <% # should raise error ? y = 15 %> y is ${y} ${a()} ${enclosing()} """ ) try: print(t.render()) assert False except UnboundLocalError: assert True def test_canget_kwargs(self): """test that arguments passed to the body() function are accessible by top-level defs""" l = lookup.TemplateLookup() l.put_string( "base", """ ${next.body(x=12)} """, ) l.put_string( "main", """ <%inherit file="base"/> <%page args="x"/> this is main. x is ${x} ${a()} <%def name="a(**args)"> this is a, x is ${x} """, ) # test via inheritance eq_( result_lines(l.get_template("main").render()), ["this is main. x is 12", "this is a, x is 12"], ) l.put_string( "another", """ <%namespace name="ns" file="main"/> ${ns.body(x=15)} """, ) # test via namespace eq_( result_lines(l.get_template("another").render()), ["this is main. x is 15", "this is a, x is 15"], ) def test_inline_expression_from_arg_one(self): """test that cache_key=${foo} gets its value from the 'foo' argument in the <%def> tag, and strict_undefined doesn't complain. this is #191. """ t = Template( """ <%def name="layout(foo)" cached="True" cache_key="${foo}"> foo: ${foo} ${layout(3)} """, strict_undefined=True, cache_impl="plain", ) eq_(result_lines(t.render()), ["foo: 3"]) def test_interpret_expression_from_arg_two(self): """test that cache_key=${foo} gets its value from the 'foo' argument regardless of it being passed from the context. This is here testing that there's no change to existing behavior before and after #191. """ t = Template( """ <%def name="layout(foo)" cached="True" cache_key="${foo}"> foo: ${value} ${layout(3)} """, cache_impl="plain", ) eq_(result_lines(t.render(foo="foo", value=1)), ["foo: 1"]) eq_(result_lines(t.render(foo="bar", value=2)), ["foo: 1"]) class NestedDefTest(TemplateTest): def test_nested_def(self): t = Template( """ ${hi()} <%def name="hi()"> hey, im hi. and heres ${foo()}, ${bar()} <%def name="foo()"> this is foo <%def name="bar()"> this is bar """ ) eq_( flatten_result(t.render()), "hey, im hi. and heres this is foo , this is bar", ) def test_nested_2(self): t = Template( """ x is ${x} <%def name="a()"> this is a, x is ${x} ${b()} <%def name="b()"> this is b: ${x} ${a()} """ ) eq_( flatten_result(t.render(x=10)), "x is 10 this is a, x is 10 this is b: 10", ) def test_nested_with_args(self): t = Template( """ ${a()} <%def name="a()"> <%def name="b(x, y=2)"> b x is ${x} y is ${y} a ${b(5)} """ ) eq_(flatten_result(t.render()), "a b x is 5 y is 2") def test_nested_def_2(self): template = Template( """ ${a()} <%def name="a()"> <%def name="b()"> <%def name="c()"> comp c ${c()} ${b()} """ ) eq_(flatten_result(template.render()), "comp c") def test_nested_nested_def(self): t = Template( """ ${a()} <%def name="a()"> a <%def name="b1()"> a_b1 <%def name="b2()"> a_b2 ${c1()} <%def name="c1()"> a_b2_c1 <%def name="b3()"> a_b3 ${c1()} <%def name="c1()"> a_b3_c1 heres x: ${x} <% y = 7 %> y is ${y} <%def name="c2()"> a_b3_c2 y is ${y} c1 is ${c1()} ${c2()} ${b1()} ${b2()} ${b3()} """ ) eq_( flatten_result(t.render(x=5, y=None)), "a a_b1 a_b2 a_b2_c1 a_b3 a_b3_c1 " "heres x: 5 y is 7 a_b3_c2 y is " "None c1 is a_b3_c1 heres x: 5 y is 7", ) def test_nested_nested_def_2(self): t = Template( """ <%def name="a()"> this is a ${b()} <%def name="b()"> this is b ${c()} <%def name="c()"> this is c ${a()} """ ) eq_(flatten_result(t.render()), "this is a this is b this is c") def test_outer_scope(self): t = Template( """ <%def name="a()"> a: x is ${x} <%def name="b()"> <%def name="c()"> <% x = 10 %> c. x is ${x}. ${a()} b. ${c()} ${b()} x is ${x} """ ) eq_(flatten_result(t.render(x=5)), "b. c. x is 10. a: x is 5 x is 5") class ExceptionTest(TemplateTest): def test_raise(self): template = Template( """ <% raise Exception("this is a test") %> """, format_exceptions=False, ) assert_raises(Exception, template.render) def test_handler(self): def handle(context, error): context.write("error message is " + str(error)) return True template = Template( """ <% raise Exception("this is a test") %> """, error_handler=handle, ) eq_(template.render().strip(), "error message is this is a test")