1# -*- coding: utf-8 -*-
2
3import os
4import unittest
5
6from mako import compat
7from mako import exceptions
8from mako import runtime
9from mako import util
10from mako.compat import u
11from mako.ext.preprocessors import convert_comments
12from mako.lookup import TemplateLookup
13from mako.template import ModuleInfo
14from mako.template import ModuleTemplate
15from mako.template import Template
16from test import assert_raises
17from test import assert_raises_message
18from test import eq_
19from test import module_base
20from test import requires_python_2
21from test import template_base
22from test import TemplateTest
23from test.util import flatten_result
24from test.util import result_lines
25
26
27class ctx(object):
28    def __init__(self, a, b):
29        pass
30
31    def __enter__(self):
32        return self
33
34    def __exit__(self, *arg):
35        pass
36
37
38class EncodingTest(TemplateTest):
39    def test_escapes_html_tags(self):
40        from mako.exceptions import html_error_template
41
42        x = Template(
43            """
44        X:
45        <% raise Exception('<span style="color:red">Foobar</span>') %>
46        """
47        )
48
49        try:
50            x.render()
51        except:
52            # <h3>Exception: <span style="color:red">Foobar</span></h3>
53            markup = html_error_template().render(full=False, css=False)
54            if compat.py3k:
55                assert (
56                    '<span style="color:red">Foobar</span></h3>'.encode(
57                        "ascii"
58                    )
59                    not in markup
60                )
61                assert (
62                    "&lt;span style=&#34;color:red&#34;"
63                    "&gt;Foobar&lt;/span&gt;".encode("ascii") in markup
64                )
65            else:
66                assert (
67                    '<span style="color:red">Foobar</span></h3>' not in markup
68                )
69                assert (
70                    "&lt;span style=&#34;color:red&#34;"
71                    "&gt;Foobar&lt;/span&gt;" in markup
72                )
73
74    def test_unicode(self):
75        self._do_memory_test(
76            u(
77                "Alors vous imaginez ma surprise, au lever du jour, quand "
78                "une drôle de petite voix m’a réveillé. Elle disait: "
79                "« S’il vous plaît… dessine-moi un mouton! »"
80            ),
81            u(
82                "Alors vous imaginez ma surprise, au lever du jour, quand "
83                "une drôle de petite voix m’a réveillé. Elle disait: "
84                "« S’il vous plaît… dessine-moi un mouton! »"
85            ),
86        )
87
88    def test_encoding_doesnt_conflict(self):
89        self._do_memory_test(
90            u(
91                "Alors vous imaginez ma surprise, au lever du jour, quand "
92                "une drôle de petite voix m’a réveillé. Elle disait: "
93                "« S’il vous plaît… dessine-moi un mouton! »"
94            ),
95            u(
96                "Alors vous imaginez ma surprise, au lever du jour, quand "
97                "une drôle de petite voix m’a réveillé. Elle disait: "
98                "« S’il vous plaît… dessine-moi un mouton! »"
99            ),
100            output_encoding="utf-8",
101        )
102
103    def test_unicode_arg(self):
104        val = u(
105            "Alors vous imaginez ma surprise, au lever du jour, quand "
106            "une drôle de petite voix m’a réveillé. Elle disait: "
107            "« S’il vous plaît… dessine-moi un mouton! »"
108        )
109        self._do_memory_test(
110            "${val}",
111            u(
112                "Alors vous imaginez ma surprise, au lever du jour, quand "
113                "une drôle de petite voix m’a réveillé. Elle disait: "
114                "« S’il vous plaît… dessine-moi un mouton! »"
115            ),
116            template_args={"val": val},
117        )
118
119    def test_unicode_file(self):
120        self._do_file_test(
121            "unicode.html",
122            u(
123                "Alors vous imaginez ma surprise, au lever du jour, quand "
124                "une drôle de petite voix m’a réveillé. Elle disait: "
125                "« S’il vous plaît… dessine-moi un mouton! »"
126            ),
127        )
128
129    def test_unicode_file_code(self):
130        self._do_file_test(
131            "unicode_code.html",
132            u("""hi, drôle de petite voix m’a réveillé."""),
133            filters=flatten_result,
134        )
135
136    def test_unicode_file_lookup(self):
137        lookup = TemplateLookup(
138            directories=[template_base],
139            output_encoding="utf-8",
140            default_filters=["decode.utf8"],
141        )
142        if compat.py3k:
143            template = lookup.get_template("/chs_unicode_py3k.html")
144        else:
145            template = lookup.get_template("/chs_unicode.html")
146        eq_(
147            flatten_result(template.render_unicode(name="毛泽东")),
148            u("毛泽东 是 新中国的主席<br/> Welcome 你 to 北京."),
149        )
150
151    def test_unicode_bom(self):
152        self._do_file_test(
153            "bom.html",
154            u(
155                "Alors vous imaginez ma surprise, au lever du jour, quand "
156                "une drôle de petite voix m’a réveillé. Elle disait: "
157                "« S’il vous plaît… dessine-moi un mouton! »"
158            ),
159        )
160
161        self._do_file_test(
162            "bommagic.html",
163            u(
164                "Alors vous imaginez ma surprise, au lever du jour, quand "
165                "une drôle de petite voix m’a réveillé. Elle disait: "
166                "« S’il vous plaît… dessine-moi un mouton! »"
167            ),
168        )
169
170        self.assertRaises(
171            exceptions.CompileException,
172            Template,
173            filename=self._file_path("badbom.html"),
174            module_directory=module_base,
175        )
176
177    def test_unicode_memory(self):
178        val = u(
179            "Alors vous imaginez ma surprise, au lever du jour, quand "
180            "une drôle de petite voix m’a réveillé. Elle disait: "
181            "« S’il vous plaît… dessine-moi un mouton! »"
182        )
183        self._do_memory_test(
184            ("## -*- coding: utf-8 -*-\n" + val).encode("utf-8"),
185            u(
186                "Alors vous imaginez ma surprise, au lever du jour, quand "
187                "une drôle de petite voix m’a réveillé. Elle disait: "
188                "« S’il vous plaît… dessine-moi un mouton! »"
189            ),
190        )
191
192    def test_unicode_text(self):
193        val = u(
194            "<%text>Alors vous imaginez ma surprise, au lever du jour, quand "
195            "une drôle de petite voix m’a réveillé. Elle disait: "
196            "« S’il vous plaît… dessine-moi un mouton! »</%text>"
197        )
198        self._do_memory_test(
199            ("## -*- coding: utf-8 -*-\n" + val).encode("utf-8"),
200            u(
201                "Alors vous imaginez ma surprise, au lever du jour, quand "
202                "une drôle de petite voix m’a réveillé. Elle disait: "
203                "« S’il vous plaît… dessine-moi un mouton! »"
204            ),
205        )
206
207    def test_unicode_text_ccall(self):
208        val = u(
209            """
210        <%def name="foo()">
211            ${capture(caller.body)}
212        </%def>
213        <%call expr="foo()">
214        <%text>Alors vous imaginez ma surprise, au lever du jour,
215quand une drôle de petite voix m’a réveillé. Elle disait:
216« S’il vous plaît… dessine-moi un mouton! »</%text>
217        </%call>"""
218        )
219        self._do_memory_test(
220            ("## -*- coding: utf-8 -*-\n" + val).encode("utf-8"),
221            u(
222                "Alors vous imaginez ma surprise, au lever du jour, quand "
223                "une drôle de petite voix m’a réveillé. Elle disait: "
224                "« S’il vous plaît… dessine-moi un mouton! »"
225            ),
226            filters=flatten_result,
227        )
228
229    def test_unicode_literal_in_expr(self):
230        if compat.py3k:
231            self._do_memory_test(
232                u(
233                    "## -*- coding: utf-8 -*-\n"
234                    '${"Alors vous imaginez ma surprise, au lever du jour, '
235                    "quand une drôle de petite voix m’a réveillé. "
236                    "Elle disait: "
237                    '« S’il vous plaît… dessine-moi un mouton! »"}\n'
238                ).encode("utf-8"),
239                u(
240                    "Alors vous imaginez ma surprise, au lever du jour, "
241                    "quand une drôle de petite voix m’a réveillé. "
242                    "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
243                ),
244                filters=lambda s: s.strip(),
245            )
246        else:
247            self._do_memory_test(
248                u(
249                    "## -*- coding: utf-8 -*-\n"
250                    '${u"Alors vous imaginez ma surprise, au lever du jour, '
251                    "quand une drôle de petite voix m’a réveillé. "
252                    "Elle disait: « S’il vous plaît… dessine-moi un "
253                    'mouton! »"}'
254                ).encode("utf-8"),
255                u(
256                    "Alors vous imaginez ma surprise, au lever du jour, "
257                    "quand une drôle de petite voix m’a réveillé. "
258                    "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
259                ),
260                filters=lambda s: s.strip(),
261            )
262
263    def test_unicode_literal_in_expr_file(self):
264        self._do_file_test(
265            "unicode_expr.html",
266            u(
267                "Alors vous imaginez ma surprise, au lever du jour, "
268                "quand une drôle de petite voix m’a réveillé. "
269                "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
270            ),
271            lambda t: t.strip(),
272        )
273
274    def test_unicode_literal_in_code(self):
275        if compat.py3k:
276            self._do_memory_test(
277                u(
278                    """## -*- coding: utf-8 -*-
279                <%
280                    context.write("Alors vous imaginez ma surprise, au """
281                    """lever du jour, quand une drôle de petite voix m’a """
282                    """réveillé. Elle disait: """
283                    """« S’il vous plaît… dessine-moi un mouton! »")
284                %>
285                """
286                ).encode("utf-8"),
287                u(
288                    "Alors vous imaginez ma surprise, au lever du jour, "
289                    "quand une drôle de petite voix m’a réveillé. "
290                    "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
291                ),
292                filters=lambda s: s.strip(),
293            )
294        else:
295            self._do_memory_test(
296                u(
297                    """## -*- coding: utf-8 -*-
298                <%
299                    context.write(u"Alors vous imaginez ma surprise, """
300                    """au lever du jour, quand une drôle de petite voix """
301                    """m’a réveillé. Elle disait: « S’il vous plaît… """
302                    """dessine-moi un mouton! »")
303                %>
304                """
305                ).encode("utf-8"),
306                u(
307                    "Alors vous imaginez ma surprise, au lever du jour, "
308                    "quand une drôle de petite voix m’a réveillé. "
309                    "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
310                ),
311                filters=lambda s: s.strip(),
312            )
313
314    def test_unicode_literal_in_controlline(self):
315        if compat.py3k:
316            self._do_memory_test(
317                u(
318                    """## -*- coding: utf-8 -*-
319                <%
320                    x = "drôle de petite voix m’a réveillé."
321                %>
322                % if x=="drôle de petite voix m’a réveillé.":
323                    hi, ${x}
324                % endif
325                """
326                ).encode("utf-8"),
327                u("""hi, drôle de petite voix m’a réveillé."""),
328                filters=lambda s: s.strip(),
329            )
330        else:
331            self._do_memory_test(
332                u(
333                    """## -*- coding: utf-8 -*-
334                <%
335                    x = u"drôle de petite voix m’a réveillé."
336                %>
337                % if x==u"drôle de petite voix m’a réveillé.":
338                    hi, ${x}
339                % endif
340                """
341                ).encode("utf-8"),
342                u("""hi, drôle de petite voix m’a réveillé."""),
343                filters=lambda s: s.strip(),
344            )
345
346    def test_unicode_literal_in_tag(self):
347        self._do_file_test(
348            "unicode_arguments.html",
349            [
350                u("x is: drôle de petite voix m’a réveillé"),
351                u("x is: drôle de petite voix m’a réveillé"),
352                u("x is: drôle de petite voix m’a réveillé"),
353                u("x is: drôle de petite voix m’a réveillé"),
354            ],
355            filters=result_lines,
356        )
357
358        self._do_memory_test(
359            util.read_file(self._file_path("unicode_arguments.html")),
360            [
361                u("x is: drôle de petite voix m’a réveillé"),
362                u("x is: drôle de petite voix m’a réveillé"),
363                u("x is: drôle de petite voix m’a réveillé"),
364                u("x is: drôle de petite voix m’a réveillé"),
365            ],
366            filters=result_lines,
367        )
368
369    def test_unicode_literal_in_def(self):
370        if compat.py3k:
371            self._do_memory_test(
372                u(
373                    """## -*- coding: utf-8 -*-
374                <%def name="bello(foo, bar)">
375                Foo: ${ foo }
376                Bar: ${ bar }
377                </%def>
378                <%call expr="bello(foo='árvíztűrő tükörfúrógép', """
379                    """bar='ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP')">
380                </%call>"""
381                ).encode("utf-8"),
382                u(
383                    """Foo: árvíztűrő tükörfúrógép """
384                    """Bar: ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"""
385                ),
386                filters=flatten_result,
387            )
388
389            self._do_memory_test(
390                u(
391                    "## -*- coding: utf-8 -*-\n"
392                    """<%def name="hello(foo='árvíztűrő tükörfúrógép', """
393                    """bar='ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP')">\n"""
394                    "Foo: ${ foo }\n"
395                    "Bar: ${ bar }\n"
396                    "</%def>\n"
397                    "${ hello() }"
398                ).encode("utf-8"),
399                u(
400                    """Foo: árvíztűrő tükörfúrógép Bar: """
401                    """ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"""
402                ),
403                filters=flatten_result,
404            )
405        else:
406            self._do_memory_test(
407                u(
408                    """## -*- coding: utf-8 -*-
409                <%def name="bello(foo, bar)">
410                Foo: ${ foo }
411                Bar: ${ bar }
412                </%def>
413                <%call expr="bello(foo=u'árvíztűrő tükörfúrógép', """
414                    """bar=u'ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP')">
415                </%call>"""
416                ).encode("utf-8"),
417                u(
418                    """Foo: árvíztűrő tükörfúrógép Bar: """
419                    """ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"""
420                ),
421                filters=flatten_result,
422            )
423
424            self._do_memory_test(
425                u(
426                    """## -*- coding: utf-8 -*-
427                <%def name="hello(foo=u'árvíztűrő tükörfúrógép', """
428                    """bar=u'ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP')">
429                Foo: ${ foo }
430                Bar: ${ bar }
431                </%def>
432                ${ hello() }"""
433                ).encode("utf-8"),
434                u(
435                    """Foo: árvíztűrő tükörfúrógép Bar: """
436                    """ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"""
437                ),
438                filters=flatten_result,
439            )
440
441    def test_input_encoding(self):
442        """test the 'input_encoding' flag on Template, and that unicode
443            objects arent double-decoded"""
444
445        if compat.py3k:
446            self._do_memory_test(
447                u("hello ${f('śląsk')}"),
448                u("hello śląsk"),
449                input_encoding="utf-8",
450                template_args={"f": lambda x: x},
451            )
452
453            self._do_memory_test(
454                u("## -*- coding: utf-8 -*-\nhello ${f('śląsk')}"),
455                u("hello śląsk"),
456                template_args={"f": lambda x: x},
457            )
458        else:
459            self._do_memory_test(
460                u("hello ${f(u'śląsk')}"),
461                u("hello śląsk"),
462                input_encoding="utf-8",
463                template_args={"f": lambda x: x},
464            )
465
466            self._do_memory_test(
467                u("## -*- coding: utf-8 -*-\nhello ${f(u'śląsk')}"),
468                u("hello śląsk"),
469                template_args={"f": lambda x: x},
470            )
471
472    def test_raw_strings(self):
473        """test that raw strings go straight thru with default_filters
474        turned off, bytestring_passthrough enabled.
475
476        """
477
478        self._do_memory_test(
479            u("## -*- coding: utf-8 -*-\nhello ${x}"),
480            "hello śląsk",
481            default_filters=[],
482            template_args={"x": "śląsk"},
483            unicode_=False,
484            bytestring_passthrough=True,
485            output_encoding=None,  # 'ascii'
486        )
487
488        # now, the way you *should* be doing it....
489        self._do_memory_test(
490            u("## -*- coding: utf-8 -*-\nhello ${x}"),
491            u("hello śląsk"),
492            template_args={"x": u("śląsk")},
493        )
494
495    def test_encoding(self):
496        self._do_memory_test(
497            u(
498                "Alors vous imaginez ma surprise, au lever du jour, quand "
499                "une drôle de petite voix m’a réveillé. Elle disait: "
500                "« S’il vous plaît… dessine-moi un mouton! »"
501            ),
502            u(
503                "Alors vous imaginez ma surprise, au lever du jour, quand "
504                "une drôle de petite voix m’a réveillé. Elle disait: "
505                "« S’il vous plaît… dessine-moi un mouton! »"
506            ).encode("utf-8"),
507            output_encoding="utf-8",
508            unicode_=False,
509        )
510
511    def test_encoding_errors(self):
512        self._do_memory_test(
513            u(
514                """KGB (transliteration of "КГБ") is the Russian-language """
515                """abbreviation for Committee for State Security, """
516                """(Russian: Комит́ет Госуд́арственной Безоп́асности """
517                """(help·info); Komitet Gosudarstvennoy Bezopasnosti)"""
518            ),
519            u(
520                """KGB (transliteration of "КГБ") is the Russian-language """
521                """abbreviation for Committee for State Security, """
522                """(Russian: Комит́ет Госуд́арственной Безоп́асности """
523                """(help·info); Komitet Gosudarstvennoy Bezopasnosti)"""
524            ).encode("iso-8859-1", "replace"),
525            output_encoding="iso-8859-1",
526            encoding_errors="replace",
527            unicode_=False,
528        )
529
530    def test_read_unicode(self):
531        lookup = TemplateLookup(
532            directories=[template_base],
533            filesystem_checks=True,
534            output_encoding="utf-8",
535        )
536        if compat.py3k:
537            template = lookup.get_template("/read_unicode_py3k.html")
538        else:
539            template = lookup.get_template("/read_unicode.html")
540        # TODO: I've no idea what encoding this file is, Python 3.1.2
541        # won't read the file even with open(...encoding='utf-8') unless
542        # errors is specified.   or if there's some quirk in 3.1.2
543        # since I'm pretty sure this test worked with py3k when I wrote it.
544        template.render(
545            path=self._file_path("internationalization.html")
546        )
547
548    @requires_python_2
549    def test_bytestring_passthru(self):
550        self._do_file_test(
551            "chs_utf8.html",
552            "毛泽东 是 新中国的主席<br/> Welcome 你 to 北京. Welcome 你 to 北京.",
553            default_filters=[],
554            disable_unicode=True,
555            output_encoding=None,
556            template_args={"name": "毛泽东"},
557            filters=flatten_result,
558            unicode_=False,
559        )
560
561        self._do_file_test(
562            "chs_utf8.html",
563            "毛泽东 是 新中国的主席<br/> Welcome 你 to 北京. Welcome 你 to 北京.",
564            disable_unicode=True,
565            output_encoding=None,
566            template_args={"name": "毛泽东"},
567            filters=flatten_result,
568            unicode_=False,
569        )
570
571        template = self._file_template(
572            "chs_utf8.html", output_encoding=None, disable_unicode=True
573        )
574        self.assertRaises(
575            UnicodeDecodeError, template.render_unicode, name="毛泽东"
576        )
577
578        template = Template(
579            "${'Alors vous imaginez ma surprise, au lever"
580            " du jour, quand une drôle de petite voix m’a "
581            "réveillé. Elle disait: « S’il vous plaît… "
582            "dessine-moi un mouton! »'}",
583            output_encoding=None,
584            disable_unicode=True,
585            input_encoding="utf-8",
586        )
587        assert (
588            template.render() == "Alors vous imaginez ma surprise, "
589            "au lever du jour, quand une drôle de petite "
590            "voix m’a réveillé. Elle disait: « S’il vous "
591            "plaît… dessine-moi un mouton! »"
592        )
593        template = Template(
594            "${'Alors vous imaginez ma surprise, au "
595            "lever du jour, quand une drôle de petite "
596            "voix m’a réveillé. Elle disait: « S’il "
597            "vous plaît… dessine-moi un mouton! »'}",
598            input_encoding="utf8",
599            output_encoding="utf8",
600            disable_unicode=False,
601            default_filters=[],
602        )
603        # raises because expression contains an encoded bytestring which cannot
604        # be decoded
605        self.assertRaises(UnicodeDecodeError, template.render)
606
607
608class PageArgsTest(TemplateTest):
609    def test_basic(self):
610        template = Template(
611            """
612            <%page args="x, y, z=7"/>
613
614            this is page, ${x}, ${y}, ${z}
615"""
616        )
617
618        assert (
619            flatten_result(template.render(x=5, y=10))
620            == "this is page, 5, 10, 7"
621        )
622        assert (
623            flatten_result(template.render(x=5, y=10, z=32))
624            == "this is page, 5, 10, 32"
625        )
626        assert_raises(TypeError, template.render, y=10)
627
628    def test_inherits(self):
629        lookup = TemplateLookup()
630        lookup.put_string(
631            "base.tmpl",
632            """
633        <%page args="bar" />
634        ${bar}
635        ${pageargs['foo']}
636        ${self.body(**pageargs)}
637        """,
638        )
639        lookup.put_string(
640            "index.tmpl",
641            """
642        <%inherit file="base.tmpl" />
643        <%page args="variable" />
644        ${variable}
645        """,
646        )
647
648        self._do_test(
649            lookup.get_template("index.tmpl"),
650            "bar foo var",
651            filters=flatten_result,
652            template_args={"variable": "var", "bar": "bar", "foo": "foo"},
653        )
654
655    def test_includes(self):
656        lookup = TemplateLookup()
657        lookup.put_string(
658            "incl1.tmpl",
659            """
660        <%page args="bar" />
661        ${bar}
662        ${pageargs['foo']}
663        """,
664        )
665        lookup.put_string(
666            "incl2.tmpl",
667            """
668        ${pageargs}
669        """,
670        )
671        lookup.put_string(
672            "index.tmpl",
673            """
674        <%include file="incl1.tmpl" args="**pageargs"/>
675        <%page args="variable" />
676        ${variable}
677        <%include file="incl2.tmpl" />
678        """,
679        )
680
681        self._do_test(
682            lookup.get_template("index.tmpl"),
683            "bar foo var {}",
684            filters=flatten_result,
685            template_args={"variable": "var", "bar": "bar", "foo": "foo"},
686        )
687
688    def test_context_small(self):
689        ctx = runtime.Context([].append, x=5, y=4)
690        eq_(sorted(ctx.keys()), ["caller", "capture", "x", "y"])
691
692    def test_with_context(self):
693        template = Template(
694            """
695            <%page args="x, y, z=7"/>
696
697            this is page, ${x}, ${y}, ${z}, ${w}
698"""
699        )
700        # print template.code
701        assert (
702            flatten_result(template.render(x=5, y=10, w=17))
703            == "this is page, 5, 10, 7, 17"
704        )
705
706    def test_overrides_builtins(self):
707        template = Template(
708            """
709            <%page args="id"/>
710
711            this is page, id is ${id}
712        """
713        )
714
715        assert (
716            flatten_result(template.render(id="im the id"))
717            == "this is page, id is im the id"
718        )
719
720    def test_canuse_builtin_names(self):
721        template = Template(
722            """
723            exception: ${Exception}
724            id: ${id}
725        """
726        )
727        assert (
728            flatten_result(
729                template.render(id="some id", Exception="some exception")
730            )
731            == "exception: some exception id: some id"
732        )
733
734    def test_builtin_names_dont_clobber_defaults_in_includes(self):
735        lookup = TemplateLookup()
736        lookup.put_string(
737            "test.mako",
738            """
739        <%include file="test1.mako"/>
740
741        """,
742        )
743
744        lookup.put_string(
745            "test1.mako",
746            """
747        <%page args="id='foo'"/>
748
749        ${id}
750        """,
751        )
752
753        for template in ("test.mako", "test1.mako"):
754            assert (
755                flatten_result(lookup.get_template(template).render()) == "foo"
756            )
757            assert (
758                flatten_result(lookup.get_template(template).render(id=5))
759                == "5"
760            )
761            assert (
762                flatten_result(lookup.get_template(template).render(id=id))
763                == "<built-in function id>"
764            )
765
766    def test_dict_locals(self):
767        template = Template(
768            """
769            <%
770                dict = "this is dict"
771                locals = "this is locals"
772            %>
773            dict: ${dict}
774            locals: ${locals}
775        """
776        )
777        assert (
778            flatten_result(template.render())
779            == "dict: this is dict locals: this is locals"
780        )
781
782
783class IncludeTest(TemplateTest):
784    def test_basic(self):
785        lookup = TemplateLookup()
786        lookup.put_string(
787            "a",
788            """
789            this is a
790            <%include file="b" args="a=3,b=4,c=5"/>
791        """,
792        )
793        lookup.put_string(
794            "b",
795            """
796            <%page args="a,b,c"/>
797            this is b.  ${a}, ${b}, ${c}
798        """,
799        )
800        assert (
801            flatten_result(lookup.get_template("a").render())
802            == "this is a this is b. 3, 4, 5"
803        )
804
805    def test_localargs(self):
806        lookup = TemplateLookup()
807        lookup.put_string(
808            "a",
809            """
810            this is a
811            <%include file="b" args="a=a,b=b,c=5"/>
812        """,
813        )
814        lookup.put_string(
815            "b",
816            """
817            <%page args="a,b,c"/>
818            this is b.  ${a}, ${b}, ${c}
819        """,
820        )
821        assert (
822            flatten_result(lookup.get_template("a").render(a=7, b=8))
823            == "this is a this is b. 7, 8, 5"
824        )
825
826    def test_viakwargs(self):
827        lookup = TemplateLookup()
828        lookup.put_string(
829            "a",
830            """
831            this is a
832            <%include file="b" args="c=5, **context.kwargs"/>
833        """,
834        )
835        lookup.put_string(
836            "b",
837            """
838            <%page args="a,b,c"/>
839            this is b.  ${a}, ${b}, ${c}
840        """,
841        )
842        # print lookup.get_template("a").code
843        assert (
844            flatten_result(lookup.get_template("a").render(a=7, b=8))
845            == "this is a this is b. 7, 8, 5"
846        )
847
848    def test_include_withargs(self):
849        lookup = TemplateLookup()
850        lookup.put_string(
851            "a",
852            """
853            this is a
854            <%include file="${i}" args="c=5, **context.kwargs"/>
855        """,
856        )
857        lookup.put_string(
858            "b",
859            """
860            <%page args="a,b,c"/>
861            this is b.  ${a}, ${b}, ${c}
862        """,
863        )
864        assert (
865            flatten_result(lookup.get_template("a").render(a=7, b=8, i="b"))
866            == "this is a this is b. 7, 8, 5"
867        )
868
869    def test_within_ccall(self):
870        lookup = TemplateLookup()
871        lookup.put_string("a", """this is a""")
872        lookup.put_string(
873            "b",
874            """
875        <%def name="bar()">
876            bar: ${caller.body()}
877            <%include file="a"/>
878        </%def>
879        """,
880        )
881        lookup.put_string(
882            "c",
883            """
884        <%namespace name="b" file="b"/>
885        <%b:bar>
886            calling bar
887        </%b:bar>
888        """,
889        )
890        assert (
891            flatten_result(lookup.get_template("c").render())
892            == "bar: calling bar this is a"
893        )
894
895    def test_include_error_handler(self):
896        def handle(context, error):
897            context.write("include error")
898            return True
899
900        lookup = TemplateLookup(include_error_handler=handle)
901        lookup.put_string(
902            "a",
903            """
904            this is a.
905            <%include file="b"/>
906        """,
907        )
908        lookup.put_string(
909            "b",
910            """
911            this is b ${1/0} end.
912        """,
913        )
914        assert (
915            flatten_result(lookup.get_template("a").render())
916            == "this is a. this is b include error"
917        )
918
919
920class UndefinedVarsTest(TemplateTest):
921    def test_undefined(self):
922        t = Template(
923            """
924            % if x is UNDEFINED:
925                undefined
926            % else:
927                x: ${x}
928            % endif
929        """
930        )
931
932        assert result_lines(t.render(x=12)) == ["x: 12"]
933        assert result_lines(t.render(y=12)) == ["undefined"]
934
935    def test_strict(self):
936        t = Template(
937            """
938            % if x is UNDEFINED:
939                undefined
940            % else:
941                x: ${x}
942            % endif
943        """,
944            strict_undefined=True,
945        )
946
947        assert result_lines(t.render(x=12)) == ["x: 12"]
948
949        assert_raises(NameError, t.render, y=12)
950
951        l = TemplateLookup(strict_undefined=True)
952        l.put_string("a", "some template")
953        l.put_string(
954            "b",
955            """
956            <%namespace name='a' file='a' import='*'/>
957            % if x is UNDEFINED:
958                undefined
959            % else:
960                x: ${x}
961            % endif
962        """,
963        )
964
965        assert result_lines(t.render(x=12)) == ["x: 12"]
966
967        assert_raises(NameError, t.render, y=12)
968
969    def test_expression_declared(self):
970        t = Template(
971            """
972            ${",".join([t for t in ("a", "b", "c")])}
973        """,
974            strict_undefined=True,
975        )
976
977        eq_(result_lines(t.render()), ["a,b,c"])
978
979        t = Template(
980            """
981            <%self:foo value="${[(val, n) for val, n in [(1, 2)]]}"/>
982
983            <%def name="foo(value)">
984                ${value}
985            </%def>
986
987        """,
988            strict_undefined=True,
989        )
990
991        eq_(result_lines(t.render()), ["[(1, 2)]"])
992
993        t = Template(
994            """
995            <%call expr="foo(value=[(val, n) for val, n in [(1, 2)]])" />
996
997            <%def name="foo(value)">
998                ${value}
999            </%def>
1000
1001        """,
1002            strict_undefined=True,
1003        )
1004
1005        eq_(result_lines(t.render()), ["[(1, 2)]"])
1006
1007        l = TemplateLookup(strict_undefined=True)
1008        l.put_string("i", "hi, ${pageargs['y']}")
1009        l.put_string(
1010            "t",
1011            """
1012            <%include file="i" args="y=[x for x in range(3)]" />
1013        """,
1014        )
1015        eq_(result_lines(l.get_template("t").render()), ["hi, [0, 1, 2]"])
1016
1017        l.put_string(
1018            "q",
1019            """
1020            <%namespace name="i" file="${(str([x for x in range(3)][2]) + """
1021            """'i')[-1]}" />
1022            ${i.body(y='x')}
1023        """,
1024        )
1025        eq_(result_lines(l.get_template("q").render()), ["hi, x"])
1026
1027        t = Template(
1028            """
1029            <%
1030                y = lambda q: str(q)
1031            %>
1032            ${y('hi')}
1033        """,
1034            strict_undefined=True,
1035        )
1036        eq_(result_lines(t.render()), ["hi"])
1037
1038    def test_list_comprehensions_plus_undeclared_nonstrict(self):
1039        # traditional behavior.  variable inside a list comprehension
1040        # is treated as an "undefined", so is pulled from the context.
1041        t = Template(
1042            """
1043            t is: ${t}
1044
1045            ${",".join([t for t in ("a", "b", "c")])}
1046        """
1047        )
1048
1049        eq_(result_lines(t.render(t="T")), ["t is: T", "a,b,c"])
1050
1051    def test_traditional_assignment_plus_undeclared(self):
1052        t = Template(
1053            """
1054            t is: ${t}
1055
1056            <%
1057                t = 12
1058            %>
1059        """
1060        )
1061        assert_raises(UnboundLocalError, t.render, t="T")
1062
1063    def test_list_comprehensions_plus_undeclared_strict(self):
1064        # with strict, a list comprehension now behaves
1065        # like the undeclared case above.
1066        t = Template(
1067            """
1068            t is: ${t}
1069
1070            ${",".join([t for t in ("a", "b", "c")])}
1071        """,
1072            strict_undefined=True,
1073        )
1074
1075        eq_(result_lines(t.render(t="T")), ["t is: T", "a,b,c"])
1076
1077
1078class StopRenderingTest(TemplateTest):
1079    def test_return_in_template(self):
1080        t = Template(
1081            """
1082           Line one
1083           <% return STOP_RENDERING %>
1084           Line Three
1085        """,
1086            strict_undefined=True,
1087        )
1088
1089        eq_(result_lines(t.render()), ["Line one"])
1090
1091
1092class ReservedNameTest(TemplateTest):
1093    def test_names_on_context(self):
1094        for name in ("context", "loop", "UNDEFINED", "STOP_RENDERING"):
1095            assert_raises_message(
1096                exceptions.NameConflictError,
1097                r"Reserved words passed to render\(\): %s" % name,
1098                Template("x").render,
1099                **{name: "foo"}
1100            )
1101
1102    def test_names_in_template(self):
1103        for name in ("context", "loop", "UNDEFINED", "STOP_RENDERING"):
1104            assert_raises_message(
1105                exceptions.NameConflictError,
1106                r"Reserved words declared in template: %s" % name,
1107                Template,
1108                "<%% %s = 5 %%>" % name,
1109            )
1110
1111    def test_exclude_loop_context(self):
1112        self._do_memory_test(
1113            "loop is ${loop}",
1114            "loop is 5",
1115            template_args=dict(loop=5),
1116            enable_loop=False,
1117        )
1118
1119    def test_exclude_loop_template(self):
1120        self._do_memory_test(
1121            "<% loop = 12 %>loop is ${loop}", "loop is 12", enable_loop=False
1122        )
1123
1124
1125class ControlTest(TemplateTest):
1126    def test_control(self):
1127        t = Template(
1128            """
1129    ## this is a template.
1130    % for x in y:
1131    %   if 'test' in x:
1132        yes x has test
1133    %   else:
1134        no x does not have test
1135    %endif
1136    %endfor
1137"""
1138        )
1139        assert result_lines(
1140            t.render(
1141                y=[
1142                    {"test": "one"},
1143                    {"foo": "bar"},
1144                    {"foo": "bar", "test": "two"},
1145                ]
1146            )
1147        ) == ["yes x has test", "no x does not have test", "yes x has test"]
1148
1149    def test_blank_control_1(self):
1150        self._do_memory_test(
1151            """
1152            % if True:
1153            % endif
1154            """,
1155            "",
1156            filters=lambda s: s.strip(),
1157        )
1158
1159    def test_blank_control_2(self):
1160        self._do_memory_test(
1161            """
1162            % if True:
1163            % elif True:
1164            % endif
1165            """,
1166            "",
1167            filters=lambda s: s.strip(),
1168        )
1169
1170    def test_blank_control_3(self):
1171        self._do_memory_test(
1172            """
1173            % if True:
1174            % else:
1175            % endif
1176            """,
1177            "",
1178            filters=lambda s: s.strip(),
1179        )
1180
1181    def test_blank_control_4(self):
1182        self._do_memory_test(
1183            """
1184            % if True:
1185            % elif True:
1186            % else:
1187            % endif
1188            """,
1189            "",
1190            filters=lambda s: s.strip(),
1191        )
1192
1193    def test_blank_control_5(self):
1194        self._do_memory_test(
1195            """
1196            % for x in range(10):
1197            % endfor
1198            """,
1199            "",
1200            filters=lambda s: s.strip(),
1201        )
1202
1203    def test_blank_control_6(self):
1204        self._do_memory_test(
1205            """
1206            % while False:
1207            % endwhile
1208            """,
1209            "",
1210            filters=lambda s: s.strip(),
1211        )
1212
1213    def test_blank_control_7(self):
1214        self._do_memory_test(
1215            """
1216            % try:
1217            % except:
1218            % endtry
1219            """,
1220            "",
1221            filters=lambda s: s.strip(),
1222        )
1223
1224    def test_blank_control_8(self):
1225        self._do_memory_test(
1226            """
1227            % with ctx('x', 'w') as fp:
1228            % endwith
1229            """,
1230            "",
1231            filters=lambda s: s.strip(),
1232            template_args={"ctx": ctx},
1233        )
1234
1235    def test_commented_blank_control_1(self):
1236        self._do_memory_test(
1237            """
1238            % if True:
1239            ## comment
1240            % endif
1241            """,
1242            "",
1243            filters=lambda s: s.strip(),
1244        )
1245
1246    def test_commented_blank_control_2(self):
1247        self._do_memory_test(
1248            """
1249            % if True:
1250            ## comment
1251            % elif True:
1252            ## comment
1253            % endif
1254            """,
1255            "",
1256            filters=lambda s: s.strip(),
1257        )
1258
1259    def test_commented_blank_control_3(self):
1260        self._do_memory_test(
1261            """
1262            % if True:
1263            ## comment
1264            % else:
1265            ## comment
1266            % endif
1267            """,
1268            "",
1269            filters=lambda s: s.strip(),
1270        )
1271
1272    def test_commented_blank_control_4(self):
1273        self._do_memory_test(
1274            """
1275            % if True:
1276            ## comment
1277            % elif True:
1278            ## comment
1279            % else:
1280            ## comment
1281            % endif
1282            """,
1283            "",
1284            filters=lambda s: s.strip(),
1285        )
1286
1287    def test_commented_blank_control_5(self):
1288        self._do_memory_test(
1289            """
1290            % for x in range(10):
1291            ## comment
1292            % endfor
1293            """,
1294            "",
1295            filters=lambda s: s.strip(),
1296        )
1297
1298    def test_commented_blank_control_6(self):
1299        self._do_memory_test(
1300            """
1301            % while False:
1302            ## comment
1303            % endwhile
1304            """,
1305            "",
1306            filters=lambda s: s.strip(),
1307        )
1308
1309    def test_commented_blank_control_7(self):
1310        self._do_memory_test(
1311            """
1312            % try:
1313            ## comment
1314            % except:
1315            ## comment
1316            % endtry
1317            """,
1318            "",
1319            filters=lambda s: s.strip(),
1320        )
1321
1322    def test_commented_blank_control_8(self):
1323        self._do_memory_test(
1324            """
1325            % with ctx('x', 'w') as fp:
1326            ## comment
1327            % endwith
1328            """,
1329            "",
1330            filters=lambda s: s.strip(),
1331            template_args={"ctx": ctx},
1332        )
1333
1334    def test_multiline_control(self):
1335        t = Template(
1336            """
1337    % for x in \\
1338        [y for y in [1,2,3]]:
1339        ${x}
1340    % endfor
1341"""
1342        )
1343        # print t.code
1344        assert flatten_result(t.render()) == "1 2 3"
1345
1346
1347class GlobalsTest(TemplateTest):
1348    def test_globals(self):
1349        self._do_memory_test(
1350            """
1351                <%!
1352                    y = "hi"
1353                %>
1354            y is ${y}
1355            """,
1356            "y is hi",
1357            filters=lambda t: t.strip(),
1358        )
1359
1360
1361class RichTracebackTest(TemplateTest):
1362    def _do_test_traceback(self, utf8, memory, syntax):
1363        if memory:
1364            if syntax:
1365                source = u(
1366                    '## coding: utf-8\n<% print "m’a réveillé. '
1367                    "Elle disait: « S’il vous plaît… dessine-moi "
1368                    "un mouton! » %>"
1369                )
1370            else:
1371                source = u(
1372                    '## coding: utf-8\n<% print u"m’a réveillé. '
1373                    "Elle disait: « S’il vous plaît… dessine-moi un "
1374                    'mouton! »" + str(5/0) %>'
1375                )
1376            if utf8:
1377                source = source.encode("utf-8")
1378            else:
1379                source = source
1380            templateargs = {"text": source}
1381        else:
1382            if syntax:
1383                filename = "unicode_syntax_error.html"
1384            else:
1385                filename = "unicode_runtime_error.html"
1386            source = util.read_file(self._file_path(filename), "rb")
1387            if not utf8:
1388                source = source.decode("utf-8")
1389            templateargs = {"filename": self._file_path(filename)}
1390        try:
1391            template = Template(**templateargs)
1392            if not syntax:
1393                template.render_unicode()
1394            assert False
1395        except Exception:
1396            tback = exceptions.RichTraceback()
1397            if utf8:
1398                assert tback.source == source.decode("utf-8")
1399            else:
1400                assert tback.source == source
1401
1402
1403for utf8 in (True, False):
1404    for memory in (True, False):
1405        for syntax in (True, False):
1406
1407            def _do_test(self):
1408                self._do_test_traceback(utf8, memory, syntax)
1409
1410            name = "test_%s_%s_%s" % (
1411                utf8 and "utf8" or "unicode",
1412                memory and "memory" or "file",
1413                syntax and "syntax" or "runtime",
1414            )
1415            _do_test.__name__ = name
1416            setattr(RichTracebackTest, name, _do_test)
1417            del _do_test
1418
1419
1420class ModuleDirTest(TemplateTest):
1421    def tearDown(self):
1422        import shutil
1423
1424        shutil.rmtree(module_base, True)
1425
1426    def test_basic(self):
1427        t = self._file_template("modtest.html")
1428        t2 = self._file_template("subdir/modtest.html")
1429
1430        eq_(t.module.__file__, os.path.join(module_base, "modtest.html.py"))
1431        eq_(
1432            t2.module.__file__,
1433            os.path.join(module_base, "subdir", "modtest.html.py"),
1434        )
1435
1436    def test_callable(self):
1437        def get_modname(filename, uri):
1438            return os.path.join(
1439                module_base,
1440                os.path.dirname(uri)[1:],
1441                "foo",
1442                os.path.basename(filename) + ".py",
1443            )
1444
1445        lookup = TemplateLookup(template_base, modulename_callable=get_modname)
1446        t = lookup.get_template("/modtest.html")
1447        t2 = lookup.get_template("/subdir/modtest.html")
1448        eq_(
1449            t.module.__file__,
1450            os.path.join(module_base, "foo", "modtest.html.py"),
1451        )
1452        eq_(
1453            t2.module.__file__,
1454            os.path.join(module_base, "subdir", "foo", "modtest.html.py"),
1455        )
1456
1457    def test_custom_writer(self):
1458        canary = []
1459
1460        def write_module(source, outputpath):
1461            f = open(outputpath, "wb")
1462            canary.append(outputpath)
1463            f.write(source)
1464            f.close()
1465
1466        lookup = TemplateLookup(
1467            template_base,
1468            module_writer=write_module,
1469            module_directory=module_base,
1470        )
1471        lookup.get_template("/modtest.html")
1472        lookup.get_template("/subdir/modtest.html")
1473        eq_(
1474            canary,
1475            [
1476                os.path.join(module_base, "modtest.html.py"),
1477                os.path.join(module_base, "subdir", "modtest.html.py"),
1478            ],
1479        )
1480
1481
1482class FilenameToURITest(TemplateTest):
1483    def test_windows_paths(self):
1484        """test that windows filenames are handled appropriately by
1485        Template."""
1486
1487        current_path = os.path
1488        import ntpath
1489
1490        os.path = ntpath
1491        try:
1492
1493            class NoCompileTemplate(Template):
1494                def _compile_from_file(self, path, filename):
1495                    self.path = path
1496                    return Template("foo bar").module
1497
1498            t1 = NoCompileTemplate(
1499                filename="c:\\foo\\template.html",
1500                module_directory="c:\\modules\\",
1501            )
1502
1503            eq_(t1.uri, "/foo/template.html")
1504            eq_(t1.path, "c:\\modules\\foo\\template.html.py")
1505
1506            t1 = NoCompileTemplate(
1507                filename="c:\\path\\to\\templates\\template.html",
1508                uri="/bar/template.html",
1509                module_directory="c:\\modules\\",
1510            )
1511
1512            eq_(t1.uri, "/bar/template.html")
1513            eq_(t1.path, "c:\\modules\\bar\\template.html.py")
1514
1515        finally:
1516            os.path = current_path
1517
1518    def test_posix_paths(self):
1519        """test that posixs filenames are handled appropriately by Template."""
1520
1521        current_path = os.path
1522        import posixpath
1523
1524        os.path = posixpath
1525        try:
1526
1527            class NoCompileTemplate(Template):
1528                def _compile_from_file(self, path, filename):
1529                    self.path = path
1530                    return Template("foo bar").module
1531
1532            t1 = NoCompileTemplate(
1533                filename="/var/www/htdocs/includes/template.html",
1534                module_directory="/var/lib/modules",
1535            )
1536
1537            eq_(t1.uri, "/var/www/htdocs/includes/template.html")
1538            eq_(
1539                t1.path,
1540                "/var/lib/modules/var/www/htdocs/includes/template.html.py",
1541            )
1542
1543            t1 = NoCompileTemplate(
1544                filename="/var/www/htdocs/includes/template.html",
1545                uri="/bar/template.html",
1546                module_directory="/var/lib/modules",
1547            )
1548
1549            eq_(t1.uri, "/bar/template.html")
1550            eq_(t1.path, "/var/lib/modules/bar/template.html.py")
1551
1552        finally:
1553            os.path = current_path
1554
1555    def test_dont_accept_relative_outside_of_root(self):
1556        assert_raises_message(
1557            exceptions.TemplateLookupException,
1558            'Template uri "../../foo.html" is invalid - it '
1559            "cannot be relative outside of the root path",
1560            Template,
1561            "test",
1562            uri="../../foo.html",
1563        )
1564
1565        assert_raises_message(
1566            exceptions.TemplateLookupException,
1567            'Template uri "/../../foo.html" is invalid - it '
1568            "cannot be relative outside of the root path",
1569            Template,
1570            "test",
1571            uri="/../../foo.html",
1572        )
1573
1574        # normalizes in the root is OK
1575        t = Template("test", uri="foo/bar/../../foo.html")
1576        eq_(t.uri, "foo/bar/../../foo.html")
1577
1578
1579class ModuleTemplateTest(TemplateTest):
1580    def test_module_roundtrip(self):
1581        lookup = TemplateLookup()
1582
1583        template = Template(
1584            """
1585        <%inherit file="base.html"/>
1586
1587        % for x in range(5):
1588            ${x}
1589        % endfor
1590""",
1591            lookup=lookup,
1592        )
1593
1594        base = Template(
1595            """
1596        This is base.
1597        ${self.body()}
1598""",
1599            lookup=lookup,
1600        )
1601
1602        lookup.put_template("base.html", base)
1603        lookup.put_template("template.html", template)
1604
1605        assert result_lines(template.render()) == [
1606            "This is base.",
1607            "0",
1608            "1",
1609            "2",
1610            "3",
1611            "4",
1612        ]
1613
1614        lookup = TemplateLookup()
1615        template = ModuleTemplate(template.module, lookup=lookup)
1616        base = ModuleTemplate(base.module, lookup=lookup)
1617
1618        lookup.put_template("base.html", base)
1619        lookup.put_template("template.html", template)
1620
1621        assert result_lines(template.render()) == [
1622            "This is base.",
1623            "0",
1624            "1",
1625            "2",
1626            "3",
1627            "4",
1628        ]
1629
1630
1631class TestTemplateAPI(unittest.TestCase):
1632    def test_metadata(self):
1633        t = Template(
1634            """
1635Text
1636Text
1637% if bar:
1638    ${expression}
1639% endif
1640
1641<%include file='bar'/>
1642
1643""",
1644            uri="/some/template",
1645        )
1646        eq_(
1647            ModuleInfo.get_module_source_metadata(t.code, full_line_map=True),
1648            {
1649                "full_line_map": [
1650                    1,
1651                    1,
1652                    1,
1653                    1,
1654                    1,
1655                    1,
1656                    1,
1657                    1,
1658                    1,
1659                    1,
1660                    1,
1661                    1,
1662                    1,
1663                    1,
1664                    0,
1665                    0,
1666                    0,
1667                    0,
1668                    0,
1669                    0,
1670                    0,
1671                    1,
1672                    4,
1673                    5,
1674                    5,
1675                    5,
1676                    7,
1677                    8,
1678                    8,
1679                    8,
1680                    8,
1681                    8,
1682                    8,
1683                    8,
1684                ],
1685                "source_encoding": "ascii",
1686                "filename": None,
1687                "line_map": {
1688                    35: 29,
1689                    15: 0,
1690                    22: 1,
1691                    23: 4,
1692                    24: 5,
1693                    25: 5,
1694                    26: 5,
1695                    27: 7,
1696                    28: 8,
1697                    29: 8,
1698                },
1699                "uri": "/some/template",
1700            },
1701        )
1702
1703    def test_metadata_two(self):
1704        t = Template(
1705            """
1706Text
1707Text
1708% if bar:
1709    ${expression}
1710% endif
1711
1712    <%block name="foo">
1713        hi block
1714    </%block>
1715
1716
1717""",
1718            uri="/some/template",
1719        )
1720        eq_(
1721            ModuleInfo.get_module_source_metadata(t.code, full_line_map=True),
1722            {
1723                "full_line_map": [
1724                    1,
1725                    1,
1726                    1,
1727                    1,
1728                    1,
1729                    1,
1730                    1,
1731                    1,
1732                    1,
1733                    1,
1734                    1,
1735                    1,
1736                    1,
1737                    1,
1738                    0,
1739                    0,
1740                    0,
1741                    0,
1742                    0,
1743                    0,
1744                    0,
1745                    0,
1746                    0,
1747                    1,
1748                    4,
1749                    5,
1750                    5,
1751                    5,
1752                    7,
1753                    7,
1754                    7,
1755                    7,
1756                    7,
1757                    10,
1758                    10,
1759                    10,
1760                    10,
1761                    10,
1762                    10,
1763                    8,
1764                    8,
1765                    8,
1766                    8,
1767                    8,
1768                    8,
1769                    8,
1770                    8,
1771                    8,
1772                    8,
1773                    8,
1774                    8,
1775                ],
1776                "source_encoding": "ascii",
1777                "filename": None,
1778                "line_map": {
1779                    34: 10,
1780                    40: 8,
1781                    46: 8,
1782                    15: 0,
1783                    52: 46,
1784                    24: 1,
1785                    25: 4,
1786                    26: 5,
1787                    27: 5,
1788                    28: 5,
1789                    29: 7,
1790                },
1791                "uri": "/some/template",
1792            },
1793        )
1794
1795
1796class PreprocessTest(TemplateTest):
1797    def test_old_comments(self):
1798        t = Template(
1799            """
1800        im a template
1801# old style comment
1802    # more old style comment
1803
1804    ## new style comment
1805    - # not a comment
1806    - ## not a comment
1807""",
1808            preprocessor=convert_comments,
1809        )
1810
1811        assert (
1812            flatten_result(t.render())
1813            == "im a template - # not a comment - ## not a comment"
1814        )
1815
1816
1817class LexerTest(TemplateTest):
1818    def _fixture(self):
1819        from mako.parsetree import TemplateNode, Text
1820
1821        class MyLexer(object):
1822            encoding = "ascii"
1823
1824            def __init__(self, *arg, **kw):
1825                pass
1826
1827            def parse(self):
1828                t = TemplateNode("foo")
1829                t.nodes.append(
1830                    Text(
1831                        "hello world",
1832                        source="foo",
1833                        lineno=0,
1834                        pos=0,
1835                        filename=None,
1836                    )
1837                )
1838                return t
1839
1840        return MyLexer
1841
1842    def _test_custom_lexer(self, template):
1843        eq_(result_lines(template.render()), ["hello world"])
1844
1845    def test_via_template(self):
1846        t = Template("foo", lexer_cls=self._fixture())
1847        self._test_custom_lexer(t)
1848
1849    def test_via_lookup(self):
1850        tl = TemplateLookup(lexer_cls=self._fixture())
1851        tl.put_string("foo", "foo")
1852        t = tl.get_template("foo")
1853        self._test_custom_lexer(t)
1854
1855
1856class FuturesTest(TemplateTest):
1857    def test_future_import(self):
1858        t = Template("${ x / y }", future_imports=["division"])
1859        assert result_lines(t.render(x=12, y=5)) == ["2.4"]
1860