1# mode: run
2# tag: cyfunction,call,python
3# cython: binding=True
4
5#######
6# Test that Cython and Python functions can call each other in various signature combinations.
7#######
8
9py_call_noargs = eval("lambda: 'noargs'")
10py_call_onearg = eval("lambda arg: arg")
11py_call_twoargs = eval("lambda arg, arg2: (arg, arg2)")
12py_call_starargs = eval("lambda *args: args")
13py_call_pos_and_starargs = eval("lambda arg, *args: (arg, args)")
14py_call_starstarargs = eval("lambda **kw: sorted(kw.items())")
15py_call_args_and_starstarargs = eval("lambda *args, **kw: (args, sorted(kw.items()))")
16
17
18def cy_call_noargs():
19    """
20    >>> cy_call_noargs()
21    'noargs'
22    """
23    return py_call_noargs()
24
25
26def cy_call_onearg(f):
27    """
28    >>> cy_call_onearg(py_call_onearg)
29    'onearg'
30    >>> try: cy_call_onearg(py_call_noargs)
31    ... except TypeError: pass
32    ... else: print("FAILED!")
33    >>> try: cy_call_onearg(py_call_twoargs)
34    ... except TypeError: pass
35    ... else: print("FAILED!")
36
37    >>> class Class(object):
38    ...     def method(self, arg): return arg
39
40    >>> cy_call_onearg(Class().method)
41    'onearg'
42    """
43    return f('onearg')
44
45
46def cy_call_twoargs(f, arg):
47    """
48    >>> cy_call_twoargs(py_call_twoargs, 132)
49    (132, 'twoargs')
50
51    >>> class Class2(object):
52    ...     def method(self, arg, arg2): return arg, arg2
53    >>> cy_call_twoargs(Class2().method, 123)
54    (123, 'twoargs')
55
56    >>> class Class1(object):
57    ...     def method(self, arg): return arg
58    >>> cy_call_twoargs(Class1.method, Class1())
59    'twoargs'
60    """
61    return f(arg, 'twoargs')
62
63
64def cy_call_two_kwargs(f, arg):
65    """
66    >>> cy_call_two_kwargs(py_call_twoargs, arg=132)
67    (132, 'two-kwargs')
68    >>> cy_call_two_kwargs(f=py_call_twoargs, arg=132)
69    (132, 'two-kwargs')
70    >>> cy_call_two_kwargs(arg=132, f=py_call_twoargs)
71    (132, 'two-kwargs')
72
73    >>> class Class(object):
74    ...     def method(self, arg, arg2): return arg, arg2
75
76    >>> cy_call_two_kwargs(Class().method, 123)
77    (123, 'two-kwargs')
78    """
79    return f(arg2='two-kwargs', arg=arg)
80
81
82def cy_call_starargs(*args):
83    """
84    >>> cy_call_starargs()
85    ()
86    >>> cy_call_starargs(1)
87    (1,)
88    >>> cy_call_starargs(1, 2)
89    (1, 2)
90    >>> cy_call_starargs(1, 2, 3)
91    (1, 2, 3)
92    """
93    return py_call_starargs(*args)
94
95
96def cy_call_pos_and_starargs(f, *args):
97    """
98    >>> cy_call_pos_and_starargs(py_call_onearg)
99    'no-arg'
100    >>> cy_call_pos_and_starargs(py_call_onearg, 123)
101    123
102    >>> cy_call_pos_and_starargs(py_call_twoargs, 123, 321)
103    (123, 321)
104    >>> cy_call_pos_and_starargs(py_call_starargs)
105    ('no-arg',)
106    >>> cy_call_pos_and_starargs(py_call_starargs, 123)
107    (123,)
108    >>> cy_call_pos_and_starargs(py_call_starargs, 123, 321)
109    (123, 321)
110    >>> cy_call_pos_and_starargs(py_call_pos_and_starargs)
111    ('no-arg', ())
112    >>> cy_call_pos_and_starargs(py_call_pos_and_starargs, 123)
113    (123, ())
114    >>> cy_call_pos_and_starargs(py_call_pos_and_starargs, 123, 321)
115    (123, (321,))
116    >>> cy_call_pos_and_starargs(py_call_pos_and_starargs, 123, 321, 234)
117    (123, (321, 234))
118
119    >>> class Class(object):
120    ...     def method(self, arg, arg2): return arg, arg2
121
122    >>> cy_call_pos_and_starargs(Class().method, 123, 321)
123    (123, 321)
124    >>> cy_call_pos_and_starargs(Class.method, Class(), 123, 321)
125    (123, 321)
126    """
127    return f(args[0] if args else 'no-arg', *args[1:])
128
129
130def cy_call_starstarargs(**kw):
131    """
132    >>> kw = {}
133    >>> cy_call_starstarargs(**kw)
134    []
135    >>> kw = {'a': 123}
136    >>> cy_call_starstarargs(**kw)
137    [('a', 123)]
138    >>> kw = {'a': 123, 'b': 321}
139    >>> cy_call_starstarargs(**kw)
140    [('a', 123), ('b', 321)]
141    """
142    return py_call_starstarargs(**kw)
143
144
145def cy_call_kw_and_starstarargs(f=None, arg1=None, **kw):
146    """
147    >>> kw = {}
148    >>> cy_call_kw_and_starstarargs(**kw)
149    [('arg', None)]
150    >>> try: cy_call_kw_and_starstarargs(py_call_noargs, **kw)
151    ... except TypeError: pass
152    >>> try: cy_call_kw_and_starstarargs(py_call_twoargs, **kw)
153    ... except TypeError: pass
154    ... else: print("FAILED!")
155    >>> cy_call_kw_and_starstarargs(py_call_onearg, **kw)
156    >>> cy_call_kw_and_starstarargs(f=py_call_onearg, **kw)
157    >>> cy_call_kw_and_starstarargs(py_call_pos_and_starargs, **kw)
158    (None, ())
159
160    >>> kw = {'arg1': 123}
161    >>> cy_call_kw_and_starstarargs(**kw)
162    [('arg', 123)]
163    >>> cy_call_kw_and_starstarargs(py_call_onearg, **kw)
164    123
165    >>> cy_call_kw_and_starstarargs(f=py_call_onearg, **kw)
166    123
167    >>> cy_call_kw_and_starstarargs(py_call_twoargs, arg2=321, **kw)
168    (123, 321)
169    >>> cy_call_kw_and_starstarargs(f=py_call_twoargs, arg2=321, **kw)
170    (123, 321)
171    >>> try: cy_call_kw_and_starstarargs(py_call_twoargs, **kw)
172    ... except TypeError: pass
173    ... else: print("FAILED!")
174    >>> try: cy_call_kw_and_starstarargs(py_call_twoargs, arg2=321, other=234, **kw)
175    ... except TypeError: pass
176    ... else: print("FAILED!")
177    >>> cy_call_kw_and_starstarargs(py_call_pos_and_starargs, **kw)
178    (123, ())
179
180    >>> try: cy_call_kw_and_starstarargs(arg=321, **kw)   # duplicate kw in Python call
181    ... except TypeError: pass
182    ... else: print("FAILED!")
183
184    >>> kw = {'a': 123}
185    >>> cy_call_kw_and_starstarargs(**kw)
186    [('a', 123), ('arg', None)]
187    >>> cy_call_kw_and_starstarargs(arg1=321, **kw)
188    [('a', 123), ('arg', 321)]
189
190    >>> kw = {'a': 123, 'b': 321}
191    >>> cy_call_kw_and_starstarargs(**kw)
192    [('a', 123), ('arg', None), ('b', 321)]
193    >>> cy_call_kw_and_starstarargs(arg1=234, **kw)
194    [('a', 123), ('arg', 234), ('b', 321)]
195
196    >>> class Class2(object):
197    ...     def method(self, arg, arg2): return arg, arg2
198
199    >>> cy_call_kw_and_starstarargs(Class2().method, arg1=123, arg2=321)
200    (123, 321)
201    """
202    return (f or py_call_starstarargs)(arg=arg1, **kw)
203
204
205def cy_call_pos_and_starstarargs(f=None, arg1=None, **kw):
206    """
207    >>> cy_call_pos_and_starstarargs(arg=123)
208    ((None,), [('arg', 123)])
209    >>> cy_call_pos_and_starstarargs(arg1=123)
210    ((123,), [])
211    >>> cy_call_pos_and_starstarargs(arg=123, arg2=321)
212    ((None,), [('arg', 123), ('arg2', 321)])
213    >>> cy_call_pos_and_starstarargs(arg1=123, arg2=321)
214    ((123,), [('arg2', 321)])
215
216    >>> class Class2(object):
217    ...     def method(self, arg, arg2=None): return arg, arg2
218
219    >>> cy_call_pos_and_starstarargs(Class2().method, 123)
220    (123, None)
221    >>> cy_call_pos_and_starstarargs(Class2().method, 123, arg2=321)
222    (123, 321)
223    >>> cy_call_pos_and_starstarargs(Class2().method, arg1=123, arg2=321)
224    (123, 321)
225    >>> cy_call_pos_and_starstarargs(Class2.method, Class2(), arg=123)
226    (123, None)
227    >>> cy_call_pos_and_starstarargs(Class2.method, Class2(), arg=123, arg2=321)
228    (123, 321)
229    >>> cy_call_pos_and_starstarargs(Class2.method, arg1=Class2(), arg=123, arg2=321)
230    (123, 321)
231    """
232    return (f or py_call_args_and_starstarargs)(arg1, **kw)
233