|
| 1 | +import re |
1 | 2 | import sys |
2 | 3 | import types |
3 | 4 | import unittest |
@@ -519,10 +520,183 @@ def m1(self): pass |
519 | 520 | self.assertIn(('m1', 'method', D), attrs, 'missing plain method') |
520 | 521 | self.assertIn(('datablob', 'data', A), attrs, 'missing data') |
521 | 522 |
|
| 523 | +class TestGetcallargsFunctions(unittest.TestCase): |
| 524 | + |
| 525 | + def assertEqualCallArgs(self, func, call_params_string, locs=None): |
| 526 | + locs = dict(locs or {}, func=func) |
| 527 | + r1 = eval('func(%s)' % call_params_string, None, locs) |
| 528 | + r2 = eval('inspect.getcallargs(func, %s)' % call_params_string, None, |
| 529 | + locs) |
| 530 | + self.assertEqual(r1, r2) |
| 531 | + |
| 532 | + def assertEqualException(self, func, call_param_string, locs=None): |
| 533 | + locs = dict(locs or {}, func=func) |
| 534 | + try: |
| 535 | + eval('func(%s)' % call_param_string, None, locs) |
| 536 | + except Exception as e: |
| 537 | + ex1 = e |
| 538 | + else: |
| 539 | + self.fail('Exception not raised') |
| 540 | + try: |
| 541 | + eval('inspect.getcallargs(func, %s)' % call_param_string, None, |
| 542 | + locs) |
| 543 | + except Exception as e: |
| 544 | + ex2 = e |
| 545 | + else: |
| 546 | + self.fail('Exception not raised') |
| 547 | + self.assertIs(type(ex1), type(ex2)) |
| 548 | + self.assertEqual(str(ex1), str(ex2)) |
| 549 | + del ex1, ex2 |
| 550 | + |
| 551 | + def makeCallable(self, signature): |
| 552 | + """Create a function that returns its locals()""" |
| 553 | + code = "lambda %s: locals()" |
| 554 | + return eval(code % signature) |
| 555 | + |
| 556 | + def test_plain(self): |
| 557 | + f = self.makeCallable('a, b=1') |
| 558 | + self.assertEqualCallArgs(f, '2') |
| 559 | + self.assertEqualCallArgs(f, '2, 3') |
| 560 | + self.assertEqualCallArgs(f, 'a=2') |
| 561 | + self.assertEqualCallArgs(f, 'b=3, a=2') |
| 562 | + self.assertEqualCallArgs(f, '2, b=3') |
| 563 | + # expand *iterable / **mapping |
| 564 | + self.assertEqualCallArgs(f, '*(2,)') |
| 565 | + self.assertEqualCallArgs(f, '*[2]') |
| 566 | + self.assertEqualCallArgs(f, '*(2, 3)') |
| 567 | + self.assertEqualCallArgs(f, '*[2, 3]') |
| 568 | + self.assertEqualCallArgs(f, '**{"a":2}') |
| 569 | + self.assertEqualCallArgs(f, 'b=3, **{"a":2}') |
| 570 | + self.assertEqualCallArgs(f, '2, **{"b":3}') |
| 571 | + self.assertEqualCallArgs(f, '**{"b":3, "a":2}') |
| 572 | + # expand UserList / UserDict |
| 573 | + self.assertEqualCallArgs(f, '*collections.UserList([2])') |
| 574 | + self.assertEqualCallArgs(f, '*collections.UserList([2, 3])') |
| 575 | + self.assertEqualCallArgs(f, '**collections.UserDict(a=2)') |
| 576 | + self.assertEqualCallArgs(f, '2, **collections.UserDict(b=3)') |
| 577 | + self.assertEqualCallArgs(f, 'b=2, **collections.UserDict(a=3)') |
| 578 | + |
| 579 | + def test_varargs(self): |
| 580 | + f = self.makeCallable('a, b=1, *c') |
| 581 | + self.assertEqualCallArgs(f, '2') |
| 582 | + self.assertEqualCallArgs(f, '2, 3') |
| 583 | + self.assertEqualCallArgs(f, '2, 3, 4') |
| 584 | + self.assertEqualCallArgs(f, '*(2,3,4)') |
| 585 | + self.assertEqualCallArgs(f, '2, *[3,4]') |
| 586 | + self.assertEqualCallArgs(f, '2, 3, *collections.UserList([4])') |
| 587 | + |
| 588 | + def test_varkw(self): |
| 589 | + f = self.makeCallable('a, b=1, **c') |
| 590 | + self.assertEqualCallArgs(f, 'a=2') |
| 591 | + self.assertEqualCallArgs(f, '2, b=3, c=4') |
| 592 | + self.assertEqualCallArgs(f, 'b=3, a=2, c=4') |
| 593 | + self.assertEqualCallArgs(f, 'c=4, **{"a":2, "b":3}') |
| 594 | + self.assertEqualCallArgs(f, '2, c=4, **{"b":3}') |
| 595 | + self.assertEqualCallArgs(f, 'b=2, **{"a":3, "c":4}') |
| 596 | + self.assertEqualCallArgs(f, '**collections.UserDict(a=2, b=3, c=4)') |
| 597 | + self.assertEqualCallArgs(f, '2, c=4, **collections.UserDict(b=3)') |
| 598 | + self.assertEqualCallArgs(f, 'b=2, **collections.UserDict(a=3, c=4)') |
| 599 | + |
| 600 | + def test_keyword_only(self): |
| 601 | + f = self.makeCallable('a=3, *, c, d=2') |
| 602 | + self.assertEqualCallArgs(f, 'c=3') |
| 603 | + self.assertEqualCallArgs(f, 'c=3, a=3') |
| 604 | + self.assertEqualCallArgs(f, 'a=2, c=4') |
| 605 | + self.assertEqualCallArgs(f, '4, c=4') |
| 606 | + self.assertEqualException(f, '') |
| 607 | + self.assertEqualException(f, '3') |
| 608 | + self.assertEqualException(f, 'a=3') |
| 609 | + self.assertEqualException(f, 'd=4') |
| 610 | + |
| 611 | + def test_multiple_features(self): |
| 612 | + f = self.makeCallable('a, b=2, *f, **g') |
| 613 | + self.assertEqualCallArgs(f, '2, 3, 7') |
| 614 | + self.assertEqualCallArgs(f, '2, 3, x=8') |
| 615 | + self.assertEqualCallArgs(f, '2, 3, x=8, *[(4,[5,6]), 7]') |
| 616 | + self.assertEqualCallArgs(f, '2, x=8, *[3, (4,[5,6]), 7], y=9') |
| 617 | + self.assertEqualCallArgs(f, 'x=8, *[2, 3, (4,[5,6])], y=9') |
| 618 | + self.assertEqualCallArgs(f, 'x=8, *collections.UserList(' |
| 619 | + '[2, 3, (4,[5,6])]), **{"y":9, "z":10}') |
| 620 | + self.assertEqualCallArgs(f, '2, x=8, *collections.UserList([3, ' |
| 621 | + '(4,[5,6])]), **collections.UserDict(' |
| 622 | + 'y=9, z=10)') |
| 623 | + |
| 624 | + def test_errors(self): |
| 625 | + f0 = self.makeCallable('') |
| 626 | + f1 = self.makeCallable('a, b') |
| 627 | + f2 = self.makeCallable('a, b=1') |
| 628 | + # f0 takes no arguments |
| 629 | + self.assertEqualException(f0, '1') |
| 630 | + self.assertEqualException(f0, 'x=1') |
| 631 | + self.assertEqualException(f0, '1,x=1') |
| 632 | + # f1 takes exactly 2 arguments |
| 633 | + self.assertEqualException(f1, '') |
| 634 | + self.assertEqualException(f1, '1') |
| 635 | + self.assertEqualException(f1, 'a=2') |
| 636 | + self.assertEqualException(f1, 'b=3') |
| 637 | + # f2 takes at least 1 argument |
| 638 | + self.assertEqualException(f2, '') |
| 639 | + self.assertEqualException(f2, 'b=3') |
| 640 | + for f in f1, f2: |
| 641 | + # f1/f2 takes exactly/at most 2 arguments |
| 642 | + self.assertEqualException(f, '2, 3, 4') |
| 643 | + self.assertEqualException(f, '1, 2, 3, a=1') |
| 644 | + self.assertEqualException(f, '2, 3, 4, c=5') |
| 645 | + self.assertEqualException(f, '2, 3, 4, a=1, c=5') |
| 646 | + # f got an unexpected keyword argument |
| 647 | + self.assertEqualException(f, 'c=2') |
| 648 | + self.assertEqualException(f, '2, c=3') |
| 649 | + self.assertEqualException(f, '2, 3, c=4') |
| 650 | + self.assertEqualException(f, '2, c=4, b=3') |
| 651 | + self.assertEqualException(f, '**{u"\u03c0\u03b9": 4}') |
| 652 | + # f got multiple values for keyword argument |
| 653 | + self.assertEqualException(f, '1, a=2') |
| 654 | + self.assertEqualException(f, '1, **{"a":2}') |
| 655 | + self.assertEqualException(f, '1, 2, b=3') |
| 656 | + # XXX: Python inconsistency |
| 657 | + # - for functions and bound methods: unexpected keyword 'c' |
| 658 | + # - for unbound methods: multiple values for keyword 'a' |
| 659 | + #self.assertEqualException(f, '1, c=3, a=2') |
| 660 | + |
| 661 | +class TestGetcallargsMethods(TestGetcallargsFunctions): |
| 662 | + |
| 663 | + def setUp(self): |
| 664 | + class Foo(object): |
| 665 | + pass |
| 666 | + self.cls = Foo |
| 667 | + self.inst = Foo() |
| 668 | + |
| 669 | + def makeCallable(self, signature): |
| 670 | + assert 'self' not in signature |
| 671 | + mk = super(TestGetcallargsMethods, self).makeCallable |
| 672 | + self.cls.method = mk('self, ' + signature) |
| 673 | + return self.inst.method |
| 674 | + |
| 675 | +class TestGetcallargsUnboundMethods(TestGetcallargsMethods): |
| 676 | + |
| 677 | + def makeCallable(self, signature): |
| 678 | + super(TestGetcallargsUnboundMethods, self).makeCallable(signature) |
| 679 | + return self.cls.method |
| 680 | + |
| 681 | + def assertEqualCallArgs(self, func, call_params_string, locs=None): |
| 682 | + return super(TestGetcallargsUnboundMethods, self).assertEqualCallArgs( |
| 683 | + *self._getAssertEqualParams(func, call_params_string, locs)) |
| 684 | + |
| 685 | + def assertEqualException(self, func, call_params_string, locs=None): |
| 686 | + return super(TestGetcallargsUnboundMethods, self).assertEqualException( |
| 687 | + *self._getAssertEqualParams(func, call_params_string, locs)) |
| 688 | + |
| 689 | + def _getAssertEqualParams(self, func, call_params_string, locs=None): |
| 690 | + assert 'inst' not in call_params_string |
| 691 | + locs = dict(locs or {}, inst=self.inst) |
| 692 | + return (func, 'inst,' + call_params_string, locs) |
| 693 | + |
522 | 694 | def test_main(): |
523 | | - run_unittest(TestDecorators, TestRetrievingSourceCode, TestOneliners, |
524 | | - TestBuggyCases, |
525 | | - TestInterpreterStack, TestClassesAndFunctions, TestPredicates) |
| 695 | + run_unittest( |
| 696 | + TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases, |
| 697 | + TestInterpreterStack, TestClassesAndFunctions, TestPredicates, |
| 698 | + TestGetcallargsFunctions, TestGetcallargsMethods, |
| 699 | + TestGetcallargsUnboundMethods) |
526 | 700 |
|
527 | 701 | if __name__ == "__main__": |
528 | 702 | test_main() |
0 commit comments