1from mako import exceptions
2from mako import lookup
3from mako.template import Template
4from mako.testing.assertions import assert_raises
5from mako.testing.assertions import assert_raises_message_with_given_cause
6from mako.testing.assertions import eq_
7from mako.testing.fixtures import TemplateTest
8from mako.testing.helpers import flatten_result
9from mako.testing.helpers import result_lines
10
11
12class NamespaceTest(TemplateTest):
13    def test_inline_crossreference(self):
14        self._do_memory_test(
15            """
16            <%namespace name="x">
17                <%def name="a()">
18                    this is x a
19                </%def>
20                <%def name="b()">
21                    this is x b, and heres ${a()}
22                </%def>
23            </%namespace>
24
25            ${x.a()}
26
27            ${x.b()}
28    """,
29            "this is x a this is x b, and heres this is x a",
30            filters=flatten_result,
31        )
32
33    def test_inline_assignment(self):
34        self._do_memory_test(
35            """
36            <%namespace name="x">
37                <%def name="a()">
38                    <%
39                        x = 5
40                    %>
41                    this is x: ${x}
42                </%def>
43            </%namespace>
44
45            ${x.a()}
46
47    """,
48            "this is x: 5",
49            filters=flatten_result,
50        )
51
52    def test_inline_arguments(self):
53        self._do_memory_test(
54            """
55            <%namespace name="x">
56                <%def name="a(x, y)">
57                    <%
58                        result = x * y
59                    %>
60                    result: ${result}
61                </%def>
62            </%namespace>
63
64            ${x.a(5, 10)}
65
66    """,
67            "result: 50",
68            filters=flatten_result,
69        )
70
71    def test_inline_not_duped(self):
72        self._do_memory_test(
73            """
74            <%namespace name="x">
75                <%def name="a()">
76                    foo
77                </%def>
78            </%namespace>
79
80            <%
81                assert x.a is not UNDEFINED, "namespace x.a wasn't defined"
82                assert a is UNDEFINED, "name 'a' is in the body locals"
83            %>
84
85    """,
86            "",
87            filters=flatten_result,
88        )
89
90    def test_dynamic(self):
91        collection = lookup.TemplateLookup()
92
93        collection.put_string(
94            "a",
95            """
96        <%namespace name="b" file="${context['b_def']}"/>
97
98        a.  b: ${b.body()}
99""",
100        )
101
102        collection.put_string(
103            "b",
104            """
105        b.
106""",
107        )
108
109        eq_(
110            flatten_result(collection.get_template("a").render(b_def="b")),
111            "a. b: b.",
112        )
113
114    def test_template(self):
115        collection = lookup.TemplateLookup()
116
117        collection.put_string(
118            "main.html",
119            """
120        <%namespace name="comp" file="defs.html"/>
121
122        this is main.  ${comp.def1("hi")}
123        ${comp.def2("there")}
124""",
125        )
126
127        collection.put_string(
128            "defs.html",
129            """
130        <%def name="def1(s)">
131            def1: ${s}
132        </%def>
133
134        <%def name="def2(x)">
135            def2: ${x}
136        </%def>
137""",
138        )
139
140        assert (
141            flatten_result(collection.get_template("main.html").render())
142            == "this is main. def1: hi def2: there"
143        )
144
145    def test_module(self):
146        collection = lookup.TemplateLookup()
147
148        collection.put_string(
149            "main.html",
150            """
151        <%namespace name="comp" module="test.sample_module_namespace"/>
152
153        this is main.  ${comp.foo1()}
154        ${comp.foo2("hi")}
155""",
156        )
157
158        assert (
159            flatten_result(collection.get_template("main.html").render())
160            == "this is main. this is foo1. this is foo2, x is hi"
161        )
162
163    def test_module_2(self):
164        collection = lookup.TemplateLookup()
165
166        collection.put_string(
167            "main.html",
168            """
169        <%namespace name="comp" module="test.foo.test_ns"/>
170
171        this is main.  ${comp.foo1()}
172        ${comp.foo2("hi")}
173""",
174        )
175
176        assert (
177            flatten_result(collection.get_template("main.html").render())
178            == "this is main. this is foo1. this is foo2, x is hi"
179        )
180
181    def test_module_imports(self):
182        collection = lookup.TemplateLookup()
183
184        collection.put_string(
185            "main.html",
186            """
187        <%namespace import="*" module="test.foo.test_ns"/>
188
189        this is main.  ${foo1()}
190        ${foo2("hi")}
191""",
192        )
193
194        assert (
195            flatten_result(collection.get_template("main.html").render())
196            == "this is main. this is foo1. this is foo2, x is hi"
197        )
198
199    def test_module_imports_2(self):
200        collection = lookup.TemplateLookup()
201
202        collection.put_string(
203            "main.html",
204            """
205        <%namespace import="foo1, foo2" module="test.foo.test_ns"/>
206
207        this is main.  ${foo1()}
208        ${foo2("hi")}
209""",
210        )
211
212        assert (
213            flatten_result(collection.get_template("main.html").render())
214            == "this is main. this is foo1. this is foo2, x is hi"
215        )
216
217    def test_context(self):
218        """test that namespace callables get access to the current context"""
219        collection = lookup.TemplateLookup()
220
221        collection.put_string(
222            "main.html",
223            """
224        <%namespace name="comp" file="defs.html"/>
225
226        this is main.  ${comp.def1()}
227        ${comp.def2("there")}
228""",
229        )
230
231        collection.put_string(
232            "defs.html",
233            """
234        <%def name="def1()">
235            def1: x is ${x}
236        </%def>
237
238        <%def name="def2(x)">
239            def2: x is ${x}
240        </%def>
241""",
242        )
243
244        assert (
245            flatten_result(
246                collection.get_template("main.html").render(x="context x")
247            )
248            == "this is main. def1: x is context x def2: x is there"
249        )
250
251    def test_overload(self):
252        collection = lookup.TemplateLookup()
253
254        collection.put_string(
255            "main.html",
256            """
257        <%namespace name="comp" file="defs.html">
258            <%def name="def1(x, y)">
259                overridden def1 ${x}, ${y}
260            </%def>
261        </%namespace>
262
263        this is main.  ${comp.def1("hi", "there")}
264        ${comp.def2("there")}
265    """,
266        )
267
268        collection.put_string(
269            "defs.html",
270            """
271        <%def name="def1(s)">
272            def1: ${s}
273        </%def>
274
275        <%def name="def2(x)">
276            def2: ${x}
277        </%def>
278    """,
279        )
280
281        assert (
282            flatten_result(collection.get_template("main.html").render())
283            == "this is main. overridden def1 hi, there def2: there"
284        )
285
286    def test_getattr(self):
287        collection = lookup.TemplateLookup()
288        collection.put_string(
289            "main.html",
290            """
291            <%namespace name="foo" file="ns.html"/>
292            <%
293                 if hasattr(foo, 'lala'):
294                     foo.lala()
295                 if not hasattr(foo, 'hoho'):
296                     context.write('foo has no hoho.')
297            %>
298         """,
299        )
300        collection.put_string(
301            "ns.html",
302            """
303          <%def name="lala()">this is lala.</%def>
304        """,
305        )
306        assert (
307            flatten_result(collection.get_template("main.html").render())
308            == "this is lala.foo has no hoho."
309        )
310
311    def test_in_def(self):
312        collection = lookup.TemplateLookup()
313        collection.put_string(
314            "main.html",
315            """
316            <%namespace name="foo" file="ns.html"/>
317
318            this is main.  ${bar()}
319            <%def name="bar()">
320                this is bar, foo is ${foo.bar()}
321            </%def>
322        """,
323        )
324
325        collection.put_string(
326            "ns.html",
327            """
328            <%def name="bar()">
329                this is ns.html->bar
330            </%def>
331        """,
332        )
333
334        assert result_lines(collection.get_template("main.html").render()) == [
335            "this is main.",
336            "this is bar, foo is",
337            "this is ns.html->bar",
338        ]
339
340    def test_in_remote_def(self):
341        collection = lookup.TemplateLookup()
342        collection.put_string(
343            "main.html",
344            """
345            <%namespace name="foo" file="ns.html"/>
346
347            this is main.  ${bar()}
348            <%def name="bar()">
349                this is bar, foo is ${foo.bar()}
350            </%def>
351        """,
352        )
353
354        collection.put_string(
355            "ns.html",
356            """
357            <%def name="bar()">
358                this is ns.html->bar
359            </%def>
360        """,
361        )
362
363        collection.put_string(
364            "index.html",
365            """
366            <%namespace name="main" file="main.html"/>
367
368            this is index
369            ${main.bar()}
370        """,
371        )
372
373        assert result_lines(
374            collection.get_template("index.html").render()
375        ) == ["this is index", "this is bar, foo is", "this is ns.html->bar"]
376
377    def test_dont_pollute_self(self):
378        # test that get_namespace() doesn't modify the original context
379        # incompatibly
380
381        collection = lookup.TemplateLookup()
382        collection.put_string(
383            "base.html",
384            """
385
386        <%def name="foo()">
387        <%
388            foo = local.get_namespace("foo.html")
389        %>
390        </%def>
391
392        name: ${self.name}
393        name via bar: ${bar()}
394
395        ${next.body()}
396
397        name: ${self.name}
398        name via bar: ${bar()}
399        <%def name="bar()">
400            ${self.name}
401        </%def>
402
403
404        """,
405        )
406
407        collection.put_string(
408            "page.html",
409            """
410        <%inherit file="base.html"/>
411
412        ${self.foo()}
413
414        hello world
415
416        """,
417        )
418
419        collection.put_string("foo.html", """<%inherit file="base.html"/>""")
420        assert result_lines(collection.get_template("page.html").render()) == [
421            "name: self:page.html",
422            "name via bar:",
423            "self:page.html",
424            "hello world",
425            "name: self:page.html",
426            "name via bar:",
427            "self:page.html",
428        ]
429
430    def test_inheritance(self):
431        """test namespace initialization in a base inherited template that
432        doesnt otherwise access the namespace"""
433        collection = lookup.TemplateLookup()
434        collection.put_string(
435            "base.html",
436            """
437            <%namespace name="foo" file="ns.html" inheritable="True"/>
438
439            ${next.body()}
440""",
441        )
442        collection.put_string(
443            "ns.html",
444            """
445            <%def name="bar()">
446                this is ns.html->bar
447            </%def>
448        """,
449        )
450
451        collection.put_string(
452            "index.html",
453            """
454            <%inherit file="base.html"/>
455
456            this is index
457            ${self.foo.bar()}
458        """,
459        )
460
461        assert result_lines(
462            collection.get_template("index.html").render()
463        ) == ["this is index", "this is ns.html->bar"]
464
465    def test_inheritance_two(self):
466        collection = lookup.TemplateLookup()
467        collection.put_string(
468            "base.html",
469            """
470            <%def name="foo()">
471                base.foo
472            </%def>
473
474            <%def name="bat()">
475                base.bat
476            </%def>
477""",
478        )
479        collection.put_string(
480            "lib.html",
481            """
482            <%inherit file="base.html"/>
483            <%def name="bar()">
484                lib.bar
485                ${parent.foo()}
486                ${self.foo()}
487                ${parent.bat()}
488                ${self.bat()}
489            </%def>
490
491            <%def name="foo()">
492                lib.foo
493            </%def>
494
495        """,
496        )
497
498        collection.put_string(
499            "front.html",
500            """
501            <%namespace name="lib" file="lib.html"/>
502            ${lib.bar()}
503        """,
504        )
505
506        assert result_lines(
507            collection.get_template("front.html").render()
508        ) == ["lib.bar", "base.foo", "lib.foo", "base.bat", "base.bat"]
509
510    def test_attr(self):
511        l = lookup.TemplateLookup()
512
513        l.put_string(
514            "foo.html",
515            """
516        <%!
517            foofoo = "foo foo"
518            onlyfoo = "only foo"
519        %>
520        <%inherit file="base.html"/>
521        <%def name="setup()">
522            <%
523            self.attr.foolala = "foo lala"
524            %>
525        </%def>
526        ${self.attr.basefoo}
527        ${self.attr.foofoo}
528        ${self.attr.onlyfoo}
529        ${self.attr.lala}
530        ${self.attr.foolala}
531        """,
532        )
533
534        l.put_string(
535            "base.html",
536            """
537        <%!
538            basefoo = "base foo 1"
539            foofoo = "base foo 2"
540        %>
541        <%
542            self.attr.lala = "base lala"
543        %>
544
545        ${self.attr.basefoo}
546        ${self.attr.foofoo}
547        ${self.attr.onlyfoo}
548        ${self.attr.lala}
549        ${self.setup()}
550        ${self.attr.foolala}
551        body
552        ${self.body()}
553        """,
554        )
555
556        assert result_lines(l.get_template("foo.html").render()) == [
557            "base foo 1",
558            "foo foo",
559            "only foo",
560            "base lala",
561            "foo lala",
562            "body",
563            "base foo 1",
564            "foo foo",
565            "only foo",
566            "base lala",
567            "foo lala",
568        ]
569
570    def test_attr_raise(self):
571        l = lookup.TemplateLookup()
572
573        l.put_string(
574            "foo.html",
575            """
576            <%def name="foo()">
577            </%def>
578        """,
579        )
580
581        l.put_string(
582            "bar.html",
583            """
584        <%namespace name="foo" file="foo.html"/>
585
586        ${foo.notfoo()}
587        """,
588        )
589
590        assert_raises(AttributeError, l.get_template("bar.html").render)
591
592    def test_custom_tag_1(self):
593        template = Template(
594            """
595
596            <%def name="foo(x, y)">
597                foo: ${x} ${y}
598            </%def>
599
600            <%self:foo x="5" y="${7+8}"/>
601        """
602        )
603        assert result_lines(template.render()) == ["foo: 5 15"]
604
605    def test_custom_tag_2(self):
606        collection = lookup.TemplateLookup()
607        collection.put_string(
608            "base.html",
609            """
610            <%def name="foo(x, y)">
611                foo: ${x} ${y}
612            </%def>
613
614            <%def name="bat(g)"><%
615                return "the bat! %s" % g
616            %></%def>
617
618            <%def name="bar(x)">
619                ${caller.body(z=x)}
620            </%def>
621        """,
622        )
623
624        collection.put_string(
625            "index.html",
626            """
627            <%namespace name="myns" file="base.html"/>
628
629            <%myns:foo x="${'some x'}" y="some y"/>
630
631            <%myns:bar x="${myns.bat(10)}" args="z">
632                record: ${z}
633            </%myns:bar>
634
635        """,
636        )
637
638        assert result_lines(
639            collection.get_template("index.html").render()
640        ) == ["foo: some x some y", "record: the bat! 10"]
641
642    def test_custom_tag_3(self):
643        collection = lookup.TemplateLookup()
644        collection.put_string(
645            "base.html",
646            """
647            <%namespace name="foo" file="ns.html" inheritable="True"/>
648
649            ${next.body()}
650    """,
651        )
652        collection.put_string(
653            "ns.html",
654            """
655            <%def name="bar()">
656                this is ns.html->bar
657                caller body: ${caller.body()}
658            </%def>
659        """,
660        )
661
662        collection.put_string(
663            "index.html",
664            """
665            <%inherit file="base.html"/>
666
667            this is index
668            <%self.foo:bar>
669                call body
670            </%self.foo:bar>
671        """,
672        )
673
674        assert result_lines(
675            collection.get_template("index.html").render()
676        ) == [
677            "this is index",
678            "this is ns.html->bar",
679            "caller body:",
680            "call body",
681        ]
682
683    def test_custom_tag_case_sensitive(self):
684        t = Template(
685            """
686        <%def name="renderPanel()">
687            panel ${caller.body()}
688        </%def>
689
690        <%def name="renderTablePanel()">
691            <%self:renderPanel>
692                hi
693            </%self:renderPanel>
694        </%def>
695
696        <%self:renderTablePanel/>
697        """
698        )
699        assert result_lines(t.render()) == ["panel", "hi"]
700
701    def test_expr_grouping(self):
702        """test that parenthesis are placed around string-embedded
703        expressions."""
704
705        template = Template(
706            """
707            <%def name="bar(x, y)">
708                ${x}
709                ${y}
710            </%def>
711
712            <%self:bar x=" ${foo} " y="x${g and '1' or '2'}y"/>
713        """,
714            input_encoding="utf-8",
715        )
716
717        # the concat has to come out as "x + (g and '1' or '2') + y"
718        assert result_lines(template.render(foo="this is foo", g=False)) == [
719            "this is foo",
720            "x2y",
721        ]
722
723    def test_ccall(self):
724        collection = lookup.TemplateLookup()
725        collection.put_string(
726            "base.html",
727            """
728            <%namespace name="foo" file="ns.html" inheritable="True"/>
729
730            ${next.body()}
731    """,
732        )
733        collection.put_string(
734            "ns.html",
735            """
736            <%def name="bar()">
737                this is ns.html->bar
738                caller body: ${caller.body()}
739            </%def>
740        """,
741        )
742
743        collection.put_string(
744            "index.html",
745            """
746            <%inherit file="base.html"/>
747
748            this is index
749            <%call expr="self.foo.bar()">
750                call body
751            </%call>
752        """,
753        )
754
755        assert result_lines(
756            collection.get_template("index.html").render()
757        ) == [
758            "this is index",
759            "this is ns.html->bar",
760            "caller body:",
761            "call body",
762        ]
763
764    def test_ccall_2(self):
765        collection = lookup.TemplateLookup()
766        collection.put_string(
767            "base.html",
768            """
769            <%namespace name="foo" file="ns1.html" inheritable="True"/>
770
771            ${next.body()}
772    """,
773        )
774        collection.put_string(
775            "ns1.html",
776            """
777            <%namespace name="foo2" file="ns2.html"/>
778            <%def name="bar()">
779                <%call expr="foo2.ns2_bar()">
780                this is ns1.html->bar
781                caller body: ${caller.body()}
782                </%call>
783            </%def>
784        """,
785        )
786
787        collection.put_string(
788            "ns2.html",
789            """
790            <%def name="ns2_bar()">
791                this is ns2.html->bar
792                caller body: ${caller.body()}
793            </%def>
794        """,
795        )
796
797        collection.put_string(
798            "index.html",
799            """
800            <%inherit file="base.html"/>
801
802            this is index
803            <%call expr="self.foo.bar()">
804                call body
805            </%call>
806        """,
807        )
808
809        assert result_lines(
810            collection.get_template("index.html").render()
811        ) == [
812            "this is index",
813            "this is ns2.html->bar",
814            "caller body:",
815            "this is ns1.html->bar",
816            "caller body:",
817            "call body",
818        ]
819
820    def test_import(self):
821        collection = lookup.TemplateLookup()
822        collection.put_string(
823            "functions.html",
824            """
825            <%def name="foo()">
826                this is foo
827            </%def>
828
829            <%def name="bar()">
830                this is bar
831            </%def>
832
833            <%def name="lala()">
834                this is lala
835            </%def>
836        """,
837        )
838
839        collection.put_string(
840            "func2.html",
841            """
842            <%def name="a()">
843                this is a
844            </%def>
845            <%def name="b()">
846                this is b
847            </%def>
848        """,
849        )
850        collection.put_string(
851            "index.html",
852            """
853            <%namespace file="functions.html" import="*"/>
854            <%namespace file="func2.html" import="a, b"/>
855            ${foo()}
856            ${bar()}
857            ${lala()}
858            ${a()}
859            ${b()}
860            ${x}
861        """,
862        )
863
864        assert result_lines(
865            collection.get_template("index.html").render(
866                bar="this is bar", x="this is x"
867            )
868        ) == [
869            "this is foo",
870            "this is bar",
871            "this is lala",
872            "this is a",
873            "this is b",
874            "this is x",
875        ]
876
877    def test_import_calledfromdef(self):
878        l = lookup.TemplateLookup()
879        l.put_string(
880            "a",
881            """
882        <%def name="table()">
883            im table
884        </%def>
885        """,
886        )
887
888        l.put_string(
889            "b",
890            """
891        <%namespace file="a" import="table"/>
892
893        <%
894            def table2():
895                table()
896                return ""
897        %>
898
899        ${table2()}
900        """,
901        )
902
903        t = l.get_template("b")
904        assert flatten_result(t.render()) == "im table"
905
906    def test_closure_import(self):
907        collection = lookup.TemplateLookup()
908        collection.put_string(
909            "functions.html",
910            """
911            <%def name="foo()">
912                this is foo
913            </%def>
914
915            <%def name="bar()">
916                this is bar
917            </%def>
918        """,
919        )
920
921        collection.put_string(
922            "index.html",
923            """
924            <%namespace file="functions.html" import="*"/>
925            <%def name="cl1()">
926                ${foo()}
927            </%def>
928
929            <%def name="cl2()">
930                ${bar()}
931            </%def>
932
933            ${cl1()}
934            ${cl2()}
935        """,
936        )
937        assert result_lines(
938            collection.get_template("index.html").render(
939                bar="this is bar", x="this is x"
940            )
941        ) == ["this is foo", "this is bar"]
942
943    def test_import_local(self):
944        t = Template(
945            """
946            <%namespace import="*">
947                <%def name="foo()">
948                    this is foo
949                </%def>
950            </%namespace>
951
952            ${foo()}
953
954        """
955        )
956        assert flatten_result(t.render()) == "this is foo"
957
958    def test_ccall_import(self):
959        collection = lookup.TemplateLookup()
960        collection.put_string(
961            "functions.html",
962            """
963            <%def name="foo()">
964                this is foo
965            </%def>
966
967            <%def name="bar()">
968                this is bar.
969                ${caller.body()}
970                ${caller.lala()}
971            </%def>
972        """,
973        )
974
975        collection.put_string(
976            "index.html",
977            """
978            <%namespace name="func" file="functions.html" import="*"/>
979            <%call expr="bar()">
980                this is index embedded
981                foo is ${foo()}
982                <%def name="lala()">
983                     this is lala ${foo()}
984                </%def>
985            </%call>
986        """,
987        )
988        # print collection.get_template("index.html").code
989        # print collection.get_template("functions.html").code
990        assert result_lines(
991            collection.get_template("index.html").render()
992        ) == [
993            "this is bar.",
994            "this is index embedded",
995            "foo is",
996            "this is foo",
997            "this is lala",
998            "this is foo",
999        ]
1000
1001    def test_nonexistent_namespace_uri(self):
1002        collection = lookup.TemplateLookup()
1003        collection.put_string(
1004            "main.html",
1005            """
1006            <%namespace name="defs" file="eefs.html"/>
1007
1008            this is main.  ${defs.def1("hi")}
1009            ${defs.def2("there")}
1010""",
1011        )
1012
1013        collection.put_string(
1014            "defs.html",
1015            """
1016        <%def name="def1(s)">
1017            def1: ${s}
1018        </%def>
1019
1020        <%def name="def2(x)">
1021            def2: ${x}
1022        </%def>
1023""",
1024        )
1025
1026        assert_raises_message_with_given_cause(
1027            exceptions.TemplateLookupException,
1028            "Can't locate template for uri 'eefs.html",
1029            exceptions.TopLevelLookupException,
1030            collection.get_template("main.html").render,
1031        )
1032