test_extcall.py revision ee8712cda46338d223509cc5751fd36509ad3860
1"""Doctest for method/function calls. 2 3We're going the use these types for extra testing 4 5 >>> from collections import UserList 6 >>> from collections import UserDict 7 8We're defining four helper functions 9 10 >>> def e(a,b): 11 ... print(a, b) 12 13 >>> def f(*a, **k): 14 ... print(a, support.sortdict(k)) 15 16 >>> def g(x, *y, **z): 17 ... print(x, y, support.sortdict(z)) 18 19 >>> def h(j=1, a=2, h=3): 20 ... print(j, a, h) 21 22Argument list examples 23 24 >>> f() 25 () {} 26 >>> f(1) 27 (1,) {} 28 >>> f(1, 2) 29 (1, 2) {} 30 >>> f(1, 2, 3) 31 (1, 2, 3) {} 32 >>> f(1, 2, 3, *(4, 5)) 33 (1, 2, 3, 4, 5) {} 34 >>> f(1, 2, 3, *[4, 5]) 35 (1, 2, 3, 4, 5) {} 36 >>> f(1, 2, 3, *UserList([4, 5])) 37 (1, 2, 3, 4, 5) {} 38 39Here we add keyword arguments 40 41 >>> f(1, 2, 3, **{'a':4, 'b':5}) 42 (1, 2, 3) {'a': 4, 'b': 5} 43 >>> f(1, 2, 3, *[4, 5], **{'a':6, 'b':7}) 44 (1, 2, 3, 4, 5) {'a': 6, 'b': 7} 45 >>> f(1, 2, 3, x=4, y=5, *(6, 7), **{'a':8, 'b': 9}) 46 (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5} 47 48 >>> f(1, 2, 3, **UserDict(a=4, b=5)) 49 (1, 2, 3) {'a': 4, 'b': 5} 50 >>> f(1, 2, 3, *(4, 5), **UserDict(a=6, b=7)) 51 (1, 2, 3, 4, 5) {'a': 6, 'b': 7} 52 >>> f(1, 2, 3, x=4, y=5, *(6, 7), **UserDict(a=8, b=9)) 53 (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5} 54 55Examples with invalid arguments (TypeErrors). We're also testing the function 56names in the exception messages. 57 58Verify clearing of SF bug #733667 59 60 >>> e(c=4) 61 Traceback (most recent call last): 62 ... 63 TypeError: e() got an unexpected keyword argument 'c' 64 65 >>> g() 66 Traceback (most recent call last): 67 ... 68 TypeError: g() takes at least 1 positional argument (0 given) 69 70 >>> g(*()) 71 Traceback (most recent call last): 72 ... 73 TypeError: g() takes at least 1 positional argument (0 given) 74 75 >>> g(*(), **{}) 76 Traceback (most recent call last): 77 ... 78 TypeError: g() takes at least 1 positional argument (0 given) 79 80 >>> g(1) 81 1 () {} 82 >>> g(1, 2) 83 1 (2,) {} 84 >>> g(1, 2, 3) 85 1 (2, 3) {} 86 >>> g(1, 2, 3, *(4, 5)) 87 1 (2, 3, 4, 5) {} 88 89 >>> class Nothing: pass 90 ... 91 >>> g(*Nothing()) 92 Traceback (most recent call last): 93 ... 94 TypeError: g() argument after * must be a sequence, not Nothing 95 96 >>> class Nothing: 97 ... def __len__(self): return 5 98 ... 99 100 >>> g(*Nothing()) 101 Traceback (most recent call last): 102 ... 103 TypeError: g() argument after * must be a sequence, not Nothing 104 105 >>> class Nothing(): 106 ... def __len__(self): return 5 107 ... def __getitem__(self, i): 108 ... if i<3: return i 109 ... else: raise IndexError(i) 110 ... 111 112 >>> g(*Nothing()) 113 0 (1, 2) {} 114 115 >>> class Nothing: 116 ... def __init__(self): self.c = 0 117 ... def __iter__(self): return self 118 ... def __next__(self): 119 ... if self.c == 4: 120 ... raise StopIteration 121 ... c = self.c 122 ... self.c += 1 123 ... return c 124 ... 125 126 >>> g(*Nothing()) 127 0 (1, 2, 3) {} 128 129Make sure that the function doesn't stomp the dictionary 130 131 >>> d = {'a': 1, 'b': 2, 'c': 3} 132 >>> d2 = d.copy() 133 >>> g(1, d=4, **d) 134 1 () {'a': 1, 'b': 2, 'c': 3, 'd': 4} 135 >>> d == d2 136 True 137 138What about willful misconduct? 139 140 >>> def saboteur(**kw): 141 ... kw['x'] = 'm' 142 ... return kw 143 144 >>> d = {} 145 >>> kw = saboteur(a=1, **d) 146 >>> d 147 {} 148 149 150 >>> g(1, 2, 3, **{'x': 4, 'y': 5}) 151 Traceback (most recent call last): 152 ... 153 TypeError: g() got multiple values for keyword argument 'x' 154 155 >>> f(**{1:2}) 156 Traceback (most recent call last): 157 ... 158 TypeError: f() keywords must be strings 159 160 >>> h(**{'e': 2}) 161 Traceback (most recent call last): 162 ... 163 TypeError: h() got an unexpected keyword argument 'e' 164 165 >>> h(*h) 166 Traceback (most recent call last): 167 ... 168 TypeError: h() argument after * must be a sequence, not function 169 170 >>> dir(*h) 171 Traceback (most recent call last): 172 ... 173 TypeError: dir() argument after * must be a sequence, not function 174 175 >>> None(*h) 176 Traceback (most recent call last): 177 ... 178 TypeError: NoneType object argument after * must be a sequence, \ 179not function 180 181 >>> h(**h) 182 Traceback (most recent call last): 183 ... 184 TypeError: h() argument after ** must be a mapping, not function 185 186 >>> dir(**h) 187 Traceback (most recent call last): 188 ... 189 TypeError: dir() argument after ** must be a mapping, not function 190 191 >>> None(**h) 192 Traceback (most recent call last): 193 ... 194 TypeError: NoneType object argument after ** must be a mapping, \ 195not function 196 197 >>> dir(b=1, **{'b': 1}) 198 Traceback (most recent call last): 199 ... 200 TypeError: dir() got multiple values for keyword argument 'b' 201 202Another helper function 203 204 >>> def f2(*a, **b): 205 ... return a, b 206 207 208 >>> d = {} 209 >>> for i in range(512): 210 ... key = 'k%d' % i 211 ... d[key] = i 212 >>> a, b = f2(1, *(2,3), **d) 213 >>> len(a), len(b), b == d 214 (3, 512, True) 215 216 >>> class Foo: 217 ... def method(self, arg1, arg2): 218 ... return arg1+arg2 219 220 >>> x = Foo() 221 >>> Foo.method(*(x, 1, 2)) 222 3 223 >>> Foo.method(x, *(1, 2)) 224 3 225 >>> Foo.method(*(1, 2, 3)) 226 5 227 >>> Foo.method(1, *[2, 3]) 228 5 229 230A PyCFunction that takes only positional parameters shoud allow an 231empty keyword dictionary to pass without a complaint, but raise a 232TypeError if te dictionary is not empty 233 234 >>> try: 235 ... silence = id(1, *{}) 236 ... True 237 ... except: 238 ... False 239 True 240 241 >>> id(1, **{'foo': 1}) 242 Traceback (most recent call last): 243 ... 244 TypeError: id() takes no keyword arguments 245 246""" 247 248from test import support 249 250def test_main(): 251 from test import test_extcall # self import 252 support.run_doctest(test_extcall, True) 253 254if __name__ == '__main__': 255 test_main() 256