1import unittest
2
3from mako import compat
4from mako import lookup
5from test.util import result_lines
6
7
8class InheritanceTest(unittest.TestCase):
9    def test_basic(self):
10        collection = lookup.TemplateLookup()
11
12        collection.put_string(
13            "main",
14            """
15<%inherit file="base"/>
16
17<%def name="header()">
18    main header.
19</%def>
20
21this is the content.
22""",
23        )
24
25        collection.put_string(
26            "base",
27            """
28This is base.
29
30header: ${self.header()}
31
32body: ${self.body()}
33
34footer: ${self.footer()}
35
36<%def name="footer()">
37    this is the footer. header again ${next.header()}
38</%def>
39""",
40        )
41
42        assert result_lines(collection.get_template("main").render()) == [
43            "This is base.",
44            "header:",
45            "main header.",
46            "body:",
47            "this is the content.",
48            "footer:",
49            "this is the footer. header again",
50            "main header.",
51        ]
52
53    def test_multilevel_nesting(self):
54        collection = lookup.TemplateLookup()
55
56        collection.put_string(
57            "main",
58            """
59<%inherit file="layout"/>
60<%def name="d()">main_d</%def>
61main_body ${parent.d()}
62full stack from the top:
63    ${self.name} ${parent.name} ${parent.context['parent'].name} """
64            """${parent.context['parent'].context['parent'].name}
65""",
66        )
67
68        collection.put_string(
69            "layout",
70            """
71<%inherit file="general"/>
72<%def name="d()">layout_d</%def>
73layout_body
74parent name: ${parent.name}
75${parent.d()}
76${parent.context['parent'].d()}
77${next.body()}
78""",
79        )
80
81        collection.put_string(
82            "general",
83            """
84<%inherit file="base"/>
85<%def name="d()">general_d</%def>
86general_body
87${next.d()}
88${next.context['next'].d()}
89${next.body()}
90""",
91        )
92        collection.put_string(
93            "base",
94            """
95base_body
96full stack from the base:
97    ${self.name} ${self.context['parent'].name} """
98            """${self.context['parent'].context['parent'].name} """
99            """${self.context['parent'].context['parent'].context['parent'].name}
100${next.body()}
101<%def name="d()">base_d</%def>
102""",
103        )
104
105        assert result_lines(collection.get_template("main").render()) == [
106            "base_body",
107            "full stack from the base:",
108            "self:main self:layout self:general self:base",
109            "general_body",
110            "layout_d",
111            "main_d",
112            "layout_body",
113            "parent name: self:general",
114            "general_d",
115            "base_d",
116            "main_body layout_d",
117            "full stack from the top:",
118            "self:main self:layout self:general self:base",
119        ]
120
121    def test_includes(self):
122        """test that an included template also has its full hierarchy
123        invoked."""
124        collection = lookup.TemplateLookup()
125
126        collection.put_string(
127            "base",
128            """
129        <%def name="a()">base_a</%def>
130        This is the base.
131        ${next.body()}
132        End base.
133""",
134        )
135
136        collection.put_string(
137            "index",
138            """
139        <%inherit file="base"/>
140        this is index.
141        a is: ${self.a()}
142        <%include file="secondary"/>
143""",
144        )
145
146        collection.put_string(
147            "secondary",
148            """
149        <%inherit file="base"/>
150        this is secondary.
151        a is: ${self.a()}
152""",
153        )
154
155        assert result_lines(collection.get_template("index").render()) == [
156            "This is the base.",
157            "this is index.",
158            "a is: base_a",
159            "This is the base.",
160            "this is secondary.",
161            "a is: base_a",
162            "End base.",
163            "End base.",
164        ]
165
166    def test_namespaces(self):
167        """test that templates used via <%namespace> have access to an
168        inheriting 'self', and that the full 'self' is also exported."""
169        collection = lookup.TemplateLookup()
170
171        collection.put_string(
172            "base",
173            """
174        <%def name="a()">base_a</%def>
175        <%def name="b()">base_b</%def>
176        This is the base.
177        ${next.body()}
178""",
179        )
180
181        collection.put_string(
182            "layout",
183            """
184        <%inherit file="base"/>
185        <%def name="a()">layout_a</%def>
186        This is the layout..
187        ${next.body()}
188""",
189        )
190
191        collection.put_string(
192            "index",
193            """
194        <%inherit file="base"/>
195        <%namespace name="sc" file="secondary"/>
196        this is index.
197        a is: ${self.a()}
198        sc.a is: ${sc.a()}
199        sc.b is: ${sc.b()}
200        sc.c is: ${sc.c()}
201        sc.body is: ${sc.body()}
202""",
203        )
204
205        collection.put_string(
206            "secondary",
207            """
208        <%inherit file="layout"/>
209        <%def name="c()">secondary_c.  a is ${self.a()} b is ${self.b()} """
210            """d is ${self.d()}</%def>
211        <%def name="d()">secondary_d.</%def>
212        this is secondary.
213        a is: ${self.a()}
214        c is: ${self.c()}
215""",
216        )
217
218        assert result_lines(collection.get_template("index").render()) == [
219            "This is the base.",
220            "this is index.",
221            "a is: base_a",
222            "sc.a is: layout_a",
223            "sc.b is: base_b",
224            "sc.c is: secondary_c. a is layout_a b is base_b d is "
225            "secondary_d.",
226            "sc.body is:",
227            "this is secondary.",
228            "a is: layout_a",
229            "c is: secondary_c. a is layout_a b is base_b d is secondary_d.",
230        ]
231
232    def test_pageargs(self):
233        collection = lookup.TemplateLookup()
234        collection.put_string(
235            "base",
236            """
237            this is the base.
238
239            <%
240            sorted_ = pageargs.items()
241            sorted_ = sorted(sorted_)
242            %>
243            pageargs: (type: ${type(pageargs)}) ${sorted_}
244            <%def name="foo()">
245                ${next.body(**context.kwargs)}
246            </%def>
247
248            ${foo()}
249        """,
250        )
251        collection.put_string(
252            "index",
253            """
254            <%inherit file="base"/>
255            <%page args="x, y, z=7"/>
256            print ${x}, ${y}, ${z}
257        """,
258        )
259
260        if compat.py3k:
261            assert result_lines(
262                collection.get_template("index").render_unicode(x=5, y=10)
263            ) == [
264                "this is the base.",
265                "pageargs: (type: <class 'dict'>) [('x', 5), ('y', 10)]",
266                "print 5, 10, 7",
267            ]
268        else:
269            assert result_lines(
270                collection.get_template("index").render_unicode(x=5, y=10)
271            ) == [
272                "this is the base.",
273                "pageargs: (type: <type 'dict'>) [('x', 5), ('y', 10)]",
274                "print 5, 10, 7",
275            ]
276
277    def test_pageargs_2(self):
278        collection = lookup.TemplateLookup()
279        collection.put_string(
280            "base",
281            """
282            this is the base.
283
284            ${next.body(**context.kwargs)}
285
286            <%def name="foo(**kwargs)">
287                ${next.body(**kwargs)}
288            </%def>
289
290            <%def name="bar(**otherargs)">
291                ${next.body(z=16, **context.kwargs)}
292            </%def>
293
294            ${foo(x=12, y=15, z=8)}
295            ${bar(x=19, y=17)}
296        """,
297        )
298        collection.put_string(
299            "index",
300            """
301            <%inherit file="base"/>
302            <%page args="x, y, z=7"/>
303            pageargs: ${x}, ${y}, ${z}
304        """,
305        )
306        assert result_lines(
307            collection.get_template("index").render(x=5, y=10)
308        ) == [
309            "this is the base.",
310            "pageargs: 5, 10, 7",
311            "pageargs: 12, 15, 8",
312            "pageargs: 5, 10, 16",
313        ]
314
315    def test_pageargs_err(self):
316        collection = lookup.TemplateLookup()
317        collection.put_string(
318            "base",
319            """
320            this is the base.
321            ${next.body()}
322        """,
323        )
324        collection.put_string(
325            "index",
326            """
327            <%inherit file="base"/>
328            <%page args="x, y, z=7"/>
329            print ${x}, ${y}, ${z}
330        """,
331        )
332        try:
333            print(collection.get_template("index").render(x=5, y=10))
334            assert False
335        except TypeError:
336            assert True
337
338    def test_toplevel(self):
339        collection = lookup.TemplateLookup()
340        collection.put_string(
341            "base",
342            """
343            this is the base.
344            ${next.body()}
345        """,
346        )
347        collection.put_string(
348            "index",
349            """
350            <%inherit file="base"/>
351            this is the body
352        """,
353        )
354        assert result_lines(collection.get_template("index").render()) == [
355            "this is the base.",
356            "this is the body",
357        ]
358        assert result_lines(
359            collection.get_template("index").get_def("body").render()
360        ) == ["this is the body"]
361
362    def test_dynamic(self):
363        collection = lookup.TemplateLookup()
364        collection.put_string(
365            "base",
366            """
367            this is the base.
368            ${next.body()}
369        """,
370        )
371        collection.put_string(
372            "index",
373            """
374            <%!
375                def dyn(context):
376                    if context.get('base', None) is not None:
377                        return 'base'
378                    else:
379                        return None
380            %>
381            <%inherit file="${dyn(context)}"/>
382            this is index.
383        """,
384        )
385        assert result_lines(collection.get_template("index").render()) == [
386            "this is index."
387        ]
388        assert result_lines(
389            collection.get_template("index").render(base=True)
390        ) == ["this is the base.", "this is index."]
391
392    def test_in_call(self):
393        collection = lookup.TemplateLookup()
394        collection.put_string(
395            "/layout.html",
396            """
397        Super layout!
398        <%call expr="self.grid()">
399            ${next.body()}
400        </%call>
401        Oh yea!
402
403        <%def name="grid()">
404            Parent grid
405                ${caller.body()}
406            End Parent
407        </%def>
408        """,
409        )
410
411        collection.put_string(
412            "/subdir/layout.html",
413            """
414        ${next.body()}
415        <%def name="grid()">
416           Subdir grid
417               ${caller.body()}
418           End subdir
419        </%def>
420        <%inherit file="/layout.html"/>
421        """,
422        )
423
424        collection.put_string(
425            "/subdir/renderedtemplate.html",
426            """
427        Holy smokes!
428        <%inherit file="/subdir/layout.html"/>
429        """,
430        )
431
432        assert result_lines(
433            collection.get_template("/subdir/renderedtemplate.html").render()
434        ) == [
435            "Super layout!",
436            "Subdir grid",
437            "Holy smokes!",
438            "End subdir",
439            "Oh yea!",
440        ]
441