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