1# -*- coding: utf-8 -*-
2
3"""Doctest for method/function calls.
4
5We're going the use these types for extra testing
6
7    >>> from UserList import UserList
8    >>> from UserDict import UserDict
9
10We're defining four helper functions
11
12    >>> def e(a,b):
13    ...     print a, b
14
15    >>> def f(*a, **k):
16    ...     print a, test_support.sortdict(k)
17
18    >>> def g(x, *y, **z):
19    ...     print x, y, test_support.sortdict(z)
20
21    >>> def h(j=1, a=2, h=3):
22    ...     print j, a, h
23
24Argument list examples
25
26    >>> f()
27    () {}
28    >>> f(1)
29    (1,) {}
30    >>> f(1, 2)
31    (1, 2) {}
32    >>> f(1, 2, 3)
33    (1, 2, 3) {}
34    >>> f(1, 2, 3, *(4, 5))
35    (1, 2, 3, 4, 5) {}
36    >>> f(1, 2, 3, *[4, 5])
37    (1, 2, 3, 4, 5) {}
38    >>> f(1, 2, 3, *UserList([4, 5]))
39    (1, 2, 3, 4, 5) {}
40
41Here we add keyword arguments
42
43    >>> f(1, 2, 3, **{'a':4, 'b':5})
44    (1, 2, 3) {'a': 4, 'b': 5}
45    >>> f(1, 2, 3, *[4, 5], **{'a':6, 'b':7})
46    (1, 2, 3, 4, 5) {'a': 6, 'b': 7}
47    >>> f(1, 2, 3, x=4, y=5, *(6, 7), **{'a':8, 'b': 9})
48    (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5}
49
50    >>> f(1, 2, 3, **UserDict(a=4, b=5))
51    (1, 2, 3) {'a': 4, 'b': 5}
52    >>> f(1, 2, 3, *(4, 5), **UserDict(a=6, b=7))
53    (1, 2, 3, 4, 5) {'a': 6, 'b': 7}
54    >>> f(1, 2, 3, x=4, y=5, *(6, 7), **UserDict(a=8, b=9))
55    (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5}
56
57Examples with invalid arguments (TypeErrors). We're also testing the function
58names in the exception messages.
59
60Verify clearing of SF bug #733667
61
62    >>> e(c=4)
63    Traceback (most recent call last):
64      ...
65    TypeError: e() got an unexpected keyword argument 'c'
66
67    >>> g()
68    Traceback (most recent call last):
69      ...
70    TypeError: g() takes at least 1 argument (0 given)
71
72    >>> g(*())
73    Traceback (most recent call last):
74      ...
75    TypeError: g() takes at least 1 argument (0 given)
76
77    >>> g(*(), **{})
78    Traceback (most recent call last):
79      ...
80    TypeError: g() takes at least 1 argument (0 given)
81
82    >>> g(1)
83    1 () {}
84    >>> g(1, 2)
85    1 (2,) {}
86    >>> g(1, 2, 3)
87    1 (2, 3) {}
88    >>> g(1, 2, 3, *(4, 5))
89    1 (2, 3, 4, 5) {}
90
91    >>> class Nothing: pass
92    ...
93    >>> g(*Nothing())
94    Traceback (most recent call last):
95      ...
96    TypeError: g() argument after * must be an iterable, not instance
97
98    >>> class Nothing:
99    ...     def __len__(self): return 5
100    ...
101
102    >>> g(*Nothing())
103    Traceback (most recent call last):
104      ...
105    TypeError: g() argument after * must be an iterable, not instance
106
107    >>> class Nothing():
108    ...     def __len__(self): return 5
109    ...     def __getitem__(self, i):
110    ...         if i<3: return i
111    ...         else: raise IndexError(i)
112    ...
113
114    >>> g(*Nothing())
115    0 (1, 2) {}
116
117    >>> class Nothing:
118    ...     def __init__(self): self.c = 0
119    ...     def __iter__(self): return self
120    ...     def next(self):
121    ...         if self.c == 4:
122    ...             raise StopIteration
123    ...         c = self.c
124    ...         self.c += 1
125    ...         return c
126    ...
127
128    >>> g(*Nothing())
129    0 (1, 2, 3) {}
130
131Check for issue #4806: Does a TypeError in a generator get propagated with the
132right error message?
133
134    >>> def broken(): raise TypeError("myerror")
135    ...
136
137    >>> g(*(broken() for i in range(1)))
138    Traceback (most recent call last):
139      ...
140    TypeError: myerror
141
142Make sure that the function doesn't stomp the dictionary
143
144    >>> d = {'a': 1, 'b': 2, 'c': 3}
145    >>> d2 = d.copy()
146    >>> g(1, d=4, **d)
147    1 () {'a': 1, 'b': 2, 'c': 3, 'd': 4}
148    >>> d == d2
149    True
150
151What about willful misconduct?
152
153    >>> def saboteur(**kw):
154    ...     kw['x'] = 'm'
155    ...     return kw
156
157    >>> d = {}
158    >>> kw = saboteur(a=1, **d)
159    >>> d
160    {}
161
162
163    >>> g(1, 2, 3, **{'x': 4, 'y': 5})
164    Traceback (most recent call last):
165      ...
166    TypeError: g() got multiple values for keyword argument 'x'
167
168    >>> f(**{1:2})
169    Traceback (most recent call last):
170      ...
171    TypeError: f() keywords must be strings
172
173    >>> h(**{'e': 2})
174    Traceback (most recent call last):
175      ...
176    TypeError: h() got an unexpected keyword argument 'e'
177
178    >>> h(*h)
179    Traceback (most recent call last):
180      ...
181    TypeError: h() argument after * must be an iterable, not function
182
183    >>> h(1, *h)
184    Traceback (most recent call last):
185      ...
186    TypeError: h() argument after * must be an iterable, not function
187
188    >>> dir(*h)
189    Traceback (most recent call last):
190      ...
191    TypeError: dir() argument after * must be an iterable, not function
192
193    >>> None(*h)
194    Traceback (most recent call last):
195      ...
196    TypeError: NoneType object argument after * must be an iterable, \
197not function
198
199    >>> h(**h)
200    Traceback (most recent call last):
201      ...
202    TypeError: h() argument after ** must be a mapping, not function
203
204    >>> h(**[])
205    Traceback (most recent call last):
206      ...
207    TypeError: h() argument after ** must be a mapping, not list
208
209    >>> h(a=1, **h)
210    Traceback (most recent call last):
211      ...
212    TypeError: h() argument after ** must be a mapping, not function
213
214    >>> h(a=1, **[])
215    Traceback (most recent call last):
216      ...
217    TypeError: h() argument after ** must be a mapping, not list
218
219    >>> dir(**h)
220    Traceback (most recent call last):
221      ...
222    TypeError: dir() argument after ** must be a mapping, not function
223
224    >>> None(**h)
225    Traceback (most recent call last):
226      ...
227    TypeError: NoneType object argument after ** must be a mapping, \
228not function
229
230    >>> dir(b=1, **{'b': 1})
231    Traceback (most recent call last):
232      ...
233    TypeError: dir() got multiple values for keyword argument 'b'
234
235Another helper function
236
237    >>> def f2(*a, **b):
238    ...     return a, b
239
240
241    >>> d = {}
242    >>> for i in xrange(512):
243    ...     key = 'k%d' % i
244    ...     d[key] = i
245    >>> a, b = f2(1, *(2,3), **d)
246    >>> len(a), len(b), b == d
247    (3, 512, True)
248
249    >>> class Foo:
250    ...     def method(self, arg1, arg2):
251    ...         return arg1+arg2
252
253    >>> x = Foo()
254    >>> Foo.method(*(x, 1, 2))
255    3
256    >>> Foo.method(x, *(1, 2))
257    3
258    >>> Foo.method(*(1, 2, 3))
259    Traceback (most recent call last):
260      ...
261    TypeError: unbound method method() must be called with Foo instance as \
262first argument (got int instance instead)
263
264    >>> Foo.method(1, *[2, 3])
265    Traceback (most recent call last):
266      ...
267    TypeError: unbound method method() must be called with Foo instance as \
268first argument (got int instance instead)
269
270A PyCFunction that takes only positional parameters should allow an
271empty keyword dictionary to pass without a complaint, but raise a
272TypeError if te dictionary is not empty
273
274    >>> try:
275    ...     silence = id(1, *{})
276    ...     True
277    ... except:
278    ...     False
279    True
280
281    >>> id(1, **{'foo': 1})
282    Traceback (most recent call last):
283      ...
284    TypeError: id() takes no keyword arguments
285
286A corner case of keyword dictionary items being deleted during
287the function call setup. See <http://bugs.python.org/issue2016>.
288
289    >>> class Name(str):
290    ...     def __eq__(self, other):
291    ...         try:
292    ...              del x[self]
293    ...         except KeyError:
294    ...              pass
295    ...         return str.__eq__(self, other)
296    ...     def __hash__(self):
297    ...         return str.__hash__(self)
298
299    >>> x = {Name("a"):1, Name("b"):2}
300    >>> def f(a, b):
301    ...     print a,b
302    >>> f(**x)
303    1 2
304
305An obscure message:
306
307    >>> def f(a, b):
308    ...    pass
309    >>> f(b=1)
310    Traceback (most recent call last):
311      ...
312    TypeError: f() takes exactly 2 arguments (1 given)
313
314The number of arguments passed in includes keywords:
315
316    >>> def f(a):
317    ...    pass
318    >>> f(6, a=4, *(1, 2, 3))
319    Traceback (most recent call last):
320      ...
321    TypeError: f() takes exactly 1 argument (5 given)
322"""
323
324import unittest
325import sys
326from test import test_support
327
328
329class ExtCallTest(unittest.TestCase):
330
331    def test_unicode_keywords(self):
332        def f(a):
333            return a
334        self.assertEqual(f(**{u'a': 4}), 4)
335        self.assertRaises(TypeError, f, **{u'stören': 4})
336        self.assertRaises(TypeError, f, **{u'someLongString':2})
337        try:
338            f(a=4, **{u'a': 4})
339        except TypeError:
340            pass
341        else:
342            self.fail("duplicate arguments didn't raise")
343
344
345def test_main():
346    test_support.run_doctest(sys.modules[__name__], True)
347    test_support.run_unittest(ExtCallTest)
348
349if __name__ == '__main__':
350    test_main()
351