| # -*- coding: utf-8 -*- | |
| """Doctest for method/function calls. | |
| We're going the use these types for extra testing | |
| >>> from UserList import UserList | |
| >>> from UserDict import UserDict | |
| We're defining four helper functions | |
| >>> def e(a,b): | |
| ... print a, b | |
| >>> def f(*a, **k): | |
| ... print a, test_support.sortdict(k) | |
| >>> def g(x, *y, **z): | |
| ... print x, y, test_support.sortdict(z) | |
| >>> def h(j=1, a=2, h=3): | |
| ... print j, a, h | |
| Argument list examples | |
| >>> f() | |
| () {} | |
| >>> f(1) | |
| (1,) {} | |
| >>> f(1, 2) | |
| (1, 2) {} | |
| >>> f(1, 2, 3) | |
| (1, 2, 3) {} | |
| >>> f(1, 2, 3, *(4, 5)) | |
| (1, 2, 3, 4, 5) {} | |
| >>> f(1, 2, 3, *[4, 5]) | |
| (1, 2, 3, 4, 5) {} | |
| >>> f(1, 2, 3, *UserList([4, 5])) | |
| (1, 2, 3, 4, 5) {} | |
| Here we add keyword arguments | |
| >>> f(1, 2, 3, **{'a':4, 'b':5}) | |
| (1, 2, 3) {'a': 4, 'b': 5} | |
| >>> f(1, 2, 3, *[4, 5], **{'a':6, 'b':7}) | |
| (1, 2, 3, 4, 5) {'a': 6, 'b': 7} | |
| >>> f(1, 2, 3, x=4, y=5, *(6, 7), **{'a':8, 'b': 9}) | |
| (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5} | |
| >>> f(1, 2, 3, **UserDict(a=4, b=5)) | |
| (1, 2, 3) {'a': 4, 'b': 5} | |
| >>> f(1, 2, 3, *(4, 5), **UserDict(a=6, b=7)) | |
| (1, 2, 3, 4, 5) {'a': 6, 'b': 7} | |
| >>> f(1, 2, 3, x=4, y=5, *(6, 7), **UserDict(a=8, b=9)) | |
| (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5} | |
| Examples with invalid arguments (TypeErrors). We're also testing the function | |
| names in the exception messages. | |
| Verify clearing of SF bug #733667 | |
| >>> e(c=4) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: e() got an unexpected keyword argument 'c' | |
| >>> g() | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: g() takes at least 1 argument (0 given) | |
| >>> g(*()) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: g() takes at least 1 argument (0 given) | |
| >>> g(*(), **{}) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: g() takes at least 1 argument (0 given) | |
| >>> g(1) | |
| 1 () {} | |
| >>> g(1, 2) | |
| 1 (2,) {} | |
| >>> g(1, 2, 3) | |
| 1 (2, 3) {} | |
| >>> g(1, 2, 3, *(4, 5)) | |
| 1 (2, 3, 4, 5) {} | |
| >>> class Nothing: pass | |
| ... | |
| >>> g(*Nothing()) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: g() argument after * must be a sequence, not instance | |
| >>> class Nothing: | |
| ... def __len__(self): return 5 | |
| ... | |
| >>> g(*Nothing()) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: g() argument after * must be a sequence, not instance | |
| >>> class Nothing(): | |
| ... def __len__(self): return 5 | |
| ... def __getitem__(self, i): | |
| ... if i<3: return i | |
| ... else: raise IndexError(i) | |
| ... | |
| >>> g(*Nothing()) | |
| 0 (1, 2) {} | |
| >>> class Nothing: | |
| ... def __init__(self): self.c = 0 | |
| ... def __iter__(self): return self | |
| ... def next(self): | |
| ... if self.c == 4: | |
| ... raise StopIteration | |
| ... c = self.c | |
| ... self.c += 1 | |
| ... return c | |
| ... | |
| >>> g(*Nothing()) | |
| 0 (1, 2, 3) {} | |
| Make sure that the function doesn't stomp the dictionary | |
| >>> d = {'a': 1, 'b': 2, 'c': 3} | |
| >>> d2 = d.copy() | |
| >>> g(1, d=4, **d) | |
| 1 () {'a': 1, 'b': 2, 'c': 3, 'd': 4} | |
| >>> d == d2 | |
| True | |
| What about willful misconduct? | |
| >>> def saboteur(**kw): | |
| ... kw['x'] = 'm' | |
| ... return kw | |
| >>> d = {} | |
| >>> kw = saboteur(a=1, **d) | |
| >>> d | |
| {} | |
| >>> g(1, 2, 3, **{'x': 4, 'y': 5}) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: g() got multiple values for keyword argument 'x' | |
| >>> f(**{1:2}) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: f() keywords must be strings | |
| >>> h(**{'e': 2}) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: h() got an unexpected keyword argument 'e' | |
| >>> h(*h) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: h() argument after * must be a sequence, not function | |
| >>> dir(*h) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: dir() argument after * must be a sequence, not function | |
| >>> None(*h) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: NoneType object argument after * must be a sequence, \ | |
| not function | |
| >>> h(**h) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: h() argument after ** must be a mapping, not function | |
| >>> dir(**h) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: dir() argument after ** must be a mapping, not function | |
| >>> None(**h) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: NoneType object argument after ** must be a mapping, \ | |
| not function | |
| >>> dir(b=1, **{'b': 1}) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: dir() got multiple values for keyword argument 'b' | |
| Another helper function | |
| >>> def f2(*a, **b): | |
| ... return a, b | |
| >>> d = {} | |
| >>> for i in xrange(512): | |
| ... key = 'k%d' % i | |
| ... d[key] = i | |
| >>> a, b = f2(1, *(2,3), **d) | |
| >>> len(a), len(b), b == d | |
| (3, 512, True) | |
| >>> class Foo: | |
| ... def method(self, arg1, arg2): | |
| ... return arg1+arg2 | |
| >>> x = Foo() | |
| >>> Foo.method(*(x, 1, 2)) | |
| 3 | |
| >>> Foo.method(x, *(1, 2)) | |
| 3 | |
| >>> Foo.method(*(1, 2, 3)) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: unbound method method() must be called with Foo instance as \ | |
| first argument (got int instance instead) | |
| >>> Foo.method(1, *[2, 3]) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: unbound method method() must be called with Foo instance as \ | |
| first argument (got int instance instead) | |
| A PyCFunction that takes only positional parameters should allow an | |
| empty keyword dictionary to pass without a complaint, but raise a | |
| TypeError if te dictionary is not empty | |
| >>> try: | |
| ... silence = id(1, *{}) | |
| ... True | |
| ... except: | |
| ... False | |
| True | |
| >>> id(1, **{'foo': 1}) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: id() takes no keyword arguments | |
| A corner case of keyword dictionary items being deleted during | |
| the function call setup. See <http://bugs.python.org/issue2016>. | |
| >>> class Name(str): | |
| ... def __eq__(self, other): | |
| ... try: | |
| ... del x[self] | |
| ... except KeyError: | |
| ... pass | |
| ... return str.__eq__(self, other) | |
| ... def __hash__(self): | |
| ... return str.__hash__(self) | |
| >>> x = {Name("a"):1, Name("b"):2} | |
| >>> def f(a, b): | |
| ... print a,b | |
| >>> f(**x) | |
| 1 2 | |
| A obscure message: | |
| >>> def f(a, b): | |
| ... pass | |
| >>> f(b=1) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: f() takes exactly 2 arguments (1 given) | |
| The number of arguments passed in includes keywords: | |
| >>> def f(a): | |
| ... pass | |
| >>> f(6, a=4, *(1, 2, 3)) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: f() takes exactly 1 argument (5 given) | |
| """ | |
| import unittest | |
| import sys | |
| from test import test_support | |
| class ExtCallTest(unittest.TestCase): | |
| def test_unicode_keywords(self): | |
| def f(a): | |
| return a | |
| self.assertEqual(f(**{u'a': 4}), 4) | |
| self.assertRaises(TypeError, f, **{u'stören': 4}) | |
| self.assertRaises(TypeError, f, **{u'someLongString':2}) | |
| try: | |
| f(a=4, **{u'a': 4}) | |
| except TypeError: | |
| pass | |
| else: | |
| self.fail("duplicate arguments didn't raise") | |
| def test_main(): | |
| test_support.run_doctest(sys.modules[__name__], True) | |
| test_support.run_unittest(ExtCallTest) | |
| if __name__ == '__main__': | |
| test_main() |