• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from mako import lookup
2from mako.template import Template
3from mako.testing.assertions import assert_raises
4from mako.testing.assertions import eq_
5from mako.testing.fixtures import TemplateTest
6from mako.testing.helpers import flatten_result
7from mako.testing.helpers import result_lines
8
9
10class DefTest(TemplateTest):
11    def test_def_noargs(self):
12        template = Template(
13            """
14
15        ${mycomp()}
16
17        <%def name="mycomp()">
18            hello mycomp ${variable}
19        </%def>
20
21        """
22        )
23        eq_(template.render(variable="hi").strip(), """hello mycomp hi""")
24
25    def test_def_blankargs(self):
26        template = Template(
27            """
28        <%def name="mycomp()">
29            hello mycomp ${variable}
30        </%def>
31
32        ${mycomp()}"""
33        )
34        eq_(template.render(variable="hi").strip(), "hello mycomp hi")
35
36    def test_def_args(self):
37        template = Template(
38            """
39        <%def name="mycomp(a, b)">
40            hello mycomp ${variable}, ${a}, ${b}
41        </%def>
42
43        ${mycomp(5, 6)}"""
44        )
45        eq_(
46            template.render(variable="hi", a=5, b=6).strip(),
47            """hello mycomp hi, 5, 6""",
48        )
49
50    def test_def_py3k_args(self):
51        template = Template(
52            """
53        <%def name="kwonly(one, two, *three, four, five=5, **six)">
54            look at all these args: ${one} ${two} ${three[0]} """
55            """${four} ${five} ${six['seven']}
56        </%def>
57
58        ${kwonly('one', 'two', 'three', four='four', seven='seven')}"""
59        )
60        eq_(
61            template.render(one=1, two=2, three=(3,), six=6).strip(),
62            """look at all these args: one two three four 5 seven""",
63        )
64
65    def test_inter_def(self):
66        """test defs calling each other"""
67        template = Template(
68            """
69        ${b()}
70
71        <%def name="a()">\
72        im a
73        </%def>
74
75        <%def name="b()">
76        im b
77        and heres a:  ${a()}
78        </%def>
79
80        <%def name="c()">
81        im c
82        </%def>
83"""
84        )
85        # check that "a" is declared in "b", but not in "c"
86        assert "a" not in template.module.render_c.__code__.co_varnames
87        assert "a" in template.module.render_b.__code__.co_varnames
88
89        # then test output
90        eq_(flatten_result(template.render()), "im b and heres a: im a")
91
92    def test_toplevel(self):
93        """test calling a def from the top level"""
94
95        template = Template(
96            """
97
98            this is the body
99
100            <%def name="a()">
101                this is a
102            </%def>
103
104            <%def name="b(x, y)">
105                this is b, ${x} ${y}
106            </%def>
107
108        """
109        )
110
111        self._do_test(
112            template.get_def("a"), "this is a", filters=flatten_result
113        )
114        self._do_test(
115            template.get_def("b"),
116            "this is b, 10 15",
117            template_args={"x": 10, "y": 15},
118            filters=flatten_result,
119        )
120        self._do_test(
121            template.get_def("body"),
122            "this is the body",
123            filters=flatten_result,
124        )
125
126        # test that args outside of the dict can be used
127        self._do_test(
128            template.get_def("a"),
129            "this is a",
130            filters=flatten_result,
131            template_args={"q": 5, "zq": "test"},
132        )
133
134    def test_def_operations(self):
135        """test get/list/has def"""
136
137        template = Template(
138            """
139
140            this is the body
141
142            <%def name="a()">
143                this is a
144            </%def>
145
146            <%def name="b(x, y)">
147                this is b, ${x} ${y}
148            </%def>
149
150        """
151        )
152
153        assert template.get_def("a")
154        assert template.get_def("b")
155        assert_raises(AttributeError, template.get_def, ("c"))
156
157        assert template.has_def("a")
158        assert template.has_def("b")
159        assert not template.has_def("c")
160
161        defs = template.list_defs()
162        assert "a" in defs
163        assert "b" in defs
164        assert "body" in defs
165        assert "c" not in defs
166
167
168class ScopeTest(TemplateTest):
169    """test scoping rules.  The key is, enclosing
170    scope always takes precedence over contextual scope."""
171
172    def test_scope_one(self):
173        self._do_memory_test(
174            """
175        <%def name="a()">
176            this is a, and y is ${y}
177        </%def>
178
179        ${a()}
180
181        <%
182            y = 7
183        %>
184
185        ${a()}
186
187""",
188            "this is a, and y is None this is a, and y is 7",
189            filters=flatten_result,
190            template_args={"y": None},
191        )
192
193    def test_scope_two(self):
194        t = Template(
195            """
196        y is ${y}
197
198        <%
199            y = 7
200        %>
201
202        y is ${y}
203"""
204        )
205        try:
206            t.render(y=None)
207            assert False
208        except UnboundLocalError:
209            assert True
210
211    def test_scope_four(self):
212        """test that variables are pulled
213        from 'enclosing' scope before context."""
214        t = Template(
215            """
216            <%
217                x = 5
218            %>
219            <%def name="a()">
220                this is a. x is ${x}.
221            </%def>
222
223            <%def name="b()">
224                <%
225                    x = 9
226                %>
227                this is b. x is ${x}.
228                calling a. ${a()}
229            </%def>
230
231            ${b()}
232"""
233        )
234        eq_(
235            flatten_result(t.render()),
236            "this is b. x is 9. calling a. this is a. x is 5.",
237        )
238
239    def test_scope_five(self):
240        """test that variables are pulled from
241        'enclosing' scope before context."""
242        # same as test four, but adds a scope around it.
243        t = Template(
244            """
245            <%def name="enclosing()">
246            <%
247                x = 5
248            %>
249            <%def name="a()">
250                this is a. x is ${x}.
251            </%def>
252
253            <%def name="b()">
254                <%
255                    x = 9
256                %>
257                this is b. x is ${x}.
258                calling a. ${a()}
259            </%def>
260
261            ${b()}
262            </%def>
263            ${enclosing()}
264"""
265        )
266        eq_(
267            flatten_result(t.render()),
268            "this is b. x is 9. calling a. this is a. x is 5.",
269        )
270
271    def test_scope_six(self):
272        """test that the initial context counts
273        as 'enclosing' scope, for plain defs"""
274        t = Template(
275            """
276
277        <%def name="a()">
278            a: x is ${x}
279        </%def>
280
281        <%def name="b()">
282            <%
283                x = 10
284            %>
285            b. x is ${x}.  ${a()}
286        </%def>
287
288        ${b()}
289    """
290        )
291        eq_(flatten_result(t.render(x=5)), "b. x is 10. a: x is 5")
292
293    def test_scope_seven(self):
294        """test that the initial context counts
295        as 'enclosing' scope, for nested defs"""
296        t = Template(
297            """
298        <%def name="enclosing()">
299            <%def name="a()">
300                a: x is ${x}
301            </%def>
302
303            <%def name="b()">
304                <%
305                    x = 10
306                %>
307                b. x is ${x}.  ${a()}
308            </%def>
309
310            ${b()}
311        </%def>
312        ${enclosing()}
313    """
314        )
315        eq_(flatten_result(t.render(x=5)), "b. x is 10. a: x is 5")
316
317    def test_scope_eight(self):
318        """test that the initial context counts
319        as 'enclosing' scope, for nested defs"""
320        t = Template(
321            """
322        <%def name="enclosing()">
323            <%def name="a()">
324                a: x is ${x}
325            </%def>
326
327            <%def name="b()">
328                <%
329                    x = 10
330                %>
331
332                b. x is ${x}.  ${a()}
333            </%def>
334
335            ${b()}
336        </%def>
337        ${enclosing()}
338    """
339        )
340        eq_(flatten_result(t.render(x=5)), "b. x is 10. a: x is 5")
341
342    def test_scope_nine(self):
343        """test that 'enclosing scope' doesnt
344        get exported to other templates"""
345
346        l = lookup.TemplateLookup()
347        l.put_string(
348            "main",
349            """
350        <%
351            x = 5
352        %>
353        this is main.  <%include file="secondary"/>
354""",
355        )
356
357        l.put_string(
358            "secondary",
359            """
360        this is secondary.  x is ${x}
361""",
362        )
363
364        eq_(
365            flatten_result(l.get_template("main").render(x=2)),
366            "this is main. this is secondary. x is 2",
367        )
368
369    def test_scope_ten(self):
370        t = Template(
371            """
372            <%def name="a()">
373                <%def name="b()">
374                    <%
375                        y = 19
376                    %>
377                    b/c: ${c()}
378                    b/y: ${y}
379                </%def>
380                <%def name="c()">
381                    c/y: ${y}
382                </%def>
383
384                <%
385                    # we assign to "y".  but the 'enclosing
386                    # scope' of "b" and "c" is from
387                    # the "y" on the outside
388                    y = 10
389                %>
390                a/y: ${y}
391                a/b: ${b()}
392            </%def>
393
394            <%
395                y = 7
396            %>
397            main/a: ${a()}
398            main/y: ${y}
399    """
400        )
401        eq_(
402            flatten_result(t.render()),
403            "main/a: a/y: 10 a/b: b/c: c/y: 10 b/y: 19 main/y: 7",
404        )
405
406    def test_scope_eleven(self):
407        t = Template(
408            """
409            x is ${x}
410            <%def name="a(x)">
411                this is a, ${b()}
412                <%def name="b()">
413                    this is b, x is ${x}
414                </%def>
415            </%def>
416
417            ${a(x=5)}
418"""
419        )
420        eq_(
421            result_lines(t.render(x=10)),
422            ["x is 10", "this is a,", "this is b, x is 5"],
423        )
424
425    def test_unbound_scope(self):
426        t = Template(
427            """
428            <%
429                y = 10
430            %>
431            <%def name="a()">
432                y is: ${y}
433                <%
434                    # should raise error ?
435                    y = 15
436                %>
437                y is ${y}
438            </%def>
439            ${a()}
440"""
441        )
442        assert_raises(UnboundLocalError, t.render)
443
444    def test_unbound_scope_two(self):
445        t = Template(
446            """
447            <%def name="enclosing()">
448            <%
449                y = 10
450            %>
451            <%def name="a()">
452                y is: ${y}
453                <%
454                    # should raise error ?
455                    y = 15
456                %>
457                y is ${y}
458            </%def>
459            ${a()}
460            </%def>
461            ${enclosing()}
462"""
463        )
464        try:
465            print(t.render())
466            assert False
467        except UnboundLocalError:
468            assert True
469
470    def test_canget_kwargs(self):
471        """test that arguments passed to the body()
472        function are accessible by top-level defs"""
473        l = lookup.TemplateLookup()
474        l.put_string(
475            "base",
476            """
477
478        ${next.body(x=12)}
479
480        """,
481        )
482
483        l.put_string(
484            "main",
485            """
486            <%inherit file="base"/>
487            <%page args="x"/>
488            this is main.  x is ${x}
489
490            ${a()}
491
492            <%def name="a(**args)">
493                this is a, x is ${x}
494            </%def>
495        """,
496        )
497
498        # test via inheritance
499        eq_(
500            result_lines(l.get_template("main").render()),
501            ["this is main. x is 12", "this is a, x is 12"],
502        )
503
504        l.put_string(
505            "another",
506            """
507            <%namespace name="ns" file="main"/>
508
509            ${ns.body(x=15)}
510        """,
511        )
512        # test via namespace
513        eq_(
514            result_lines(l.get_template("another").render()),
515            ["this is main. x is 15", "this is a, x is 15"],
516        )
517
518    def test_inline_expression_from_arg_one(self):
519        """test that cache_key=${foo} gets its value from
520        the 'foo' argument in the <%def> tag,
521        and strict_undefined doesn't complain.
522
523        this is #191.
524
525        """
526        t = Template(
527            """
528        <%def name="layout(foo)" cached="True" cache_key="${foo}">
529        foo: ${foo}
530        </%def>
531
532        ${layout(3)}
533        """,
534            strict_undefined=True,
535            cache_impl="plain",
536        )
537
538        eq_(result_lines(t.render()), ["foo: 3"])
539
540    def test_interpret_expression_from_arg_two(self):
541        """test that cache_key=${foo} gets its value from
542        the 'foo' argument regardless of it being passed
543        from the context.
544
545        This is here testing that there's no change
546        to existing behavior before and after #191.
547
548        """
549        t = Template(
550            """
551        <%def name="layout(foo)" cached="True" cache_key="${foo}">
552        foo: ${value}
553        </%def>
554
555        ${layout(3)}
556        """,
557            cache_impl="plain",
558        )
559
560        eq_(result_lines(t.render(foo="foo", value=1)), ["foo: 1"])
561        eq_(result_lines(t.render(foo="bar", value=2)), ["foo: 1"])
562
563
564class NestedDefTest(TemplateTest):
565    def test_nested_def(self):
566        t = Template(
567            """
568
569        ${hi()}
570
571        <%def name="hi()">
572            hey, im hi.
573            and heres ${foo()}, ${bar()}
574
575            <%def name="foo()">
576                this is foo
577            </%def>
578
579            <%def name="bar()">
580                this is bar
581            </%def>
582        </%def>
583"""
584        )
585        eq_(
586            flatten_result(t.render()),
587            "hey, im hi. and heres this is foo , this is bar",
588        )
589
590    def test_nested_2(self):
591        t = Template(
592            """
593            x is ${x}
594            <%def name="a()">
595                this is a, x is ${x}
596                ${b()}
597                <%def name="b()">
598                    this is b: ${x}
599                </%def>
600            </%def>
601            ${a()}
602"""
603        )
604
605        eq_(
606            flatten_result(t.render(x=10)),
607            "x is 10 this is a, x is 10 this is b: 10",
608        )
609
610    def test_nested_with_args(self):
611        t = Template(
612            """
613        ${a()}
614        <%def name="a()">
615            <%def name="b(x, y=2)">
616                b x is ${x} y is ${y}
617            </%def>
618            a ${b(5)}
619        </%def>
620"""
621        )
622        eq_(flatten_result(t.render()), "a b x is 5 y is 2")
623
624    def test_nested_def_2(self):
625        template = Template(
626            """
627        ${a()}
628        <%def name="a()">
629            <%def name="b()">
630                <%def name="c()">
631                    comp c
632                </%def>
633                ${c()}
634            </%def>
635            ${b()}
636        </%def>
637"""
638        )
639        eq_(flatten_result(template.render()), "comp c")
640
641    def test_nested_nested_def(self):
642        t = Template(
643            """
644
645        ${a()}
646        <%def name="a()">
647            a
648            <%def name="b1()">
649                a_b1
650            </%def>
651            <%def name="b2()">
652                a_b2 ${c1()}
653                <%def name="c1()">
654                    a_b2_c1
655                </%def>
656            </%def>
657            <%def name="b3()">
658                a_b3 ${c1()}
659                <%def name="c1()">
660                    a_b3_c1 heres x: ${x}
661                    <%
662                        y = 7
663                    %>
664                    y is ${y}
665                </%def>
666                <%def name="c2()">
667                    a_b3_c2
668                    y is ${y}
669                    c1 is ${c1()}
670                </%def>
671                ${c2()}
672            </%def>
673
674            ${b1()} ${b2()}  ${b3()}
675        </%def>
676"""
677        )
678        eq_(
679            flatten_result(t.render(x=5, y=None)),
680            "a a_b1 a_b2 a_b2_c1 a_b3 a_b3_c1 "
681            "heres x: 5 y is 7 a_b3_c2 y is "
682            "None c1 is a_b3_c1 heres x: 5 y is 7",
683        )
684
685    def test_nested_nested_def_2(self):
686        t = Template(
687            """
688        <%def name="a()">
689            this is a ${b()}
690            <%def name="b()">
691                this is b
692                ${c()}
693            </%def>
694
695            <%def name="c()">
696                this is c
697            </%def>
698        </%def>
699        ${a()}
700"""
701        )
702        eq_(flatten_result(t.render()), "this is a this is b this is c")
703
704    def test_outer_scope(self):
705        t = Template(
706            """
707        <%def name="a()">
708            a: x is ${x}
709        </%def>
710
711        <%def name="b()">
712            <%def name="c()">
713            <%
714                x = 10
715            %>
716            c. x is ${x}.  ${a()}
717            </%def>
718
719            b. ${c()}
720        </%def>
721
722        ${b()}
723
724        x is ${x}
725"""
726        )
727        eq_(flatten_result(t.render(x=5)), "b. c. x is 10. a: x is 5 x is 5")
728
729
730class ExceptionTest(TemplateTest):
731    def test_raise(self):
732        template = Template(
733            """
734            <%
735                raise Exception("this is a test")
736            %>
737    """,
738            format_exceptions=False,
739        )
740        assert_raises(Exception, template.render)
741
742    def test_handler(self):
743        def handle(context, error):
744            context.write("error message is " + str(error))
745            return True
746
747        template = Template(
748            """
749            <%
750                raise Exception("this is a test")
751            %>
752    """,
753            error_handler=handle,
754        )
755        eq_(template.render().strip(), "error message is this is a test")
756