summaryrefslogtreecommitdiff
path: root/tests/run/cfunc_convert.pyx
blob: 89e09ea362a2824aa9b458e24d577292c85ca55f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# mode: run
# tag: autowrap
# cython: always_allow_keywords=True

cimport cython

from libc.math cimport sqrt

cdef void empty_cfunc():
    print "here"

# same signature
cdef void another_empty_cfunc():
    print "there"

def call_empty_cfunc():
    """
    >>> call_empty_cfunc()
    here
    there
    """
    cdef object py_func = empty_cfunc
    py_func()
    cdef object another_py_func = another_empty_cfunc
    another_py_func()


cdef double square_c(double x):
    return x * x

def call_square_c(x):
    """
    >>> call_square_c(2)
    4.0
    >>> call_square_c(-7)
    49.0
    """
    cdef object py_func = square_c
    return py_func(x)


def return_square_c():
    """
    >>> square_c = return_square_c()
    >>> square_c(5)
    25.0
    >>> square_c(x=4)
    16.0
    >>> square_c.__doc__   # FIXME: try to make original C function name available
    'wrap(x: float) -> float'
    """
    return square_c


def return_libc_sqrt():
    """
    >>> sqrt = return_libc_sqrt()
    >>> sqrt(9)
    3.0
    >>> sqrt(x=9)
    3.0
    >>> sqrt.__doc__
    'wrap(x: float) -> float'
    """
    return sqrt


global_csqrt = sqrt

def test_global():
    """
    >>> global_csqrt(9)
    3.0
    >>> global_csqrt.__doc__
    'wrap(x: float) -> float'
    >>> test_global()
    double (double) noexcept nogil
    Python object
    """
    print cython.typeof(sqrt)
    print cython.typeof(global_csqrt)


cdef long long rad(long long x):
    cdef long long rad = 1
    for p in range(2, <long long>sqrt(<double>x) + 1):  # MSVC++ fails without the input cast
        if x % p == 0:
            rad *= p
            while x % p == 0:
                x //= p
        if x == 1:
            break
    return rad

cdef bint abc(long long a, long long b, long long c) except -1:
    if a + b != c:
        raise ValueError("Not a valid abc candidate: (%s, %s, %s)" % (a, b, c))
    return rad(a*b*c) < c

def call_abc(a, b, c):
    """
    >>> call_abc(2, 3, 5)
    False
    >>> call_abc(1, 63, 64)
    True
    >>> call_abc(2, 3**10 * 109, 23**5)
    True
    >>> call_abc(a=2, b=3**10 * 109, c=23**5)
    True
    >>> call_abc(1, 1, 1)
    Traceback (most recent call last):
    ...
    ValueError: Not a valid abc candidate: (1, 1, 1)
    """
    cdef object py_func = abc
    return py_func(a, b, c)

def return_abc():
    """
    >>> abc = return_abc()
    >>> abc(2, 3, 5)
    False
    >>> abc.__doc__
    "wrap(a: 'long long', b: 'long long', c: 'long long') -> bool"
    """
    return abc


ctypedef double foo
cdef foo test_typedef_cfunc(foo x):
    return x

def test_typedef(x):
    """
    >>> test_typedef(100)
    100.0
    """
    return (<object>test_typedef_cfunc)(x)


cdef union my_union:
    int a
    double b

cdef struct my_struct:
    int which
    my_union y

cdef my_struct c_struct_builder(int which, int a, double b):
    cdef my_struct value
    value.which = which
    if which:
        value.y.a = a
    else:
        value.y.b = b
    return value

def return_struct_builder():
    """
    >>> make = return_struct_builder()
    >>> d = make(0, 1, 2)
    >>> d['which']
    0
    >>> d['y']['b']
    2.0
    >>> d = make(1, 1, 2)
    >>> d['which']
    1
    >>> d['y']['a']
    1
    >>> make.__doc__
    "wrap(which: 'int', a: 'int', b: float) -> 'my_struct'"
    """
    return c_struct_builder


cdef object test_object_params_cfunc(a, b):
    return a, b

def test_object_params(a, b):
    """
    >>> test_object_params(1, 'a')
    (1, 'a')
    """
    return (<object>test_object_params_cfunc)(a, b)


cdef tuple test_builtin_params_cfunc(list a, dict b):
    return a, b

def test_builtin_params(a, b):
    """
    >>> test_builtin_params([], {})
    ([], {})
    >>> test_builtin_params(1, 2)
    Traceback (most recent call last):
    ...
    TypeError: Argument 'a' has incorrect type (expected list, got int)
    """
    return (<object>test_builtin_params_cfunc)(a, b)

def return_builtin_params_cfunc():
    """
    >>> cfunc = return_builtin_params_cfunc()
    >>> cfunc([1, 2], {'a': 3})
    ([1, 2], {'a': 3})
    >>> cfunc.__doc__
    'wrap(a: list, b: dict) -> tuple'
    """
    return test_builtin_params_cfunc


cdef class A:
    def __repr__(self):
        return self.__class__.__name__

cdef class B(A):
    pass

cdef A test_cdef_class_params_cfunc(A a, B b):
    return b

def test_cdef_class_params(a, b):
    """
    >>> test_cdef_class_params(A(), B())
    B
    >>> test_cdef_class_params(B(), A())
    Traceback (most recent call last):
    ...
    TypeError: Argument 'b' has incorrect type (expected cfunc_convert.B, got cfunc_convert.A)
    """
    return (<object>test_cdef_class_params_cfunc)(a, b)

# There were a few cases where duplicate utility code definitions (i.e. with the same name)
# could be generated, causing C compile errors. This file tests them.

cdef cfunc_dup_f1(x, r):
    return "f1"

cdef cfunc_dup_f2(x1, r):
    return "f2"

def make_map():
    """
    https://github.com/cython/cython/issues/3716
    This is testing the generation of wrappers for f1 and f2
    >>> for k, f in make_map().items():
    ...    print(k == f(0, 0))  # in both cases the functions should just return their name
    True
    True

    # Test passing of keyword arguments
    >>> print(make_map()['f1'](x=1, r=2))
    f1
    >>> make_map()['f1'](x1=1, r=2)  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    TypeError: ...
    >>> print(make_map()['f2'](x1=1, r=2))
    f2
    >>> make_map()['f2'](x=1, r=2)  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    TypeError: ...
    """
    cdef map = {
        "f1": cfunc_dup_f1,
        "f2": cfunc_dup_f2,
    }
    return map


cdef class HasCdefFunc:
    cdef int x
    def __init__(self, x):
        self.x = x

    cdef int func(self, int y):
        return self.x + y

def test_unbound_methods():
    """
    >>> f = test_unbound_methods()
    >>> f(HasCdefFunc(1), 2)
    3
    """
    return HasCdefFunc.func

def test_bound_methods():
    """
    >>> f = test_bound_methods()
    >>> f(2)
    3
    """
    return HasCdefFunc(1).func