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