from mako.template import Template from mako import lookup from test import TemplateTest from util import flatten_result, result_lines from test import eq_, assert_raises class DefTest(TemplateTest): def test_def_noargs(self): template = Template(""" ${mycomp()} <%def name="mycomp()"> hello mycomp ${variable} </%def> """) eq_( template.render(variable='hi').strip(), """hello mycomp hi""" ) def test_def_blankargs(self): template = Template(""" <%def name="mycomp()"> hello mycomp ${variable} </%def> ${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} </%def> ${mycomp(5, 6)}""") eq_( template.render(variable='hi', a=5, b=6).strip(), """hello mycomp hi, 5, 6""" ) def test_inter_def(self): """test defs calling each other""" template = Template(""" ${b()} <%def name="a()">\ im a </%def> <%def name="b()"> im b and heres a: ${a()} </%def> <%def name="c()"> im c </%def> """) # check that "a" is declared in "b", but not in "c" 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> <%def name="b(x, y)"> this is b, ${x} ${y} </%def> """) 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'}) 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} </%def> ${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> <%def name="b()"> <% x = 9 %> this is b. x is ${x}. calling a. ${a()} </%def> ${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> <%def name="b()"> <% x = 9 %> this is b. x is ${x}. calling a. ${a()} </%def> ${b()} </%def> ${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> <%def name="b()"> <% x = 10 %> b. x is ${x}. ${a()} </%def> ${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> <%def name="b()"> <% x = 10 %> b. x is ${x}. ${a()} </%def> ${b()} </%def> ${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> <%def name="b()"> <% x = 10 %> b. x is ${x}. ${a()} </%def> ${b()} </%def> ${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> <%def name="c()"> c/y: ${y} </%def> <% # 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()} </%def> <% 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} </%def> </%def> ${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} </%def> ${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} </%def> ${a()} </%def> ${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} </%def> """) # 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} </%def> ${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} </%def> ${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> <%def name="bar()"> this is bar </%def> </%def> """) 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} </%def> </%def> ${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} </%def> a ${b(5)} </%def> """) 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 </%def> ${c()} </%def> ${b()} </%def> """) 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> <%def name="b2()"> a_b2 ${c1()} <%def name="c1()"> a_b2_c1 </%def> </%def> <%def name="b3()"> a_b3 ${c1()} <%def name="c1()"> a_b3_c1 heres x: ${x} <% y = 7 %> y is ${y} </%def> <%def name="c2()"> a_b3_c2 y is ${y} c1 is ${c1()} </%def> ${c2()} </%def> ${b1()} ${b2()} ${b3()} </%def> """) 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> <%def name="c()"> this is c </%def> </%def> ${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> <%def name="b()"> <%def name="c()"> <% x = 10 %> c. x is ${x}. ${a()} </%def> b. ${c()} </%def> ${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" )