1# mode: run
2
3"""
4Test Python def functions without extern types
5"""
6
7cy = __import__("cython")
8cimport cython
9
10cdef extern from *:
11    int __Pyx_CyFunction_Check(object)
12
13cdef class Base(object):
14    def __repr__(self):
15        return type(self).__name__
16
17
18cdef class ExtClassA(Base):
19    pass
20
21cdef class ExtClassB(Base):
22    pass
23
24cdef enum MyEnum:
25    entry0
26    entry1
27    entry2
28    entry3
29    entry4
30
31ctypedef fused fused_t:
32    str
33    int
34    long
35    complex
36    ExtClassA
37    ExtClassB
38    MyEnum
39
40
41ctypedef ExtClassA xxxlast
42ctypedef ExtClassB aaafirst
43
44
45ctypedef fused fused_with_object:
46    aaafirst
47    object
48    xxxlast
49    int
50    long
51
52
53f = 5.6
54i = 9
55
56
57def opt_func(fused_t obj, cython.floating myf = 1.2, cython.integral myi = 7):
58    """
59    Test runtime dispatch, indexing of various kinds and optional arguments
60
61    >>> opt_func("spam", f, i)
62    str object double long
63    spam 5.60 9 5.60 9
64    >>> opt_func("spam", f, myi=i)
65    str object double long
66    spam 5.60 9 5.60 9
67    >>> opt_func("spam", myf=f, myi=i)
68    str object double long
69    spam 5.60 9 5.60 9
70    >>> opt_func[str, float, int]("spam", f, i)
71    str object float int
72    spam 5.60 9 5.60 9
73    >>> opt_func[str, cy.double, cy.long]("spam", f, i)
74    str object double long
75    spam 5.60 9 5.60 9
76    >>> opt_func[str, cy.double, cy.long]("spam", f, myi=i)
77    str object double long
78    spam 5.60 9 5.60 9
79    >>> opt_func[str, float, cy.int]("spam", f, i)
80    str object float int
81    spam 5.60 9 5.60 9
82
83
84    >>> opt_func(ExtClassA(), f, i)
85    ExtClassA double long
86    ExtClassA 5.60 9 5.60 9
87    >>> opt_func[ExtClassA, float, int](ExtClassA(), f, i)
88    ExtClassA float int
89    ExtClassA 5.60 9 5.60 9
90    >>> opt_func[ExtClassA, cy.double, cy.long](ExtClassA(), f, i)
91    ExtClassA double long
92    ExtClassA 5.60 9 5.60 9
93
94    >>> opt_func(ExtClassB(), f, i)
95    ExtClassB double long
96    ExtClassB 5.60 9 5.60 9
97    >>> opt_func[ExtClassB, cy.double, cy.long](ExtClassB(), f, i)
98    ExtClassB double long
99    ExtClassB 5.60 9 5.60 9
100
101    >>> opt_func(10, f)
102    long double long
103    10 5.60 7 5.60 9
104    >>> opt_func[int, float, int](10, f)
105    int float int
106    10 5.60 7 5.60 9
107
108    >>> opt_func(10 + 2j, myf = 2.6)
109    double complex double long
110    (10+2j) 2.60 7 5.60 9
111    >>> opt_func[cy.py_complex, float, int](10 + 2j, myf = 2.6)
112    double complex float int
113    (10+2j) 2.60 7 5.60 9
114    >>> opt_func[cy.doublecomplex, cy.float, cy.int](10 + 2j, myf = 2.6)
115    double complex float int
116    (10+2j) 2.60 7 5.60 9
117
118    >>> opt_func(object(), f)
119    Traceback (most recent call last):
120    TypeError: Function call with ambiguous argument types
121    >>> opt_func()
122    Traceback (most recent call last):
123    TypeError: Expected at least 1 argument, got 0
124    >>> opt_func("abc", f, i, 5)  # doctest: +ELLIPSIS
125    Traceback (most recent call last):
126    TypeError: ...at most 3...
127    >>> opt_func[ExtClassA, cy.float, cy.long](object(), f)
128    Traceback (most recent call last):
129    TypeError: Argument 'obj' has incorrect type (expected fused_def.ExtClassA, got object)
130    """
131    print cython.typeof(obj), cython.typeof(myf), cython.typeof(myi)
132    print obj, "%.2f" % myf, myi, "%.2f" % f, i
133
134def run_cyfunction_check():
135    """
136    tp_base of the fused function was being set incorrectly meaning
137    it wasn't being identified as a CyFunction
138    >>> run_cyfunction_check()
139    fused_cython_function
140    1
141    """
142    print(type(opt_func).__name__)
143    print(__Pyx_CyFunction_Check(opt_func))  # should be True
144
145def test_opt_func():
146    """
147    >>> test_opt_func()
148    str object double long
149    ham 5.60 4 5.60 9
150    """
151    opt_func("ham", f, entry4)
152
153
154def test_opt_func_introspection():
155    """
156    >>> opt_func.__defaults__
157    (1.2, 7)
158    >>> opt_func.__kwdefaults__
159    >>> opt_func.__annotations__
160    {}
161
162    >>> opt_func[str, float, int].__defaults__
163    (1.2, 7)
164    >>> opt_func[str, float, int].__kwdefaults__
165    >>> opt_func[str, float, int].__annotations__
166    {}
167
168    >>> opt_func[str, cy.double, cy.long].__defaults__
169    (1.2, 7)
170    >>> opt_func[str, cy.double, cy.long].__kwdefaults__
171    >>> opt_func[str, cy.double, cy.long].__annotations__
172    {}
173    """
174
175
176def func_with_object(fused_with_object obj, cython.integral myi = 7):
177    """
178    >>> func_with_object(1)
179    long long
180    1 7
181    >>> func_with_object(1, 3)
182    long long
183    1 3
184    >>> func_with_object['int', 'int'](1, 3)
185    int int
186    1 3
187    >>> func_with_object(1j, 3)
188    Python object long
189    1j 3
190    >>> func_with_object('abc', 3)
191    Python object long
192    abc 3
193    >>> func_with_object(ExtClassA(), 3)
194    xxxlast long
195    ExtClassA 3
196    >>> func_with_object(ExtClassB(), 3)
197    aaafirst long
198    ExtClassB 3
199    >>> func_with_object['object', 'long'](ExtClassA(), 3)
200    Python object long
201    ExtClassA 3
202    >>> func_with_object['object', 'long'](ExtClassB(), 3)
203    Python object long
204    ExtClassB 3
205    """
206    print cython.typeof(obj), cython.typeof(myi)
207    print obj, myi
208
209
210
211def args_kwargs(fused_t obj, cython.floating myf = 1.2, *args, **kwargs):
212    """
213    >>> args_kwargs("foo")
214    str object double
215    foo 1.20 5.60 () {}
216
217    >>> args_kwargs("eggs", f, 1, 2, [], d={})
218    str object double
219    eggs 5.60 5.60 (1, 2, []) {'d': {}}
220
221    >>> args_kwargs[str, float]("eggs", f, 1, 2, [], d={})
222    str object float
223    eggs 5.60 5.60 (1, 2, []) {'d': {}}
224
225    """
226    print cython.typeof(obj), cython.typeof(myf)
227    print obj, "%.2f" % myf, "%.2f" % f, args, kwargs
228
229
230class BaseClass(object):
231    """
232    Test fused class/static/normal methods and super() without args
233    """
234
235    @staticmethod
236    def mystaticmethod(cython.integral arg1):
237        print cython.typeof(arg1), arg1
238
239    @classmethod
240    def myclassmethod(cls, cython.integral arg1):
241        print cls, cython.typeof(arg1), arg1
242
243    def normalmethod(self, cython.integral arg1):
244        print self, cython.typeof(arg1), arg1
245
246    def __repr__(self):
247        return "<%s.%s object>" % (__name__, type(self).__name__)
248
249class SubClass(BaseClass):
250
251    @staticmethod
252    def mystaticmethod(self, cython.integral arg1):
253        print cython.typeof(arg1), arg1
254        super().mystaticmethod(arg1 + 1)
255
256    @classmethod
257    def myclassmethod(cls, cython.integral arg1):
258        print cls, cython.typeof(arg1), arg1
259        super().myclassmethod(arg1 + 1)
260
261    def normalmethod(self, cython.integral arg1):
262        print self, cython.typeof(arg1), arg1
263        super().normalmethod(arg1 + 1)
264
265class SubSubClass(SubClass):
266    pass
267
268def test_fused_def_super():
269    """
270    >>> test_fused_def_super()
271    long 10
272    long 11
273    long 11
274    long 12
275    short 12
276    long 13
277    short 13
278    long 14
279    <class 'fused_def.SubClass'> long 14
280    <class 'fused_def.SubClass'> long 15
281    <class 'fused_def.SubClass'> long 15
282    <class 'fused_def.SubClass'> long 16
283    <class 'fused_def.SubClass'> short 16
284    <class 'fused_def.SubClass'> long 17
285    <class 'fused_def.SubClass'> short 17
286    <class 'fused_def.SubClass'> long 18
287    <fused_def.SubClass object> long 18
288    <fused_def.SubClass object> long 19
289    <fused_def.SubClass object> long 19
290    <fused_def.SubClass object> long 20
291    <fused_def.SubClass object> short 20
292    <fused_def.SubClass object> long 21
293    <fused_def.SubClass object> short 21
294    <fused_def.SubClass object> long 22
295    """
296    obj = SubClass()
297    cls = SubClass
298
299    obj.mystaticmethod(obj, 10)
300    cls.mystaticmethod(obj, 11)
301    obj.mystaticmethod[cy.short](obj, 12)
302    cls.mystaticmethod[cy.short](obj, 13)
303
304    obj.myclassmethod(14)
305    cls.myclassmethod(15)
306    obj.myclassmethod[cy.short](16)
307    cls.myclassmethod[cy.short](17)
308
309    obj.normalmethod(18)
310    cls.normalmethod(obj, 19)
311    obj.normalmethod[cy.short](20)
312    cls.normalmethod[cy.short](obj, 21)
313
314def test_fused_def_classmethod():
315    """
316    >>> test_fused_def_classmethod()
317    <class 'fused_def.SubSubClass'> long 10
318    <class 'fused_def.SubSubClass'> long 11
319    <class 'fused_def.SubSubClass'> long 11
320    <class 'fused_def.SubSubClass'> long 12
321    <class 'fused_def.SubSubClass'> short 12
322    <class 'fused_def.SubSubClass'> long 13
323    <class 'fused_def.SubSubClass'> short 13
324    <class 'fused_def.SubSubClass'> long 14
325    """
326    SubSubClass().myclassmethod(10)
327    SubSubClass.myclassmethod(11)
328
329    SubSubClass().myclassmethod[cy.short](12)
330    SubSubClass.myclassmethod[cy.short](13)
331
332cdef class CBaseClass(object):
333    """
334    Test fused def and cpdef methods in cdef classes.
335
336    >>> import cython as cy
337    >>> obj = CBaseClass()
338    >>> cls = CBaseClass
339
340    >>> obj.mystaticmethod(10)
341    long 10
342    >>> obj.mystaticmethod[cy.short](10)
343    short 10
344    >>> cls.mystaticmethod(10)
345    long 10
346    >>> cls.mystaticmethod[cy.short](10)
347    short 10
348
349    >>> obj.myclassmethod(10)
350    CBaseClass long 10
351    >>> obj.myclassmethod[cy.short](10)
352    CBaseClass short 10
353    >>> cls.myclassmethod(10)
354    CBaseClass long 10
355    >>> cls.myclassmethod[cy.short](10)
356    CBaseClass short 10
357
358    >>> obj.normalmethod(10, 11, 12)
359    <fused_def.CBaseClass object> long 10 11 12
360    >>> obj.normalmethod[cy.short](10, 11, 12)
361    <fused_def.CBaseClass object> short 10 11 12
362    >>> cls.normalmethod(obj, 10, 11, 12)
363    <fused_def.CBaseClass object> long 10 11 12
364    >>> cls.normalmethod[cy.short](obj, 10, 11, 12)
365    <fused_def.CBaseClass object> short 10 11 12
366
367    >>> obj.cpdefmethod(10)
368    <fused_def.CBaseClass object> long 10
369    >>> obj.cpdefmethod[cy.short](10)
370    <fused_def.CBaseClass object> short 10
371    >>> cls.cpdefmethod(obj, 10)
372    <fused_def.CBaseClass object> long 10
373    >>> cls.cpdefmethod[cy.short](obj, 10)
374    <fused_def.CBaseClass object> short 10
375    """
376
377    @staticmethod
378    def mystaticmethod(cython.integral arg1):
379        print cython.typeof(arg1), arg1
380
381    @classmethod
382    def myclassmethod(cls, cython.integral arg1):
383        print cls.__name__, cython.typeof(arg1), arg1
384
385    def normalmethod(self, cython.integral arg1, arg2, arg3):
386        print self, cython.typeof(arg1), arg1, arg2, arg3
387
388    cpdef cpdefmethod(self, cython.integral arg1):
389        print self, cython.typeof(arg1), arg1
390
391    def __repr__(self):
392        return "<%s.%s object>" % (__name__, type(self).__name__)
393
394def getcode(func):
395    return getattr(func, '__code__', None) or func.func_code
396
397def test_code_object(cython.floating dummy = 2.0):
398    """
399    A test for default arguments is in cyfunction_defaults
400
401    >>> getcode(test_code_object) is getcode(test_code_object[float])
402    True
403    """
404
405def create_dec(value):
406    def dec(f):
407        if not hasattr(f, 'order'):
408            f.order = []
409        f.order.append(value)
410        return f
411    return dec
412
413@create_dec(1)
414@create_dec(2)
415@create_dec(3)
416def test_decorators(cython.floating arg):
417    """
418    >>> test_decorators.order
419    [3, 2, 1]
420    """
421
422@cython.binding(True)
423def bind_me(self, cython.floating a=1.):
424    return a
425
426cdef class HasBound:
427    """
428    Using default arguments of bound specialized fused functions used to cause a segfault
429    https://github.com/cython/cython/issues/3370
430    >>> inst = HasBound()
431    >>> inst.func()
432    1.0
433    >>> inst.func(2)
434    2.0
435    >>> inst.func_fused()
436    1.0
437    >>> inst.func_fused(2.)
438    2.0
439    >>> bind_me.__defaults__
440    (1.0,)
441    >>> inst.func.__defaults__
442    (1.0,)
443    >>> inst.func_fused.__defaults__
444    (1.0,)
445    """
446    func = bind_me[float]
447
448    func_fused = bind_me
449