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