# cython: infer_types = True cimport cython from cython cimport typeof, infer_types from cpython cimport bool ################################################## # type inference tests in 'full' mode cdef class MyType: pass def simple(): """ >>> simple() """ i = 3 assert typeof(i) == "long", typeof(i) x = 1.41 assert typeof(x) == "double", typeof(x) xptr = &x assert typeof(xptr) == "double *", typeof(xptr) xptrptr = &xptr assert typeof(xptrptr) == "double **", typeof(xptrptr) b = b"abc" assert typeof(b) == "bytes object", typeof(b) s = "abc" assert typeof(s) == "str object", typeof(s) u = u"xyz" assert typeof(u) == "unicode object", typeof(u) L = [1,2,3] assert typeof(L) == "list object", typeof(L) t = (4,5,6,()) assert typeof(t) == "tuple object", typeof(t) t2 = (4, 5.0, 6) assert typeof(t2) == "(long, double, long)", typeof(t) def builtin_types(): """ >>> builtin_types() """ b = bytes() assert typeof(b) == "bytes object", typeof(b) u = unicode() assert typeof(u) == "unicode object", typeof(u) L = list() assert typeof(L) == "list object", typeof(L) t = tuple() assert typeof(t) == "tuple object", typeof(t) d = dict() assert typeof(d) == "dict object", typeof(d) B = bool() assert typeof(B) == "bool", typeof(B) def slicing(): """ >>> slicing() """ b = b"abc" assert typeof(b) == "bytes object", typeof(b) b1 = b[1:2] assert typeof(b1) == "bytes object", typeof(b1) b2 = b[1:2:2] assert typeof(b2) == "bytes object", typeof(b2) u = u"xyz" assert typeof(u) == "unicode object", typeof(u) u1 = u[1:2] assert typeof(u1) == "unicode object", typeof(u1) u2 = u[1:2:2] assert typeof(u2) == "unicode object", typeof(u2) s = "xyz" assert typeof(s) == "str object", typeof(s) s1 = s[1:2] assert typeof(s1) == "str object", typeof(s1) s2 = s[1:2:2] assert typeof(s2) == "str object", typeof(s2) L = [1,2,3] assert typeof(L) == "list object", typeof(L) L1 = L[1:2] assert typeof(L1) == "list object", typeof(L1) L2 = L[1:2:2] assert typeof(L2) == "list object", typeof(L2) t = (4,5,6,()) assert typeof(t) == "tuple object", typeof(t) t1 = t[1:2] assert typeof(t1) == "tuple object", typeof(t1) t2 = t[1:2:2] assert typeof(t2) == "tuple object", typeof(t2) def indexing(): """ >>> indexing() """ b = b"abc" assert typeof(b) == "bytes object", typeof(b) b1 = b[1] assert typeof(b1) == "Python object", typeof(b1) # Py2: bytes, Py3: int u = u"xyz" assert typeof(u) == "unicode object", typeof(u) u1 = u[1] assert typeof(u1) == "Py_UCS4", typeof(u1) s = "xyz" assert typeof(s) == "str object", typeof(s) s1 = s[1] assert typeof(s1) == "str object", typeof(s1) L = [1,2,3] assert typeof(L) == "list object", typeof(L) L1 = L[1] assert typeof(L1) == "Python object", typeof(L1) t = (4,5,()) assert typeof(t) == "tuple object", typeof(t) t1 = t[1] assert typeof(t1) == "long", typeof(t1) t2 = ('abc', 'def', 'ghi') assert typeof(t2) == "tuple object", typeof(t2) t2_1 = t2[1] assert typeof(t2_1) == "str object", typeof(t2_1) t2_2 = t2[t[0]-3] assert typeof(t2_2) == "str object", typeof(t2_2) t5 = (b'abc', 'def', u'ghi') t5_0 = t5[0] assert typeof(t5_0) == "bytes object", typeof(t5_0) t5_1 = t5[1] assert typeof(t5_1) == "str object", typeof(t5_1) t5_2 = t5[2] assert typeof(t5_2) == "unicode object", typeof(t5_2) t5_3 = t5[t[0]-3] assert typeof(t5_3) == "Python object", typeof(t5_3) def multiple_assignments(): """ >>> multiple_assignments() """ a = 3 a = 4 a = 5 assert typeof(a) == "long", typeof(a) b = a b = 3.1 b = 3.14159 assert typeof(b) == "double", typeof(b) c = a c = b c = [1,2,3] assert typeof(c) == "Python object", typeof(c) d = b'abc' d = bytes() d = bytes(b'xyz') d = None assert typeof(d) == "bytes object", typeof(d) def arithmetic(): """ >>> arithmetic() """ a = 1 + 2 assert typeof(a) == "long", typeof(a) b = 1 + 1.5 assert typeof(b) == "double", typeof(b) c = 1 + 2 assert typeof(c) == "Python object", typeof(c) d = 1 * 1.5 ** 2 assert typeof(d) == "double", typeof(d) cdef class some_class: pass def unary_operators(): """ >>> unary_operators() """ cdef int x = 1 assert typeof(~x) == "int", typeof(~x) cdef some_class obj assert typeof(~obj) == "Python object", typeof(~obj) a = int(1) assert typeof(a) == "Python object", typeof(a) b = not int(3) assert typeof(b) == "bint", typeof(b) c = +int(3) assert typeof(c) == "Python object", typeof(c) d = -int(5) assert typeof(d) == "Python object", typeof(d) def builtin_type_operations(): """ >>> builtin_type_operations() """ b1 = b'a' * 10 b1 = 10 * b'a' b1 = 10 * b'a' * 10 assert typeof(b1) == "bytes object", typeof(b1) b2 = b'a' + b'b' assert typeof(b2) == "bytes object", typeof(b2) u1 = u'a' * 10 u1 = 10 * u'a' assert typeof(u1) == "unicode object", typeof(u1) u2 = u'a' + u'b' assert typeof(u2) == "unicode object", typeof(u2) u3 = u'a%s' % u'b' u3 = u'a%s' % 10 assert typeof(u3) == "unicode object", typeof(u3) s1 = "abc %s" % "x" s1 = "abc %s" % 10 assert typeof(s1) == "str object", typeof(s1) s2 = "abc %s" + "x" assert typeof(s2) == "str object", typeof(s2) s3 = "abc %s" * 10 s3 = "abc %s" * 10 * 10 s3 = 10 * "abc %s" * 10 assert typeof(s3) == "str object", typeof(s3) L1 = [] + [] assert typeof(L1) == "list object", typeof(L1) L2 = [] * 2 assert typeof(L2) == "list object", typeof(L2) T1 = () + () assert typeof(T1) == "tuple object", typeof(T1) T2 = () * 2 assert typeof(T2) == "tuple object", typeof(T2) def builtin_type_methods(): """ >>> builtin_type_methods() """ l = [] assert typeof(l) == 'list object', typeof(l) append = l.append assert typeof(append) == 'Python object', typeof(append) append(1) assert l == [1], str(l) cdef int cfunc(int x): return x+1 def c_functions(): """ >>> c_functions() """ f = cfunc assert typeof(f) == 'int (*)(int) except? -1', typeof(f) assert 2 == f(1) def builtin_functions(): """ >>> _abs, _getattr = builtin_functions() Python object Python object >>> _abs(-1) 1 >>> class o(object): pass >>> o.x = 1 >>> _getattr(o, 'x') 1 """ _abs = abs print(typeof(_abs)) _getattr = getattr print(typeof(_getattr)) return _abs, _getattr def cascade(): """ >>> cascade() """ a = 1.0 b = a + 2 c = b + 3 d = c + 4 assert typeof(d) == "double" e = a + b + c + d assert typeof(e) == "double" def cascaded_assignment(): """ >>> cascaded_assignment() """ a = b = c = d = 1.0 assert typeof(a) == "double" assert typeof(b) == "double" assert typeof(c) == "double" assert typeof(d) == "double" e = a + b + c + d assert typeof(e) == "double" def unpacking(x): """ >>> unpacking(0) """ a, b, c, (d, e) = x, 1, 2.0, [3, [5, 6]] assert typeof(a) == "Python object", typeof(a) assert typeof(b) == "long", typeof(b) assert typeof(c) == "double", typeof(c) assert typeof(d) == "long", typeof(d) assert typeof(e) == "list object", typeof(e) def star_unpacking(*x): """ >>> star_unpacking(1, 2) """ a, b = x c, *d = x *e, f = x *g, g = x # re-assignment assert typeof(a) == "Python object", typeof(a) assert typeof(b) == "Python object", typeof(b) assert typeof(c) == "Python object", typeof(c) assert typeof(d) == "list object", typeof(d) assert typeof(e) == "list object", typeof(e) assert typeof(f) == "Python object", typeof(f) assert typeof(g) == "Python object", typeof(f) def increment(): """ >>> increment() """ a = 5 a += 1 assert typeof(a) == "long" def loop(): """ >>> loop() """ for a in range(10): pass assert typeof(a) == "long" b = 1.0 for b in range(5): pass assert typeof(b) == "double" for c from 0 <= c < 10 by .5: pass assert typeof(c) == "double" for d in range(0, 10L, 2): pass assert typeof(a) == "long" def loop_over_charptr(): """ >>> print( loop_over_charptr() ) char """ cdef char* char_ptr_string = 'abcdefg' for c in char_ptr_string: pass return typeof(c) def loop_over_bytes_literal(): """ >>> print( loop_over_bytes_literal() ) Python object """ for c in b'abcdefg': pass return typeof(c) def loop_over_bytes(): """ >>> print( loop_over_bytes() ) Python object """ cdef bytes bytes_string = b'abcdefg' # bytes in Py2, int in Py3 for c in bytes_string: pass return typeof(c) def loop_over_str(): """ >>> print( loop_over_str() ) str object """ cdef str string = 'abcdefg' # str (bytes) in Py2, str (unicode) in Py3 for c in string: pass return typeof(c) def loop_over_unicode(): """ >>> print( loop_over_unicode() ) Py_UCS4 """ cdef unicode ustring = u'abcdefg' # Py_UCS4 can represent any Unicode character for uchar in ustring: pass return typeof(uchar) def loop_over_unicode_literal(): """ >>> print( loop_over_unicode_literal() ) Py_UCS4 """ # Py_UCS4 can represent any Unicode character for uchar in u'abcdefg': pass return typeof(uchar) def loop_over_int_array(): """ >>> print( loop_over_int_array() ) int """ cdef int[10] int_array for i in int_array: pass return typeof(i) cdef struct MyStruct: int a def loop_over_struct_ptr(): """ >>> print( loop_over_struct_ptr() ) MyStruct """ cdef MyStruct[10] a_list cdef MyStruct *a_ptr = a_list for i in a_list[:10]: pass return typeof(i) cdef unicode retu(): return u"12345" cdef bytes retb(): return b"12345" def conditional(x): """ >>> conditional(True) (True, 'Python object') >>> conditional(False) (False, 'Python object') """ if x: a = retu() else: a = retb() return type(a) is unicode, typeof(a) ################################################## # type inference tests that work in 'safe' mode @infer_types(None) def double_inference(): """ >>> values, types = double_inference() >>> values == (1.0, 1.0*2, 1.0*2.0+2.0*2.0, 1.0*2.0) True >>> types ('double', 'double', 'double', 'Python object') """ d_a = 1.0 d_b = d_a * float(2) d_c = d_a * float(some_float_value()) + d_b * float(some_float_value()) o_d = d_a * some_float_value() return (d_a,d_b,d_c,o_d), (typeof(d_a), typeof(d_b), typeof(d_c), typeof(o_d)) cdef object some_float_value(): return 2.0 @infer_types(None) @cython.test_fail_if_path_exists('//DefNode//NameNode[@type.is_pyobject = True]') @cython.test_assert_path_exists('//DefNode//NameNode[@type.is_pyobject]', '//DefNode//NameNode[@type.is_pyobject = False]') def double_loop(): """ >>> double_loop() == 1.0 * 10 True """ cdef int i d = 1.0 for i in range(9): d += 1.0 return d @infer_types(None) def safe_only(): """ >>> safe_only() """ a = 1.0 assert typeof(a) == "double", typeof(c) b = 1; assert typeof(b) == "long", typeof(b) c = MyType() assert typeof(c) == "MyType", typeof(c) for i in range(10): pass assert typeof(i) == "long", typeof(i) d = 1 res = ~d assert typeof(d) == "long", typeof(d) # we special-case inference to type str, see # trac #553 s = "abc" assert typeof(s) == "Python object", typeof(s) cdef str t = "def" assert typeof(t) == "str object", typeof(t) # potentially overflowing arithmetic e = 1 e += 1 assert typeof(e) == "Python object", typeof(e) f = 1 res = f * 10 assert typeof(f) == "Python object", typeof(f) g = 1 res = 10*(~g) assert typeof(g) == "Python object", typeof(g) for j in range(10): res = -j assert typeof(j) == "Python object", typeof(j) h = 1 res = abs(h) assert typeof(h) == "Python object", typeof(h) cdef int c_int = 1 assert typeof(abs(c_int)) == "int", typeof(abs(c_int)) # float can be inferred cdef float fl = 5.0 from_fl = fl assert typeof(from_fl) == "float", typeof(from_fl) @infer_types(None) def safe_c_functions(): """ >>> safe_c_functions() """ f = cfunc assert typeof(f) == 'int (*)(int) except? -1', typeof(f) assert 2 == f(1) @infer_types(None) def ptr_types(): """ >>> ptr_types() """ cdef int a a_ptr = &a assert typeof(a_ptr) == "int *", typeof(a_ptr) a_ptr_ptr = &a_ptr assert typeof(a_ptr_ptr) == "int **", typeof(a_ptr_ptr) cdef int[1] b b_ref = b assert typeof(b_ref) == "int *", typeof(b_ref) ptr = &a ptr = b assert typeof(ptr) == "int *", typeof(ptr) def const_types(const double x, double y, double& z): """ >>> const_types(1, 1, 1) """ a = x a = y a = z assert typeof(a) == "double", typeof(a) @infer_types(None) def args_tuple_keywords(*args, **kwargs): """ >>> args_tuple_keywords(1,2,3, a=1, b=2) """ assert typeof(args) == "tuple object", typeof(args) assert typeof(kwargs) == "dict object", typeof(kwargs) @infer_types(None) def args_tuple_keywords_reassign_same(*args, **kwargs): """ >>> args_tuple_keywords_reassign_same(1,2,3, a=1, b=2) """ assert typeof(args) == "tuple object", typeof(args) assert typeof(kwargs) == "dict object", typeof(kwargs) args = () kwargs = {} @infer_types(None) def args_tuple_keywords_reassign_pyobjects(*args, **kwargs): """ >>> args_tuple_keywords_reassign_pyobjects(1,2,3, a=1, b=2) """ assert typeof(args) == "Python object", typeof(args) assert typeof(kwargs) == "Python object", typeof(kwargs) args = [] kwargs = "test" # / A -> AA -> AAA # Base0 -> Base - # \ B -> BB # C -> CC cdef class Base0: pass cdef class Base(Base0): pass cdef class A(Base): pass cdef class AA(A): pass cdef class AAA(AA): pass cdef class B(Base): pass cdef class BB(B): pass cdef class C: pass cdef class CC(C): pass @infer_types(None) def common_extension_type_base(): """ >>> common_extension_type_base() """ x = A() x = AA() assert typeof(x) == "A", typeof(x) y = A() y = B() assert typeof(y) == "Base", typeof(y) z = AAA() z = BB() assert typeof(z) == "Base", typeof(z) w = A() w = CC() assert typeof(w) == "Python object", typeof(w) cdef class AcceptsKeywords: def __init__(self, *args, **kwds): pass @infer_types(None) def constructor_call(): """ >>> constructor_call() """ x = AcceptsKeywords(a=1, b=2) assert typeof(x) == "AcceptsKeywords", typeof(x) @infer_types(None) def large_literals(): """ >>> large_literals() """ # It's only safe to infer small integer literals. a = 10 b = 100000000000000000000000000000000 assert typeof(a) == "long", typeof(a) assert typeof(b) == "Python object", typeof(b) c, d = 10, 100000000000000000000000000000000 assert typeof(c) == "long", typeof(c) assert typeof(d) == "Python object", typeof(d) class EmptyContextManager(object): def __enter__(self): return None def __exit__(self, *args): return 0 def with_statement(): """ >>> with_statement() Python object Python object """ x = 1.0 with EmptyContextManager() as x: print(typeof(x)) print(typeof(x)) return x @cython.final cdef class TypedContextManager(object): cpdef double __enter__(self): return 2.0 def __exit__(self, *args): return 0 def with_statement_typed(): """ >>> with_statement_typed() double double 2.0 """ x = 1.0 with TypedContextManager() as x: print(typeof(x)) print(typeof(x)) return x def with_statement_untyped(): """ >>> with_statement_untyped() Python object Python object 2.0 """ x = 1.0 cdef object t = TypedContextManager() with t as x: print(typeof(x)) print(typeof(x)) return x def self_lookup(a): b = a b = b.foo(keyword=None) print typeof(b) # Regression test for trac #638. def bar(foo): qux = foo quux = foo[qux.baz] cdef enum MyEnum: enum_x = 1 enum_y = 2 ctypedef long my_long def test_int_typedef_inference(): """ >>> test_int_typedef_inference() """ cdef long x = 1 cdef my_long y = 2 cdef long long z = 3 assert typeof(x + y) == typeof(y + x) == 'my_long', typeof(x + y) assert typeof(y + z) == typeof(z + y) == 'long long', typeof(y + z) from libc.stdint cimport int32_t, int64_t def int64_long_sum(): cdef long x = 1 cdef int32_t x32 = 2 cdef int64_t x64 = 3 cdef unsigned long ux = 4 assert typeof(x + x32) == typeof(x32 + x) == 'long', typeof(x + x32) assert typeof(x + x64) == typeof(x64 + x) == 'int64_t', typeof(x + x64) # The correct answer here is either unsigned long or int64_t, depending on # whether sizeof(long) == 64 or not. Incorrect signedness is probably # preferable to incorrect width. assert typeof(ux + x64) == typeof(x64 + ux) == 'int64_t', typeof(ux + x64) cdef class InferInProperties: """ >>> InferInProperties().x ('double', 'unicode object', 'MyEnum', 'MyEnum') """ cdef MyEnum attr def __cinit__(self): self.attr = enum_x property x: def __get__(self): a = 1.0 b = u'abc' c = self.attr d = enum_y c = d return typeof(a), typeof(b), typeof(c), typeof(d) cdef class WithMethods: cdef int offset def __init__(self, offset): self.offset = offset cpdef int one_arg(self, int x): return x + self.offset cpdef int default_arg(self, int x, int y=0): return x + y + self.offset def test_bound_methods(): """ >>> test_bound_methods() """ o = WithMethods(10) assert typeof(o) == 'WithMethods', typeof(o) one_arg = o.one_arg assert one_arg(2) == 12, one_arg(2) default_arg = o.default_arg assert default_arg(2) == 12, default_arg(2) assert default_arg(2, 3) == 15, default_arg(2, 2) def test_builtin_max(): """ # builtin max is slightly complicated because it gets transformed to EvalWithTempExprNode # See https://github.com/cython/cython/issues/4155 >>> test_builtin_max() """ class C: a = 2 def get_max(self): a = max(self.a, self.a) assert typeof(a) == "Python object", typeof(a) C().get_max()