1from mako import lookup 2from mako.template import Template 3from mako.testing.assertions import assert_raises 4from mako.testing.assertions import eq_ 5from mako.testing.fixtures import TemplateTest 6from mako.testing.helpers import flatten_result 7from mako.testing.helpers import result_lines 8 9 10class DefTest(TemplateTest): 11 def test_def_noargs(self): 12 template = Template( 13 """ 14 15 ${mycomp()} 16 17 <%def name="mycomp()"> 18 hello mycomp ${variable} 19 </%def> 20 21 """ 22 ) 23 eq_(template.render(variable="hi").strip(), """hello mycomp hi""") 24 25 def test_def_blankargs(self): 26 template = Template( 27 """ 28 <%def name="mycomp()"> 29 hello mycomp ${variable} 30 </%def> 31 32 ${mycomp()}""" 33 ) 34 eq_(template.render(variable="hi").strip(), "hello mycomp hi") 35 36 def test_def_args(self): 37 template = Template( 38 """ 39 <%def name="mycomp(a, b)"> 40 hello mycomp ${variable}, ${a}, ${b} 41 </%def> 42 43 ${mycomp(5, 6)}""" 44 ) 45 eq_( 46 template.render(variable="hi", a=5, b=6).strip(), 47 """hello mycomp hi, 5, 6""", 48 ) 49 50 def test_def_py3k_args(self): 51 template = Template( 52 """ 53 <%def name="kwonly(one, two, *three, four, five=5, **six)"> 54 look at all these args: ${one} ${two} ${three[0]} """ 55 """${four} ${five} ${six['seven']} 56 </%def> 57 58 ${kwonly('one', 'two', 'three', four='four', seven='seven')}""" 59 ) 60 eq_( 61 template.render(one=1, two=2, three=(3,), six=6).strip(), 62 """look at all these args: one two three four 5 seven""", 63 ) 64 65 def test_inter_def(self): 66 """test defs calling each other""" 67 template = Template( 68 """ 69 ${b()} 70 71 <%def name="a()">\ 72 im a 73 </%def> 74 75 <%def name="b()"> 76 im b 77 and heres a: ${a()} 78 </%def> 79 80 <%def name="c()"> 81 im c 82 </%def> 83""" 84 ) 85 # check that "a" is declared in "b", but not in "c" 86 assert "a" not in template.module.render_c.__code__.co_varnames 87 assert "a" in template.module.render_b.__code__.co_varnames 88 89 # then test output 90 eq_(flatten_result(template.render()), "im b and heres a: im a") 91 92 def test_toplevel(self): 93 """test calling a def from the top level""" 94 95 template = Template( 96 """ 97 98 this is the body 99 100 <%def name="a()"> 101 this is a 102 </%def> 103 104 <%def name="b(x, y)"> 105 this is b, ${x} ${y} 106 </%def> 107 108 """ 109 ) 110 111 self._do_test( 112 template.get_def("a"), "this is a", filters=flatten_result 113 ) 114 self._do_test( 115 template.get_def("b"), 116 "this is b, 10 15", 117 template_args={"x": 10, "y": 15}, 118 filters=flatten_result, 119 ) 120 self._do_test( 121 template.get_def("body"), 122 "this is the body", 123 filters=flatten_result, 124 ) 125 126 # test that args outside of the dict can be used 127 self._do_test( 128 template.get_def("a"), 129 "this is a", 130 filters=flatten_result, 131 template_args={"q": 5, "zq": "test"}, 132 ) 133 134 def test_def_operations(self): 135 """test get/list/has def""" 136 137 template = Template( 138 """ 139 140 this is the body 141 142 <%def name="a()"> 143 this is a 144 </%def> 145 146 <%def name="b(x, y)"> 147 this is b, ${x} ${y} 148 </%def> 149 150 """ 151 ) 152 153 assert template.get_def("a") 154 assert template.get_def("b") 155 assert_raises(AttributeError, template.get_def, ("c")) 156 157 assert template.has_def("a") 158 assert template.has_def("b") 159 assert not template.has_def("c") 160 161 defs = template.list_defs() 162 assert "a" in defs 163 assert "b" in defs 164 assert "body" in defs 165 assert "c" not in defs 166 167 168class ScopeTest(TemplateTest): 169 """test scoping rules. The key is, enclosing 170 scope always takes precedence over contextual scope.""" 171 172 def test_scope_one(self): 173 self._do_memory_test( 174 """ 175 <%def name="a()"> 176 this is a, and y is ${y} 177 </%def> 178 179 ${a()} 180 181 <% 182 y = 7 183 %> 184 185 ${a()} 186 187""", 188 "this is a, and y is None this is a, and y is 7", 189 filters=flatten_result, 190 template_args={"y": None}, 191 ) 192 193 def test_scope_two(self): 194 t = Template( 195 """ 196 y is ${y} 197 198 <% 199 y = 7 200 %> 201 202 y is ${y} 203""" 204 ) 205 try: 206 t.render(y=None) 207 assert False 208 except UnboundLocalError: 209 assert True 210 211 def test_scope_four(self): 212 """test that variables are pulled 213 from 'enclosing' scope before context.""" 214 t = Template( 215 """ 216 <% 217 x = 5 218 %> 219 <%def name="a()"> 220 this is a. x is ${x}. 221 </%def> 222 223 <%def name="b()"> 224 <% 225 x = 9 226 %> 227 this is b. x is ${x}. 228 calling a. ${a()} 229 </%def> 230 231 ${b()} 232""" 233 ) 234 eq_( 235 flatten_result(t.render()), 236 "this is b. x is 9. calling a. this is a. x is 5.", 237 ) 238 239 def test_scope_five(self): 240 """test that variables are pulled from 241 'enclosing' scope before context.""" 242 # same as test four, but adds a scope around it. 243 t = Template( 244 """ 245 <%def name="enclosing()"> 246 <% 247 x = 5 248 %> 249 <%def name="a()"> 250 this is a. x is ${x}. 251 </%def> 252 253 <%def name="b()"> 254 <% 255 x = 9 256 %> 257 this is b. x is ${x}. 258 calling a. ${a()} 259 </%def> 260 261 ${b()} 262 </%def> 263 ${enclosing()} 264""" 265 ) 266 eq_( 267 flatten_result(t.render()), 268 "this is b. x is 9. calling a. this is a. x is 5.", 269 ) 270 271 def test_scope_six(self): 272 """test that the initial context counts 273 as 'enclosing' scope, for plain defs""" 274 t = Template( 275 """ 276 277 <%def name="a()"> 278 a: x is ${x} 279 </%def> 280 281 <%def name="b()"> 282 <% 283 x = 10 284 %> 285 b. x is ${x}. ${a()} 286 </%def> 287 288 ${b()} 289 """ 290 ) 291 eq_(flatten_result(t.render(x=5)), "b. x is 10. a: x is 5") 292 293 def test_scope_seven(self): 294 """test that the initial context counts 295 as 'enclosing' scope, for nested defs""" 296 t = Template( 297 """ 298 <%def name="enclosing()"> 299 <%def name="a()"> 300 a: x is ${x} 301 </%def> 302 303 <%def name="b()"> 304 <% 305 x = 10 306 %> 307 b. x is ${x}. ${a()} 308 </%def> 309 310 ${b()} 311 </%def> 312 ${enclosing()} 313 """ 314 ) 315 eq_(flatten_result(t.render(x=5)), "b. x is 10. a: x is 5") 316 317 def test_scope_eight(self): 318 """test that the initial context counts 319 as 'enclosing' scope, for nested defs""" 320 t = Template( 321 """ 322 <%def name="enclosing()"> 323 <%def name="a()"> 324 a: x is ${x} 325 </%def> 326 327 <%def name="b()"> 328 <% 329 x = 10 330 %> 331 332 b. x is ${x}. ${a()} 333 </%def> 334 335 ${b()} 336 </%def> 337 ${enclosing()} 338 """ 339 ) 340 eq_(flatten_result(t.render(x=5)), "b. x is 10. a: x is 5") 341 342 def test_scope_nine(self): 343 """test that 'enclosing scope' doesnt 344 get exported to other templates""" 345 346 l = lookup.TemplateLookup() 347 l.put_string( 348 "main", 349 """ 350 <% 351 x = 5 352 %> 353 this is main. <%include file="secondary"/> 354""", 355 ) 356 357 l.put_string( 358 "secondary", 359 """ 360 this is secondary. x is ${x} 361""", 362 ) 363 364 eq_( 365 flatten_result(l.get_template("main").render(x=2)), 366 "this is main. this is secondary. x is 2", 367 ) 368 369 def test_scope_ten(self): 370 t = Template( 371 """ 372 <%def name="a()"> 373 <%def name="b()"> 374 <% 375 y = 19 376 %> 377 b/c: ${c()} 378 b/y: ${y} 379 </%def> 380 <%def name="c()"> 381 c/y: ${y} 382 </%def> 383 384 <% 385 # we assign to "y". but the 'enclosing 386 # scope' of "b" and "c" is from 387 # the "y" on the outside 388 y = 10 389 %> 390 a/y: ${y} 391 a/b: ${b()} 392 </%def> 393 394 <% 395 y = 7 396 %> 397 main/a: ${a()} 398 main/y: ${y} 399 """ 400 ) 401 eq_( 402 flatten_result(t.render()), 403 "main/a: a/y: 10 a/b: b/c: c/y: 10 b/y: 19 main/y: 7", 404 ) 405 406 def test_scope_eleven(self): 407 t = Template( 408 """ 409 x is ${x} 410 <%def name="a(x)"> 411 this is a, ${b()} 412 <%def name="b()"> 413 this is b, x is ${x} 414 </%def> 415 </%def> 416 417 ${a(x=5)} 418""" 419 ) 420 eq_( 421 result_lines(t.render(x=10)), 422 ["x is 10", "this is a,", "this is b, x is 5"], 423 ) 424 425 def test_unbound_scope(self): 426 t = Template( 427 """ 428 <% 429 y = 10 430 %> 431 <%def name="a()"> 432 y is: ${y} 433 <% 434 # should raise error ? 435 y = 15 436 %> 437 y is ${y} 438 </%def> 439 ${a()} 440""" 441 ) 442 assert_raises(UnboundLocalError, t.render) 443 444 def test_unbound_scope_two(self): 445 t = Template( 446 """ 447 <%def name="enclosing()"> 448 <% 449 y = 10 450 %> 451 <%def name="a()"> 452 y is: ${y} 453 <% 454 # should raise error ? 455 y = 15 456 %> 457 y is ${y} 458 </%def> 459 ${a()} 460 </%def> 461 ${enclosing()} 462""" 463 ) 464 try: 465 print(t.render()) 466 assert False 467 except UnboundLocalError: 468 assert True 469 470 def test_canget_kwargs(self): 471 """test that arguments passed to the body() 472 function are accessible by top-level defs""" 473 l = lookup.TemplateLookup() 474 l.put_string( 475 "base", 476 """ 477 478 ${next.body(x=12)} 479 480 """, 481 ) 482 483 l.put_string( 484 "main", 485 """ 486 <%inherit file="base"/> 487 <%page args="x"/> 488 this is main. x is ${x} 489 490 ${a()} 491 492 <%def name="a(**args)"> 493 this is a, x is ${x} 494 </%def> 495 """, 496 ) 497 498 # test via inheritance 499 eq_( 500 result_lines(l.get_template("main").render()), 501 ["this is main. x is 12", "this is a, x is 12"], 502 ) 503 504 l.put_string( 505 "another", 506 """ 507 <%namespace name="ns" file="main"/> 508 509 ${ns.body(x=15)} 510 """, 511 ) 512 # test via namespace 513 eq_( 514 result_lines(l.get_template("another").render()), 515 ["this is main. x is 15", "this is a, x is 15"], 516 ) 517 518 def test_inline_expression_from_arg_one(self): 519 """test that cache_key=${foo} gets its value from 520 the 'foo' argument in the <%def> tag, 521 and strict_undefined doesn't complain. 522 523 this is #191. 524 525 """ 526 t = Template( 527 """ 528 <%def name="layout(foo)" cached="True" cache_key="${foo}"> 529 foo: ${foo} 530 </%def> 531 532 ${layout(3)} 533 """, 534 strict_undefined=True, 535 cache_impl="plain", 536 ) 537 538 eq_(result_lines(t.render()), ["foo: 3"]) 539 540 def test_interpret_expression_from_arg_two(self): 541 """test that cache_key=${foo} gets its value from 542 the 'foo' argument regardless of it being passed 543 from the context. 544 545 This is here testing that there's no change 546 to existing behavior before and after #191. 547 548 """ 549 t = Template( 550 """ 551 <%def name="layout(foo)" cached="True" cache_key="${foo}"> 552 foo: ${value} 553 </%def> 554 555 ${layout(3)} 556 """, 557 cache_impl="plain", 558 ) 559 560 eq_(result_lines(t.render(foo="foo", value=1)), ["foo: 1"]) 561 eq_(result_lines(t.render(foo="bar", value=2)), ["foo: 1"]) 562 563 564class NestedDefTest(TemplateTest): 565 def test_nested_def(self): 566 t = Template( 567 """ 568 569 ${hi()} 570 571 <%def name="hi()"> 572 hey, im hi. 573 and heres ${foo()}, ${bar()} 574 575 <%def name="foo()"> 576 this is foo 577 </%def> 578 579 <%def name="bar()"> 580 this is bar 581 </%def> 582 </%def> 583""" 584 ) 585 eq_( 586 flatten_result(t.render()), 587 "hey, im hi. and heres this is foo , this is bar", 588 ) 589 590 def test_nested_2(self): 591 t = Template( 592 """ 593 x is ${x} 594 <%def name="a()"> 595 this is a, x is ${x} 596 ${b()} 597 <%def name="b()"> 598 this is b: ${x} 599 </%def> 600 </%def> 601 ${a()} 602""" 603 ) 604 605 eq_( 606 flatten_result(t.render(x=10)), 607 "x is 10 this is a, x is 10 this is b: 10", 608 ) 609 610 def test_nested_with_args(self): 611 t = Template( 612 """ 613 ${a()} 614 <%def name="a()"> 615 <%def name="b(x, y=2)"> 616 b x is ${x} y is ${y} 617 </%def> 618 a ${b(5)} 619 </%def> 620""" 621 ) 622 eq_(flatten_result(t.render()), "a b x is 5 y is 2") 623 624 def test_nested_def_2(self): 625 template = Template( 626 """ 627 ${a()} 628 <%def name="a()"> 629 <%def name="b()"> 630 <%def name="c()"> 631 comp c 632 </%def> 633 ${c()} 634 </%def> 635 ${b()} 636 </%def> 637""" 638 ) 639 eq_(flatten_result(template.render()), "comp c") 640 641 def test_nested_nested_def(self): 642 t = Template( 643 """ 644 645 ${a()} 646 <%def name="a()"> 647 a 648 <%def name="b1()"> 649 a_b1 650 </%def> 651 <%def name="b2()"> 652 a_b2 ${c1()} 653 <%def name="c1()"> 654 a_b2_c1 655 </%def> 656 </%def> 657 <%def name="b3()"> 658 a_b3 ${c1()} 659 <%def name="c1()"> 660 a_b3_c1 heres x: ${x} 661 <% 662 y = 7 663 %> 664 y is ${y} 665 </%def> 666 <%def name="c2()"> 667 a_b3_c2 668 y is ${y} 669 c1 is ${c1()} 670 </%def> 671 ${c2()} 672 </%def> 673 674 ${b1()} ${b2()} ${b3()} 675 </%def> 676""" 677 ) 678 eq_( 679 flatten_result(t.render(x=5, y=None)), 680 "a a_b1 a_b2 a_b2_c1 a_b3 a_b3_c1 " 681 "heres x: 5 y is 7 a_b3_c2 y is " 682 "None c1 is a_b3_c1 heres x: 5 y is 7", 683 ) 684 685 def test_nested_nested_def_2(self): 686 t = Template( 687 """ 688 <%def name="a()"> 689 this is a ${b()} 690 <%def name="b()"> 691 this is b 692 ${c()} 693 </%def> 694 695 <%def name="c()"> 696 this is c 697 </%def> 698 </%def> 699 ${a()} 700""" 701 ) 702 eq_(flatten_result(t.render()), "this is a this is b this is c") 703 704 def test_outer_scope(self): 705 t = Template( 706 """ 707 <%def name="a()"> 708 a: x is ${x} 709 </%def> 710 711 <%def name="b()"> 712 <%def name="c()"> 713 <% 714 x = 10 715 %> 716 c. x is ${x}. ${a()} 717 </%def> 718 719 b. ${c()} 720 </%def> 721 722 ${b()} 723 724 x is ${x} 725""" 726 ) 727 eq_(flatten_result(t.render(x=5)), "b. c. x is 10. a: x is 5 x is 5") 728 729 730class ExceptionTest(TemplateTest): 731 def test_raise(self): 732 template = Template( 733 """ 734 <% 735 raise Exception("this is a test") 736 %> 737 """, 738 format_exceptions=False, 739 ) 740 assert_raises(Exception, template.render) 741 742 def test_handler(self): 743 def handle(context, error): 744 context.write("error message is " + str(error)) 745 return True 746 747 template = Template( 748 """ 749 <% 750 raise Exception("this is a test") 751 %> 752 """, 753 error_handler=handle, 754 ) 755 eq_(template.render().strip(), "error message is this is a test") 756