# mode: run # ticket: 1772 cimport cython from cython.view cimport array from cython cimport integral from cpython cimport Py_INCREF from Cython import Shadow as pure_cython ctypedef char * string_t # floating = cython.fused_type(float, double) floating # integral = cython.fused_type(int, long) integral ctypedef cython.floating floating fused_type1 = cython.fused_type(int, long, float, double, string_t) fused_type2 = cython.fused_type(string_t) ctypedef fused_type1 *composed_t other_t = cython.fused_type(int, double) ctypedef double *p_double ctypedef int *p_int fused_type3 = cython.fused_type(int, double) fused_composite = cython.fused_type(fused_type2, fused_type3) just_float = cython.fused_type(float) def test_pure(): """ >>> test_pure() 10 """ mytype = pure_cython.typedef(pure_cython.fused_type(int, long, complex)) print mytype(10) cdef cdef_func_with_fused_args(fused_type1 x, fused_type1 y, fused_type2 z): if fused_type1 is string_t: print x.decode('ascii'), y.decode('ascii'), z.decode('ascii') else: print x, y, z.decode('ascii') return x + y def test_cdef_func_with_fused_args(): """ >>> test_cdef_func_with_fused_args() spam ham eggs spamham 10 20 butter 30 4.2 8.6 bunny 12.8 """ print cdef_func_with_fused_args(b'spam', b'ham', b'eggs').decode('ascii') print cdef_func_with_fused_args(10, 20, b'butter') print cdef_func_with_fused_args(4.2, 8.6, b'bunny') cdef fused_type1 fused_with_pointer(fused_type1 *array): for i in range(5): if fused_type1 is string_t: print array[i].decode('ascii') else: print array[i] obj = array[0] + array[1] + array[2] + array[3] + array[4] # if cython.typeof(fused_type1) is string_t: Py_INCREF(obj) return obj def test_fused_with_pointer(): """ >>> test_fused_with_pointer() 0 1 2 3 4 10 0 1 2 3 4 10 0.0 1.0 2.0 3.0 4.0 10.0 humpty dumpty fall splatch breakfast humptydumptyfallsplatchbreakfast """ cdef int[5] int_array cdef long[5] long_array cdef float[5] float_array cdef string_t[5] string_array cdef char *s strings = [b"humpty", b"dumpty", b"fall", b"splatch", b"breakfast"] for i in range(5): int_array[i] = i long_array[i] = i float_array[i] = i s = strings[i] string_array[i] = s print fused_with_pointer(int_array) print print fused_with_pointer(long_array) print print fused_with_pointer(float_array) print print fused_with_pointer(string_array).decode('ascii') cdef fused_type1* fused_pointer_except_null(fused_type1* x) except NULL: if fused_type1 is string_t: assert(bool(x[0])) else: assert(x[0] < 10) return x def test_fused_pointer_except_null(value): """ >>> test_fused_pointer_except_null(1) 1 >>> test_fused_pointer_except_null(2.0) 2.0 >>> test_fused_pointer_except_null(b'foo') foo >>> test_fused_pointer_except_null(16) Traceback (most recent call last): AssertionError >>> test_fused_pointer_except_null(15.1) Traceback (most recent call last): AssertionError >>> test_fused_pointer_except_null(b'') Traceback (most recent call last): AssertionError """ if isinstance(value, int): test_int = cython.declare(cython.int, value) print fused_pointer_except_null(&test_int)[0] elif isinstance(value, float): test_float = cython.declare(cython.float, value) print fused_pointer_except_null(&test_float)[0] elif isinstance(value, bytes): test_str = cython.declare(string_t, value) print fused_pointer_except_null(&test_str)[0].decode('ascii') include "../testsupport/cythonarrayutil.pxi" cpdef cython.integral test_fused_memoryviews(cython.integral[:, ::1] a): """ >>> import cython >>> a = create_array((3, 5), mode="c") >>> test_fused_memoryviews[cython.int](a) 7 """ return a[1, 2] ctypedef int[:, ::1] memview_int ctypedef long[:, ::1] memview_long memview_t = cython.fused_type(memview_int, memview_long) def test_fused_memoryview_def(memview_t a): """ >>> a = create_array((3, 5), mode="c") >>> test_fused_memoryview_def["memview_int"](a) 7 """ return a[1, 2] cdef test_specialize(fused_type1 x, fused_type1 *y, composed_t z, other_t *a): cdef fused_type1 result if composed_t is p_double: print "double pointer" if fused_type1 in floating: result = x + y[0] + z[0] + a[0] return result def test_specializations(): """ >>> test_specializations() double pointer double pointer double pointer double pointer double pointer """ cdef object (*f)(double, double *, double *, int *) cdef double somedouble = 2.2 cdef double otherdouble = 3.3 cdef int someint = 4 cdef p_double somedouble_p = &somedouble cdef p_double otherdouble_p = &otherdouble cdef p_int someint_p = &someint f = test_specialize assert f(1.1, somedouble_p, otherdouble_p, someint_p) == 10.6 f = test_specialize assert f(1.1, somedouble_p, otherdouble_p, someint_p) == 10.6 assert ( test_specialize)(1.1, somedouble_p, otherdouble_p, someint_p) == 10.6 f = test_specialize[double, int] assert f(1.1, somedouble_p, otherdouble_p, someint_p) == 10.6 assert test_specialize[double, int](1.1, somedouble_p, otherdouble_p, someint_p) == 10.6 # The following cases are not supported # f = test_specialize[double][p_int] # print f(1.1, somedouble_p, otherdouble_p) # print # print test_specialize[double][p_int](1.1, somedouble_p, otherdouble_p) # print # print test_specialize[double](1.1, somedouble_p, otherdouble_p) # print cdef opt_args(integral x, floating y = 4.0): print x, y def test_opt_args(): """ >>> test_opt_args() 3 4.0 3 4.0 3 4.0 3 4.0 """ opt_args[int, float](3) opt_args[int, double](3) opt_args[int, float](3, 4.0) opt_args[int, double](3, 4.0) class NormalClass(object): def method(self, cython.integral i): print cython.typeof(i), i def test_normal_class(): """ >>> test_normal_class() short 10 """ NormalClass().method[pure_cython.short](10) def test_normal_class_refcount(): """ >>> test_normal_class_refcount() short 10 0 """ import sys x = NormalClass() c = sys.getrefcount(x) x.method[pure_cython.short](10) print sys.getrefcount(x) - c def test_fused_declarations(cython.integral i, cython.floating f): """ >>> test_fused_declarations[pure_cython.short, pure_cython.float](5, 6.6) short float 25 43.56 >>> test_fused_declarations[pure_cython.long, pure_cython.double](5, 6.6) long double 25 43.56 """ cdef cython.integral squared_int = i * i cdef cython.floating squared_float = f * f assert cython.typeof(squared_int) == cython.typeof(i) assert cython.typeof(squared_float) == cython.typeof(f) print cython.typeof(squared_int) print cython.typeof(squared_float) print '%d %.2f' % (squared_int, squared_float) def test_sizeof_fused_type(fused_type1 b): """ >>> test_sizeof_fused_type[pure_cython.double](11.1) """ t = sizeof(b), sizeof(fused_type1), sizeof(double) assert t[0] == t[1] == t[2], t def get_array(itemsize, format): result = array((10,), itemsize, format) result[5] = 5.0 result[6] = 6.0 return result def get_intc_array(): result = array((10,), sizeof(int), 'i') result[5] = 5 result[6] = 6 return result def test_fused_memslice_dtype(cython.floating[:] array): """ Note: the np.ndarray dtype test is in numpy_test >>> import cython >>> sorted(test_fused_memslice_dtype.__signatures__) ['double', 'float'] >>> test_fused_memslice_dtype[cython.double](get_array(8, 'd')) double[:] double[:] 5.0 6.0 >>> test_fused_memslice_dtype[cython.float](get_array(4, 'f')) float[:] float[:] 5.0 6.0 # None should evaluate to *something* (currently the first # in the list, but this shouldn't be a hard requirement) >>> test_fused_memslice_dtype(None) float[:] >>> test_fused_memslice_dtype[cython.double](None) double[:] """ if array is None: print(cython.typeof(array)) return cdef cython.floating[:] otherarray = array[0:100:1] print cython.typeof(array), cython.typeof(otherarray), \ array[5], otherarray[6] cdef cython.floating value; cdef cython.floating[:] test_cast = &value def test_fused_memslice_dtype_repeated(cython.floating[:] array1, cython.floating[:] array2): """ Note: the np.ndarray dtype test is in numpy_test >>> sorted(test_fused_memslice_dtype_repeated.__signatures__) ['double', 'float'] >>> test_fused_memslice_dtype_repeated(get_array(8, 'd'), get_array(8, 'd')) double[:] double[:] >>> test_fused_memslice_dtype_repeated(get_array(4, 'f'), get_array(4, 'f')) float[:] float[:] >>> test_fused_memslice_dtype_repeated(get_array(8, 'd'), get_array(4, 'f')) Traceback (most recent call last): ValueError: Buffer dtype mismatch, expected 'double' but got 'float' """ print cython.typeof(array1), cython.typeof(array2) def test_fused_memslice_dtype_repeated_2(cython.floating[:] array1, cython.floating[:] array2, fused_type3[:] array3): """ Note: the np.ndarray dtype test is in numpy_test >>> sorted(test_fused_memslice_dtype_repeated_2.__signatures__) ['double|double', 'double|int', 'float|double', 'float|int'] >>> test_fused_memslice_dtype_repeated_2(get_array(8, 'd'), get_array(8, 'd'), get_array(8, 'd')) double[:] double[:] double[:] >>> test_fused_memslice_dtype_repeated_2(get_array(8, 'd'), get_array(8, 'd'), get_intc_array()) double[:] double[:] int[:] >>> test_fused_memslice_dtype_repeated_2(get_array(4, 'f'), get_array(4, 'f'), get_intc_array()) float[:] float[:] int[:] """ print cython.typeof(array1), cython.typeof(array2), cython.typeof(array3) def test_fused_const_memslice_dtype_repeated(const cython.floating[:] array1, cython.floating[:] array2): """Test fused types memory view with one being const >>> sorted(test_fused_const_memslice_dtype_repeated.__signatures__) ['double', 'float'] >>> test_fused_const_memslice_dtype_repeated(get_array(8, 'd'), get_array(8, 'd')) const double[:] double[:] >>> test_fused_const_memslice_dtype_repeated(get_array(4, 'f'), get_array(4, 'f')) const float[:] float[:] >>> test_fused_const_memslice_dtype_repeated(get_array(8, 'd'), get_array(4, 'f')) Traceback (most recent call last): ValueError: Buffer dtype mismatch, expected 'double' but got 'float' """ print cython.typeof(array1), cython.typeof(array2) def test_cython_numeric(cython.numeric arg): """ Test to see whether complex numbers have their utility code declared properly. >>> test_cython_numeric(10.0 + 1j) double complex (10+1j) """ print cython.typeof(arg), arg cdef fused ints_t: int long cdef _test_index_fused_args(cython.floating f, ints_t i): print cython.typeof(f), cython.typeof(i) def test_index_fused_args(cython.floating f, ints_t i): """ >>> import cython >>> test_index_fused_args[cython.double, cython.int](2.0, 3) double int """ _test_index_fused_args[cython.floating, ints_t](f, i) cdef _test_index_const_fused_args(const cython.floating f, const ints_t i): print(cython.typeof(f), cython.typeof(i)) def test_index_const_fused_args(const cython.floating f, const ints_t i): """Test indexing function implementation with const fused type args >>> import cython >>> test_index_const_fused_args[cython.double, cython.int](2.0, 3) ('const double', 'const int') """ _test_index_const_fused_args[cython.floating, ints_t](f, i) def test_composite(fused_composite x): """ >>> print(test_composite(b'a').decode('ascii')) a >>> test_composite(3) 6 >>> test_composite(3.0) 6.0 """ if fused_composite is string_t: return x else: return 2 * x cdef cdef_func_const_fused_arg(const cython.floating val, const fused_type1 * ptr_to_const, const (cython.floating *) const_ptr): print(val, cython.typeof(val)) print(ptr_to_const[0], cython.typeof(ptr_to_const[0])) print(const_ptr[0], cython.typeof(const_ptr[0])) ptr_to_const = NULL # pointer is not const, value is const const_ptr[0] = 0.0 # pointer is const, value is not const def test_cdef_func_with_const_fused_arg(): """Test cdef function with const fused type argument >>> test_cdef_func_with_const_fused_arg() (0.0, 'const float') (1, 'const int') (2.0, 'float') """ cdef float arg0 = 0.0 cdef int arg1 = 1 cdef float arg2 = 2.0 cdef_func_const_fused_arg(arg0, &arg1, &arg2) cdef in_check_1(just_float x): return just_float in floating cdef in_check_2(just_float x, floating y): # the "floating" on the right-hand side of the in statement should not be specialized # - the test should still work. return just_float in floating cdef in_check_3(floating x): # the floating on the left-hand side of the in statement should be specialized # but the one of the right-hand side should not (so that the test can still work). return floating in floating def test_fused_in_check(): """ It should be possible to use fused types on in "x in ...fused_type" statements even if that type is specialized in the function. >>> test_fused_in_check() True True True True """ print(in_check_1(1.0)) print(in_check_2(1.0, 2.0)) print(in_check_2[float, double](1.0, 2.0)) print(in_check_3[float](1.0)) ### see GH3642 - presence of cdef inside "unrelated" caused a type to be incorrectly inferred cdef unrelated(cython.floating x): cdef cython.floating t = 1 return t cdef handle_float(float* x): return 'float' cdef handle_double(double* x): return 'double' def convert_to_ptr(cython.floating x): """ >>> convert_to_ptr(1.0) 'double' >>> convert_to_ptr['double'](1.0) 'double' >>> convert_to_ptr['float'](1.0) 'float' """ if cython.floating is float: return handle_float(&x) elif cython.floating is double: return handle_double(&x) cdef double get_double(): return 1.0 cdef float get_float(): return 0.0 cdef call_func_pointer(cython.floating (*f)()): return f() def test_fused_func_pointer(): """ >>> test_fused_func_pointer() 1.0 0.0 """ print(call_func_pointer(get_double)) print(call_func_pointer(get_float)) cdef double get_double_from_int(int i): return i cdef call_func_pointer_with_1(cython.floating (*f)(cython.integral)): return f(1) def test_fused_func_pointer2(): """ >>> test_fused_func_pointer2() 1.0 """ print(call_func_pointer_with_1(get_double_from_int)) cdef call_function_that_calls_fused_pointer(object (*f)(cython.floating (*)(cython.integral))): if cython.floating is double and cython.integral is int: return 5*f(get_double_from_int) else: return None # practically it's hard to make this kind of function useful... def test_fused_func_pointer_multilevel(): """ >>> test_fused_func_pointer_multilevel() 5.0 None """ print(call_function_that_calls_fused_pointer(call_func_pointer_with_1[double, int])) print(call_function_that_calls_fused_pointer(call_func_pointer_with_1[float, int]))