potluck.tests.test_mast
Tests of the mast module.
test_mast.py
1""" 2Tests of the mast module. 3 4test_mast.py 5""" 6 7import ast 8 9from .. import mast, mast_utils 10 11TESTS = [ 12 (True, 1, '2', '2'), 13 (False, 0, '2', '3'), 14 (True, 1, 'x', '_a_'), 15 (True, 1, '(x, y)', '(_a_, _b_)'), 16 (False, 0, '(x, y)', '(_a_, _a_)'), 17 (True, 1, '(x, x)', '(_a_, _a_)'), 18 (True, 4, 'max(7,3)', '_x_'), 19 (True, 1, 'max(7,3)', 'max(7,3)'), 20 (False, 0, 'max(7,2)', 'max(7,3)'), 21 (True, 1, 'max(7,3,5)', 'max(___args___)'), 22 (False, 1, 'min(max(7,3),5)', 'max(___args___)'), 23 (True, 2, 'min(max(7,3),5)', '_f_(___args___)'), 24 (True, 1, 'min(max(7,3),5)', 'min(max(___maxargs___),___minargs___)'), 25 (True, 1, 'max()', 'max(___args___)'), 26 (True, 1, 'max(4)', 'max(4,___args___)'), 27 (True, 1, 'max(4,5,6)', 'max(4,___args___)'), 28 (True, 1, '"hello %s" % x', '_a_str_ % _b_'), 29 (False, 0, 'y % x', '_a_str_ % _b_'), 30 (False, 0, '7 % x', '_a_str_ % _b_'), 31 (True, 1, '3', '_a_int_'), 32 (True, 1, '3.4', '_a_float_'), 33 (False, 0, '3', '_a_float_'), 34 (False, 0, '3.4', '_a_int_'), 35 (True, 1, 'True', '_a_bool_'), 36 (True, 1, 'False', '_a_bool_'), 37 (True, 1, 'None', 'None'), 38 # node vard can bind statements or exprs based on context. 39 (True, 7, 'print("hello"+str(3))', '_x_'), # 6 => 7 in Python 3 40 (True, 1, 'print("hello"+str(3))', 'print(_x_)'), 41 (True, 1, 'print(1)', 'print(_x_, ___args___)'), 42 (False, 0, 'print(1)', 'print(_x_, _y_, ___args___)'), 43 (False, 0, 'print(1, 2)', 'print(_x_)'), 44 (True, 1, 'print(1, 2)', 'print(_x_, ___args___)'), 45 (True, 1, 'print(1, 2)', 'print(_x_, _y_, ___args___)'), 46 (True, 1, 'print(1, 2, 3)', 'print(_x_, ___args___)'), 47 (True, 1, 'print(1, 2, 3)', 'print(_x_, _y_, ___args___)'), 48 (True, 1, 49 ''' 50def f(x): 51 return 17 52 ''', 53 ''' 54def f(_a_): 55 return _b_ 56 '''), 57 (True, 1, 58 ''' 59def f(x): 60 return x 61 ''', 62 ''' 63def f(_a_): 64 return _b_ 65 '''), 66 (True, 1, 67 ''' 68def f(x): 69 return x 70 ''', 71 ''' 72def f(_a_): 73 return _a_ 74 '''), 75 (False, 0, 76 ''' 77def f(x): 78 return 17 79 ''', 80 ''' 81def f(_a_): 82 return _a_ 83 '''), 84 (True, 1, 85 ''' 86def f(x): 87 return 17 88 ''', 89 ''' 90def f(_x_): 91 return _y_ 92 '''), 93 (True, 1, 94 ''' 95def f(x,y): 96 print('hi') 97 return x 98 ''', 99 ''' 100def f(_x_,_y_): 101 print('hi') 102 return _x_ 103 '''), 104 (True, 1, 105 ''' 106def f(x,y): 107 print('hi') 108 return x 109 ''', 110 ''' 111def _f_(_x_,_y_): 112 print('hi') 113 return _x_ 114 '''), 115 (False, 0, 116 ''' 117def f(x,y): 118 print('hi') 119 return y 120 ''', 121 ''' 122def f(_a_,_b_): 123 print('hi') 124 return _a_ 125 '''), 126 (False, 0, 'x', 'y'), 127 (True, 1, 128 ''' 129def f(x,y): 130 print('hi') 131 return y 132 ''', 133 ''' 134def _f_(_x_,_y_): 135 ___ 136 return _y_ 137 '''), 138 (True, 1, 139 ''' 140def f(x,y): 141 print('hi') 142 print('world') 143 print('bye') 144 return y 145 ''', 146 ''' 147def _f_(_x_,_y_): 148 ___stmts___ 149 return _y_ 150 '''), 151 (False, 0, 152 ''' 153def f(x,y): 154 print('hi') 155 print('world') 156 x = 4 157 print('really') 158 y = 7 159 print('bye') 160 return y 161 ''', 162 ''' 163def _f_(_x_,_y_): 164 ___stmts___ 165 print(_z_) 166 _y_ = _a_int_ 167 return _y_ 168 '''), 169 (True, 1, 170 ''' 171def f(x,y): 172 print('hi') 173 print('world') 174 x = 4 175 print('really') 176 y = 7 177 print('bye') 178 return y 179 ''', 180 ''' 181def _f_(_x_,_y_): 182 ___stmts___ 183 print(_z_) 184 _y_ = _a_int_ 185 ___more___ 186 return _y_ 187 '''), 188 (True, 1, 189 ''' 190def f(x,y): 191 print('hi') 192 print('world') 193 x = 4 194 print('really') 195 y = 7 196 print('bye') 197 return y 198 ''', 199 ''' 200def _f_(_x_,_y_): 201 ___stmts___ 202 print(_a_) 203 _b_ = _c_ 204 ___more___ 205 return _d_ 206 '''), 207 (False, 1, 208 ''' 209def f(x,y): 210 print('hi') 211 print('world') 212 x = 4 213 print('really') 214 y = 7 215 print('bye') 216 return y 217 ''', 218 ''' 219___stmts___ 220print(_a_) 221_b_ = _c_ 222___more___ 223return _d_ 224 '''), 225 (True, 1, 226 ''' 227def eyes(): 228 eye1 = Layer() 229 eye2 = Layer() 230 face = Layer() 231 face.add(eye) 232 face.add(eye2) 233 return face 234 ''', 235 ''' 236def eyes(): 237 ___ 238 _face_ = Layer() 239 ___ 240 return _face_ 241 '''), 242 (True, 1, '1 == 2', '2 == 1'), 243 (True, 1, '1 <= 2', '2 >= 1'), 244 (False, 0, '1 <= 2', '2 <= 1'), 245 (False, 0, 'f() <= 2', '2 >= f()'), 246 # Hmnm, is this the semantics we want for `and`? 247 (True, 1, 'a and b and c', 'b and a and c'), 248 (True, 1, '(a == b) == (b == c)', '(a == b) == (c == b)'), 249 (True, 1, '(a and b) and c', 'a and (b and c)'), 250 (True, 1, 'a and b', 'a and b'), 251 (True, 1, 'g == "a" or g == "b" or g == "c"', 252 '_g_ == _a_ or _g_ == _b_ or _c_ == _g_'), 253 (True, 1, ''' 254x = 1 255y = 2 256''', ''' 257x = 1 258y = 2 259'''), 260 (True, 1, ''' 261x = 1 262y = 2 263''', ''' 264___ 265y = 2 266'''), 267 (True, 1, ''' 268x = 1 269if (a or b or c): 270 return True 271else: 272 return False 273 ''', 274 ''' 275___ 276if _: 277 return _a_bool_ 278else: 279 return _b_bool_ 280___ 281 '''), 282 (True, 1, ''' 283if (a or b or c): 284 return True 285else: 286 return False 287 ''', 288 ''' 289if _: 290 return _a_bool_ 291else: 292 return _b_bool_ 293 '''), 294 (True, 1, ''' 295x = 1 296if (a or b or c): 297 return True 298else: 299 return False 300 ''', 301 ''' 302___ 303if _: 304 return _a_bool_ 305else: 306 return _b_bool_ 307 '''), 308 (False, 1, ''' 309def f(): 310 if (a or b or c): 311 return True 312 else: 313 return False 314 ''', 315 ''' 316___ 317if _: 318 return _a_bool_ 319else: 320 return _b_bool_ 321___ 322 '''), 323 (False, 1, ''' 324def isValidGesture(gesture): 325 if (gesture == 'rock' or gesture == 'paper' or gesture == 'scissors'): 326 return True 327 else: 328 return False 329 ''', ''' 330if _: 331 return _a_bool_ 332else: 333 return _b_bool_ 334 '''), 335 (False, 1, ''' 336def isValidGesture(gesture): 337 print('blah') 338 if (gesture == 'rock' or gesture == 'paper' or gesture == 'scissors'): 339 return True 340 return False 341 ''', ''' 342___ 343if _: 344 return _a_bool_ 345return _b_bool_ 346 '''), 347 348 (False, 1, ''' 349def isValidGesture(gesture): 350 print('blah') 351 if (gesture == 'rock' or gesture == 'paper' or gesture == 'scissors'): 352 return True 353 return False 354 ''', ''' 355if _: 356 return _a_bool_ 357return _b_bool_ 358 '''), 359 (False, 1, ''' 360def isValidGesture(gesture): 361 if (gesture == 'rock' or gesture == 'paper' or gesture == 'scissors'): 362 return True 363 return False 364 ''', ''' 365if _: 366 return _a_bool_ 367return _b_bool_ 368 '''), 369 (False, 1, ''' 370def isValidGesture(gesture): 371 if (gesture == 'rock' or gesture == 'paper' or gesture == 'scissors'): 372 x = True 373 x = False 374 return x 375 ''', ''' 376if _: 377 _x_ = _a_bool_ 378_x_ = _b_bool_ 379 '''), 380 (True, 1, ''' 381def isValidGesture(gesture): 382 if (gesture == 'rock' or gesture == 'paper' or gesture == 'scissors'): 383 return True 384 return False 385 ''', ''' 386def _(_): 387 if _: 388 return _a_bool_ 389 return _b_bool_ 390 '''), 391 (True, 1, 'x, y = f()', '___vars___ = f()'), 392 (True, 1, 'x, y = f()', '_vars_ = f()'), 393 (True, 1, 'f(a=1, b=2)', 'f(b=2, a=1)'), 394 (True, 1, ''' 395def f(x,y): 396 """with a docstring""" 397 if level <= 0: 398 pass 399 else: 400 fd(3) 401 lt(90) 402 ''', ''' 403def f(_, _): 404 ___ 405 if _: 406 ___t___ 407 else: 408 ___e___ 409 ___s___ 410 '''), 411 (True, 1, ''' 412class A(B): 413 def f(self, x): 414 pass 415 ''', 'class _(_): ___'), 416 # (True, True, 417 # 'for x, y in f():\n ___', 418 # 'for ___vars___ in f():\n ___'), 419 (False, 0, 'drawLs(size/2, level - 1)', 'drawLs(_size_/2.0, _)'), 420 (False, 0, '2', '2.0'), 421 (False, 0, '2', '_d_float_'), 422 (True, 1, ''' 423def keepFirstLetter(phrase): 424 """Returns a new string that contains only the first occurrence of a 425 letter from the original phrase. 426 The first letter occurrence can be upper or lower case. 427 Non-alpha characters (such as punctuations and space) are left unchanged. 428 """ 429 #this list holds lower-cased versions of all of the letters already used 430 usedCharacters = [] 431 432 finalPhrase = "" 433 for n in phrase: 434 if n.isalpha(): 435 #we need to create a temporary lower-cased version of the letter, 436 #so that we can check and see if we've seen an upper or lower-cased 437 #version of this letter before 438 tempN = n.lower() 439 if tempN not in usedCharacters: 440 usedCharacters.append(tempN) 441 #but we need to add the original n, so that we can preserve 442 #if it was upper cased or not 443 finalPhrase = finalPhrase + n 444 445 #this adds all non-letter characters into the final phrase list 446 else: 447 finalPhrase = finalPhrase + n 448 449 return finalPhrase 450 ''', ''' 451def _(___): 452 ___ 453 _acc_ = "" 454 ___ 455 for _ in _: 456 ___ 457 return _acc_ 458 ___ 459 '''), 460 (False, # Pattern should not match program 461 1, # Pattern should be found within program 462 # Program 463 ''' 464def keepFirstLetter(phrase): 465 #this list holds lower-cased versions of all of the letters already used 466 usedCharacters = [] 467 468 finalPhrase = "" 469 for n in phrase: 470 if n.isalpha(): 471 #we need to create a temporary lower-cased version of the letter, 472 #so that we can check and see if we've seen an upper or lower-cased 473 #version of this letter before 474 tempN = n.lower() 475 if tempN not in usedCharacters: 476 usedCharacters.append(tempN) 477 #but we need to add the original n, so that we can preserve 478 #if it was upper cased or not 479 finalPhrase = finalPhrase + n 480 481 #this adds all non-letter characters into the final phrase list 482 else: 483 finalPhrase = finalPhrase + n 484 485 return finalPhrase 486 ''', 487 # Pattern 488 ''' 489___prefix___ 490_acc_ = "" 491___middle___ 492for _ in _: 493 ___ 494return _acc_ 495___suffix___ 496 '''), 497 (True, 1, ''' 498a = 1 499b = 2 500c = 3 501 ''', '''___; _x_ = _n_'''), 502 (False, # Pattern should not match program 503 1, # Pattern should be found within program 504 # Program 505 ''' 506def f(a): 507 x = "" 508 return x 509 ''', 510 # Pattern 511 ''' 512_acc_ = "" 513return _acc_ 514 '''), 515 516 # Treatment of elses: 517 (True, 1, 'if x: print(1)', 'if _: _'), 518 (False, 0, 'if x: print(1)\nelse: pass', 'if _: _'), 519 (True, 1, 'if x: print(1)\nelse: pass', 'if _: _\nelse: ___'), 520 (False, 0, 'if x: print(1)', ''' 521if _: _ 522else: 523 _ 524 ___ 525 '''), 526 (True, 1, 'if x: print(1)\nelse: pass', ''' 527if _: _ 528else: 529 _ 530 ___ 531 '''), 532 533 # If ExpandExplicitElsePattern is used: 534 # (False, 0, 'if x: print(1)', 'if _: _\nelse: ___'), 535 536 # If ExpandExplicitElsePattern is NOT used: 537 (True, 1, 'if x: print(1)', 'if _: _\nelse: ___'), 538 539 # Keyword arguments 540 (True, 1, 'f(a=1)', 'f(a=1)'), 541 (True, 1, 'f(a=1)', 'f(_kw_=1)'), 542 (True, 1, 'f(a=1)', 'f(_kw_=_arg_)'), 543 (True, 1, 'f(a=1)', 'f(_=1)'), 544 (True, 1, 'f(a=1)', 'f(_=_arg_)'), 545 (True, 1, 'f(a=1, b=2)', 'f(_x_=_, _y_=_)'), 546 (True, 1, 'f(a=1, b=2)', 'f(_x_=2, _y_=_)'), 547 (False, 0, 'f(a=1, b=2)', 'f(_x_=_)'), 548 (False, 0, 'f(a=1, b=2)', 'f(_x_=_, _y_=_, _z_=_)'), 549 (False, 0, 'f(a=1, b=2)', 'f(_x_=2, _y_=2)'), 550 (True, 1, 'f(a=1, b=2)', 'f(_x_=_, b=_)'), 551 (True, 1, 'f(a=1, b=2)', 'f(b=_, _x_=_)'), 552 (True, 1, 'f(a=1+1, b=1+1)', 'f(_c_=_x_+_x_, _d_=_y_+_y_)'), 553 (True, 1, 'f(a=1+1, b=2+2)', 'f(_c_=_x_+_x_, _d_=_y_+_y_)'), 554 (True, 1, 'f(a=1+2, b=2+1)', 'f(_c_=_x_+_y_, _d_=_y_+_x_)'), 555 (True, 1, 'f(a=1+1, b=1+1)', 'f(_c_=_x_+_x_, _d_=_x_+_x_)'), 556 (False, 0, 'f(a=1+1, b=2+2)', 'f(_c_=_x_+_x_, _d_=_x_+_x_)'), 557 (True, 1, 'f(a=1, b=2)', 'f(___=_)'), 558 (True, 1, 'f(a=1, b=2)', 'f(___kwargs___=_)'), 559 (True, 1, 'f(a=1, b=2)', 'f(___kwargs___=_, b=_)'), 560 (True, 1, 'f(a=1, b=2)', 'f(b=_, ___kwargs___=_)'), 561 (True, 1, 'f(a=1, b=2)', 'f(___kwargs___=_, a=_, b=_)'), 562 (True, 1, 'f(a=1, b=2)', 'f(a=_, b=_, ___kwargs___=_)'), 563 (True, 1, 'f(a=1, b=2)', 'f(___kwargs___=_, b=_, a=_)'), 564 (True, 1, 'f(a=1, b=2)', 'f(b=_, a=_, ___kwargs___=_)'), 565 (True, 1, 'f(a=1, b=2)', 'f(a=_, ___kwargs___=_, b=_)'), 566 (True, 1, 'f(a=1, b=2)', 'f(b=_, ___kwargs___=_, a=_)'), 567 (True, 1, 'b = 7; f(a=1, b=2)', '_x_ = _; f(_x_=_, _y_=_)'), 568 (False, 0, 'b = 7; f(a=1, b=2)', '_x_ = _; f(_x_=1, _y_=_)'), 569 570 # and/or set wildcards 571 (True, 1, 'x or y or z', '_x_ or ___rest___'), 572 (True, 1, 'x or y or z', '_x_ or _y_ or ___rest___'), 573 (True, 1, 'x or y or z', '_x_ or _y_ or _z_ or ___rest___'), 574 575 # kwarg matching 576 (True, 1, 'def f(x=3):\n return x', 'def _f_(x=3):\n return x'), 577 (True, 1, 'def f(x=3):\n return x', 'def _f_(_=3):\n return _'), 578 (True, 1, 'def f(x=3):\n return x', 'def _f_(_=3):\n ___'), 579 (True, 1, 'def f(x=3):\n return x', 'def _f_(_x_=3):\n return _x_'), 580 (True, 1, 581 'def f(y=7):\n return y', 'def _f_(_x_=_y_):\n return _x_'), 582 (False, 0, 'def f(x=3):\n return x', 'def _f_(_y_=7):\n return _x_'), 583 584 # Succeeds! 585 (True, 1, 'def f(x=12):\n return x', 'def _f_(_=_):\n ___'), 586 587 # Should match because ___ has no default 588 (True, 1, 'def f(x=17):\n return x', 'def _f_(___, _=17):\n ___'), 589 590 # Multiple kwargs 591 (True, 1, 'def f(x=3, y=4):\n return x', 'def _f_(_=3, _=4):\n ___'), 592 (True, 1, 'def f(x=5, y=6):\n return x', 'def _f_(_=_, _=_):\n ___'), 593 # ___ doesn't match kwargs 594 (False, 0, 'def f(x=7, y=8):\n return x', 'def _f_(___):\n ___'), 595 596 # Exact matching of kwarg expressions 597 (True, 1, 'def f(x=y+3):\n return x', 'def _f_(_=y+3):\n ___'), 598 599 # Matching of kw-only args 600 (True, 1, 601 'def f(*a, x=5):\n return x', 602 'def _f_(*_, _x_=5):\n return _x_'), 603 # ___ does not match *_ 604 (False, 0, 605 'def f(*a, x=6):\n return x', 606 'def _f_(___, _x_=6):\n return _x_'), 607 608 # Multiple kw-only args 609 (True, 1, 610 'def f(*a, x=5, y=6):\n return x, y', 611 'def _f_(*_, _x_=5, _y_=6):\n return _x_, _y_'), 612 (False, 0, 613 'def f(*a, x=7, y=8):\n return x, y', 614 'def _f_(___, _x_=7, _y_=8):\n return _x_, _y_'), 615 616 # Function with docstring (must use ast.get_docstring!) 617 (False, 0, 'def f():\n """docstring"""', 'def _f_(___):\n _a_str_'), 618 619 # Function with docstring (using ast.get_docstring) 620 (True, 1, 621 'def f(x):\n """doc1"""\n return x', 622 'def _f_(___):\n ___', 623 lambda node, env: ast.get_docstring(node) is not None), 624 625 # Function without docstring (using ast.get_docstring) 626 (False, 0, 627 'def f(x):\n """doc2"""\n return x', 628 'def _f_(___):\n ___', 629 lambda node, env:( 630 ast.get_docstring(node) is None 631 or ast.get_docstring(node).strip() == '' 632 )), 633 (True, 1, 634 'def f(x):\n """"""\n return x', 635 'def _f_(___):\n ___', 636 lambda node, env:( 637 ast.get_docstring(node) is None 638 or ast.get_docstring(node).strip() == '' 639 )), 640 (True, 1, 641 'def nodoc(x):\n return x', 642 'def _f_(___):\n ___', 643 lambda node, env:( 644 ast.get_docstring(node) is None 645 or ast.get_docstring(node).strip() == '' 646 )), 647 648 # TODO: Recursive matching of kwarg expressions 649 (True, 1, 'def f(x=y+3):\n return x', 'def _f_(_=_+3):\n ___'), 650 651 # Function with multiple normal arguments 652 (True, 1, 'def f(x, y, z):\n return x', 'def _f_(___):\n ___'), 653 654 # Matching redundant elif conditions 655 ( 656 True, 657 1, 658 'if x == 3:\n return x\nelif not x == 3 and x > 5:\n return x-2', 659 'if _cond_:\n ___\nelif not _cond_ and ___:\n ___' 660 ), 661 ( # order matters 662 False, 663 0, 664 'if x == 3:\n return x\nelif x > 5 and not x == 3:\n return x-2', 665 'if _cond_:\n ___\nelif not _cond_ and ___:\n ___' 666 ), 667 ( # not == is not the same as != 668 False, 669 0, 670 'if x == 3:\n return x\nelif x > 5 and x != 3:\n return x-2', 671 'if _cond_:\n ___\nelif not _cond_ and ___:\n ___' 672 ), 673 ( # not == is not the same as != 674 True, 675 1, 676 'if x == 3:\n return x\nelif x > 5 and not x == 3:\n return x-2', 677 'if _cond_:\n ___\nelif ___ and not _cond_:\n ___' 678 ), 679 ( # not == is not the same as != 680 True, 681 1, 682 'if x == 3:\n return x\nelif x > 5 and x != 3:\n return x-2', 683 'if _n_ == _v_:\n ___\nelif ___ and _n_ != _v_:\n ___' 684 ), 685 ( # extra conditions do matter! 686 False, 687 0, 688 'if x == 3:\n return x\nelif not x == 3 and x > 5:\n return x-2\n' 689 + 'elif not x == 3 and x <= 5 and x < 0:\n return 0', 690 'if _cond_:\n ___\nelif not _cond_ and ___:\n ___' 691 ), 692 ( # match extra conditions: 693 True, 694 1, 695 'if x == 3:\n return x\nelif not x == 3 and x > 5:\n return x-2\n' 696 + 'elif not x == 3 and x <= 5 and x < 0:\n return 0', 697 'if _cond_:\n ___\nelif not _cond_ and ___:\n ___\nelif _:\n ___' 698 ), 699 ( # number of conditions must match exactly: 700 False, 701 0, 702 'if x == 3:\n return x\nelif not x == 3 and x > 5:\n return x-2\n' 703 + 'elif not x == 3 and x <= 5 and x < 0:\n return 0\n' 704 + 'elif not x == 3 and x <= 5 and x >= 0 and x == 1:\n return 1.5', 705 'if _cond_:\n ___\nelif not _cond_ and ___:\n ___\nelif _:\n ___' 706 ), 707 ( # matching with elif: 708 True, 709 2, 710 'if x < 0:\n x += 1\nelif x < 10:\n x += 0.5\nelse:\n x += 0.25', 711 'if _:\n ___\nelse:\n ___' 712 ), 713 ( # left/right branches are both okay 714 True, 715 1, 716 'x == 3', 717 '3 == x', 718 ), 719 ( # order does matter for some operators 720 False, 721 0, 722 '1 + 2 + 3', 723 '3 + 2 + 1' 724 ), 725 ( # but not for others 726 True, 727 1, 728 '1 and 2 and 3', 729 '3 and 2 and 1' 730 ), 731 732 # Import matching 733 (True, 1, 'import math', 'import _'), 734 (False, 0, 'import math as m', 'import _'), 735 (True, 1, 'import math as m', 'import _ as _'), 736 (True, 1, 'import math as m', 'import _ as _'), 737 (True, 1, 'import math as m', 'import math as _'), 738 (True, 1, 'import math as m', 'import _ as m'), 739 (True, 1, 'import math as m', 'import math as m'), 740 (True, 1, 'import math, io', 'import ___'), 741 (True, 1, 'import math, io', 'import math, ___'), 742 (True, 1, 'import math, io', 'import io, ___'), 743 (False, 0, 'from math import cos, sin', 'import math'), 744 (False, 0, 'from math import cos, sin', 'import _'), 745 (False, 0, 'from math import cos, sin', 'import ___'), 746 (True, 1, 'from math import cos, sin', 'from _ import ___'), 747 (True, 1, 'from math import cos, sin', 'from _ import _x_, sin'), 748 (True, 1, 'from math import cos, sin', 'from _ import cos, _x_'), 749 (True, 1, 'from math import cos, sin', 'from _ import _x_, cos'), 750 (True, 1, 'from math import cos, sin', 'from _ import sin, _x_'), 751 (True, 1, 'from math import cos, sin', 'from _ import _x_, ___'), 752 # Note: two bindings, but only one node match 753 (True, 1, 'from math import cos, sin', 'from math import ___'), 754 (True, 1, 'from math import cos, sin', 'from math import cos, sin'), 755 (True, 1, 'from math import cos, sin', 'from math import sin, cos'), 756 757 # Try/except matching 758 ( 759 True, 1, 760 'try:\n pass\nexcept ValueError as e:\n pass', 761 'try:\n ___\nexcept _ as e:\n ___', 762 ), # Note: it's horrible that 'e' has to match exactly here... 763 ( 764 True, 1, 765 'try:\n pass\nexcept ValueError:\n pass\nexcept TypeError:\n pass', 766 'try:\n ___\nexcept _:\n ___\nexcept _:\n ___', 767 ), 768 # Note: doesn't feel great that we need to match order/number of 769 # excepts, as well as presence/absence of as clause for each. 770 ( 771 True, 1, 772 'try:\n pass\nexcept ValueError:\n pass\nfinally:\n pass', 773 'try:\n ___\nexcept _:\n ___\nfinally:\n ___', 774 ), 775 ( 776 True, 1, 777 'try:\n pass\nfinally:\n pass', 778 'try:\n ___\nfinally:\n ___', 779 ), 780 781 # With matching 782 ( 783 True, 1, 784 'with open("a", "r") as fin:\n fin.read()', 785 'with _ as _:\n ___', 786 ), 787 ( 788 True, 1, 789 'with handler:\n code', 790 'with _:\n ___', 791 ), 792 ( 793 True, 1, 794 'with one, two as x:\n code', 795 'with _, _ as _:\n ___', 796 ), 797 # Note: Kinda sucks that we've got to match ordering & number of 798 # withitems, etc. TODO: allow sequence vars in withitems? 799] 800 801# These tests fail, but in the future, maybe they could pass? 802FAILING = [ 803 # TODO: Fails probably because of docstring issues? 804 (True, 1, 805 ''' 806def f(x,y): 807 """I have a docstring. 808 It is a couple lines long.""" 809 eye = Layer() 810 if eye: 811 face = Layer() 812 else: 813 face = Layer() 814 eye2 = Layer() 815 face.add(eye) 816 face.add(eye2) 817 return face 818 ''', 819 ''' 820def f(___args___): 821 eye = Layer() 822 ___ 823 '''), 824 825 # TODO: Fails because we compare all defaults as one block 826 # Zero extra keyword arguments: 827 (True, 1, 'def f(x=17):\n return x', 'def _f_(_=17, ___=_):\n ___'), 828 829 # TODO: Fails because of default matching bug 830 (True, 1, 'def f(x=9, y=10):\n return x', 'def _f_(___=_):\n ___'), 831 832 # TODO: This fails because the 'name' field of an ast.ExceptHandler 833 # is just a string, and node_is_bindable returns False for that. We 834 # ideally need some way of saying that a string node is bindable as a 835 # Name(id=value, ctx=None) IF the node is a 'name' filed of an 836 # ast.ExceptHandler, but that's not easy to figure out... 837 ( 838 True, 1, 839 'try:\n pass\nexcept ValueError as e:\n pass', 840 'try:\n ___\nexcept _ as _:\n ___', 841 ) 842] 843 844 845# TODO: Get this to work!!! 846def _test_import_findall_envs(): 847 """ 848 Tests # of environment matches w/ findall & a mix of scalar/set vars. 849 Note that this DOESN'T work for 3+ imports, and that's a bug... 850 """ 851 node = mast.parse("from math import cos, sin, tan") 852 pat = mast.parse_pattern("from _ import _x_, ___") 853 results = list(mast.findall(node, pat)) 854 assert len(results) == 1 855 mn, me = results[0] 856 assert isinstance(mn, ast.ImportFrom) 857 assert mn.module == "math" 858 assert mn.level == 0 859 print("names:", [e['x'].id for e in me]) 860 assert len(me) == 3 861 assert all(len(env) == 1 for env in me) 862 assert all('x' in env for env in me) 863 xs = [env['x'] for env in me] 864 assert len([x for x in xs if x.id == "cos"]) == 1 865 assert len([x for x in xs if x.id == "sin"]) == 1 866 assert len([x for x in xs if x.id == "tan"]) == 1 867 868 869def test_two_aliases(): 870 """ 871 Tests matching a list of two aliases against different permutations 872 of a scalar + set var. 873 """ 874 node = [ 875 ast.alias(name="cos", asname=None), 876 ast.alias(name="sin", asname=None) 877 ] 878 pat1 = [ 879 ast.alias(name="_x_", asname=None), 880 ast.alias(name="___", asname=None) 881 ] 882 pat2 = [ 883 ast.alias(name="___", asname=None), 884 ast.alias(name="_x_", asname=None) 885 ] 886 887 m1 = list(mast.imatches(node, pat1, mast_utils.Some({}), True)) 888 m2 = list(mast.imatches(node, pat2, mast_utils.Some({}), True)) 889 890 assert len(m1) == 1 891 assert len(m2) == 1 892 e1 = m1[0] 893 e2 = m2[0] 894 assert len(e1) == 1 895 assert len(e2) == 1 896 assert 'x' in e1 897 assert 'x' in e2 898 x1 = e1['x'] 899 x2 = e2['x'] 900 assert x1.id == 'cos' 901 assert x2.id == 'sin' 902 903 904def test_mast(): 905 """ 906 Runs all of the TESTS. 907 """ 908 for test_spec in TESTS: 909 run_test(*test_spec) 910 911 912def run_test( 913 expect_match, 914 expect_count, 915 src, 916 pattern, 917 matchpred=mast.predtrue 918): 919 """ 920 Runs a test of the match, find, and count functions using a given 921 source string to match against and pattern to match. A match 922 predicate may also be provided to test that functionality, but is 923 optional. 924 925 You must specify whether a match is expected for the full pattern, 926 and the number of matches expected in count mode. 927 """ 928 # Create & reduce the node to match against 929 node = mast.parse(src) 930 node = ( 931 node.body[0].value 932 if type(node.body[0]) == ast.Expr and len(node.body) == 1 933 else ( 934 node.body[0] 935 if len(node.body) == 1 936 else node.body 937 ) 938 ) 939 940 # Create our pattern node 941 pat_node = mast.parse_pattern(pattern) 942 943 # Messages to include when we assert for context 944 baggage = '' 945 baggage += f'Program: {src} => {mast.dump(node)}\n' 946 if isinstance(node, ast.FunctionDef): 947 baggage += f'Docstring: {ast.get_docstring(node)}\n' 948 949 baggage += f'Pattern: {pattern} => {mast.dump(pat_node)}\n' 950 951 if matchpred != mast.predtrue: 952 baggage += f'Matchpred: {matchpred.__name__}\n' 953 954 # gen = False 955 assert ( 956 bool(mast.match(node, pat_node, gen=False, matchpred=matchpred)) 957 == expect_match 958 ), baggage 959 960 # gen = True 961 assert ( 962 bool( 963 mast_utils.takeone( 964 mast.match(node, pat_node, gen=True, matchpred=matchpred) 965 ) 966 ) 967 == expect_match 968 ), baggage 969 970 opt = mast.find(node, pat_node, matchpred=matchpred) 971 assert bool(opt) == (0 < expect_count), baggage 972 973 c = mast.count(node, pat_node, matchpred=matchpred) 974 assert c == expect_count, baggage 975 976 nmatches = 0 977 for (node, envs) in mast.findall(node, pat_node, matchpred=matchpred): 978 # find should return first findall result. 979 if nmatches == 0: 980 fnode, fenvs = opt.get() 981 # Note: because of arg -> name conversion, == doesn't work on 982 # the envs, since the name nodes produced will be the same, 983 # but they won't be == to each other! 984 985 assert fnode == node, baggage 986 assert [ 987 {k: mast.dump(env[k]) for k in env} 988 for env in envs 989 ] == [ 990 {k: mast.dump(env[k]) for k in env} 991 for env in fenvs 992 ], baggage 993 nmatches += 1 994 pass 995 996 # count should count the same number of matches that findall finds. 997 assert nmatches == c, baggage
def
test_two_aliases():
870def test_two_aliases(): 871 """ 872 Tests matching a list of two aliases against different permutations 873 of a scalar + set var. 874 """ 875 node = [ 876 ast.alias(name="cos", asname=None), 877 ast.alias(name="sin", asname=None) 878 ] 879 pat1 = [ 880 ast.alias(name="_x_", asname=None), 881 ast.alias(name="___", asname=None) 882 ] 883 pat2 = [ 884 ast.alias(name="___", asname=None), 885 ast.alias(name="_x_", asname=None) 886 ] 887 888 m1 = list(mast.imatches(node, pat1, mast_utils.Some({}), True)) 889 m2 = list(mast.imatches(node, pat2, mast_utils.Some({}), True)) 890 891 assert len(m1) == 1 892 assert len(m2) == 1 893 e1 = m1[0] 894 e2 = m2[0] 895 assert len(e1) == 1 896 assert len(e2) == 1 897 assert 'x' in e1 898 assert 'x' in e2 899 x1 = e1['x'] 900 x2 = e2['x'] 901 assert x1.id == 'cos' 902 assert x2.id == 'sin'
Tests matching a list of two aliases against different permutations of a scalar + set var.
def
test_mast():
905def test_mast(): 906 """ 907 Runs all of the TESTS. 908 """ 909 for test_spec in TESTS: 910 run_test(*test_spec)
Runs all of the TESTS.
def
run_test( expect_match, expect_count, src, pattern, matchpred=<function predtrue>):
913def run_test( 914 expect_match, 915 expect_count, 916 src, 917 pattern, 918 matchpred=mast.predtrue 919): 920 """ 921 Runs a test of the match, find, and count functions using a given 922 source string to match against and pattern to match. A match 923 predicate may also be provided to test that functionality, but is 924 optional. 925 926 You must specify whether a match is expected for the full pattern, 927 and the number of matches expected in count mode. 928 """ 929 # Create & reduce the node to match against 930 node = mast.parse(src) 931 node = ( 932 node.body[0].value 933 if type(node.body[0]) == ast.Expr and len(node.body) == 1 934 else ( 935 node.body[0] 936 if len(node.body) == 1 937 else node.body 938 ) 939 ) 940 941 # Create our pattern node 942 pat_node = mast.parse_pattern(pattern) 943 944 # Messages to include when we assert for context 945 baggage = '' 946 baggage += f'Program: {src} => {mast.dump(node)}\n' 947 if isinstance(node, ast.FunctionDef): 948 baggage += f'Docstring: {ast.get_docstring(node)}\n' 949 950 baggage += f'Pattern: {pattern} => {mast.dump(pat_node)}\n' 951 952 if matchpred != mast.predtrue: 953 baggage += f'Matchpred: {matchpred.__name__}\n' 954 955 # gen = False 956 assert ( 957 bool(mast.match(node, pat_node, gen=False, matchpred=matchpred)) 958 == expect_match 959 ), baggage 960 961 # gen = True 962 assert ( 963 bool( 964 mast_utils.takeone( 965 mast.match(node, pat_node, gen=True, matchpred=matchpred) 966 ) 967 ) 968 == expect_match 969 ), baggage 970 971 opt = mast.find(node, pat_node, matchpred=matchpred) 972 assert bool(opt) == (0 < expect_count), baggage 973 974 c = mast.count(node, pat_node, matchpred=matchpred) 975 assert c == expect_count, baggage 976 977 nmatches = 0 978 for (node, envs) in mast.findall(node, pat_node, matchpred=matchpred): 979 # find should return first findall result. 980 if nmatches == 0: 981 fnode, fenvs = opt.get() 982 # Note: because of arg -> name conversion, == doesn't work on 983 # the envs, since the name nodes produced will be the same, 984 # but they won't be == to each other! 985 986 assert fnode == node, baggage 987 assert [ 988 {k: mast.dump(env[k]) for k in env} 989 for env in envs 990 ] == [ 991 {k: mast.dump(env[k]) for k in env} 992 for env in fenvs 993 ], baggage 994 nmatches += 1 995 pass 996 997 # count should count the same number of matches that findall finds. 998 assert nmatches == c, baggage
Runs a test of the match, find, and count functions using a given source string to match against and pattern to match. A match predicate may also be provided to test that functionality, but is optional.
You must specify whether a match is expected for the full pattern, and the number of matches expected in count mode.