import sys, os, py import pytest from cffi import FFI, VerificationError, FFIError, CDefError from cffi import recompiler from testing.udir import udir from testing.support import u, long from testing.support import FdWriteCapture, StdErrCapture, _verify try: import importlib except ImportError: importlib = None def check_type_table(input, expected_output, included=None): ffi = FFI() if included: ffi1 = FFI() ffi1.cdef(included) ffi.include(ffi1) ffi.cdef(input) recomp = recompiler.Recompiler(ffi, 'testmod') recomp.collect_type_table() assert ''.join(map(str, recomp.cffi_types)) == expected_output def verify(ffi, module_name, source, *args, **kwds): no_cpp = kwds.pop('no_cpp', False) ignore_warnings = kwds.pop('ignore_warnings', False) kwds.setdefault('undef_macros', ['NDEBUG']) module_name = '_CFFI_' + module_name ffi.set_source(module_name, source) if not os.environ.get('NO_CPP') and not no_cpp: # test the .cpp mode too kwds.setdefault('source_extension', '.cpp') source = 'extern "C" {\n%s\n}' % (source,) elif sys.platform != 'win32' and not ignore_warnings: # add '-Werror' to the existing 'extra_compile_args' flags from testing.support import extra_compile_args kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) + extra_compile_args) if sys.platform == 'darwin': kwds['extra_link_args'] = (kwds.get('extra_link_args', []) + ['-stdlib=libc++']) return _verify(ffi, module_name, source, *args, **kwds) def test_set_source_no_slashes(): ffi = FFI() py.test.raises(ValueError, ffi.set_source, "abc/def", None) py.test.raises(ValueError, ffi.set_source, "abc/def", "C code") def test_type_table_func(): check_type_table("double sin(double);", "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)") check_type_table("float sin(double);", "(FUNCTION 3)(PRIMITIVE 14)(FUNCTION_END 0)(PRIMITIVE 13)") check_type_table("float sin(void);", "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 13)") check_type_table("double sin(float); double cos(float);", "(FUNCTION 3)(PRIMITIVE 13)(FUNCTION_END 0)(PRIMITIVE 14)") check_type_table("double sin(float); double cos(double);", "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)" # cos "(FUNCTION 1)(PRIMITIVE 13)(FUNCTION_END 0)") # sin check_type_table("float sin(double); float cos(float);", "(FUNCTION 4)(PRIMITIVE 14)(FUNCTION_END 0)" # sin "(FUNCTION 4)(PRIMITIVE 13)(FUNCTION_END 0)") # cos def test_type_table_use_noop_for_repeated_args(): check_type_table("double sin(double *, double *);", "(FUNCTION 4)(POINTER 4)(NOOP 1)(FUNCTION_END 0)" "(PRIMITIVE 14)") check_type_table("double sin(double *, double *, double);", "(FUNCTION 3)(POINTER 3)(NOOP 1)(PRIMITIVE 14)" "(FUNCTION_END 0)") def test_type_table_dont_use_noop_for_primitives(): check_type_table("double sin(double, double);", "(FUNCTION 1)(PRIMITIVE 14)(PRIMITIVE 14)(FUNCTION_END 0)") def test_type_table_funcptr_as_argument(): check_type_table("int sin(double(float));", "(FUNCTION 6)(PRIMITIVE 13)(FUNCTION_END 0)" "(FUNCTION 7)(POINTER 0)(FUNCTION_END 0)" "(PRIMITIVE 14)(PRIMITIVE 7)") def test_type_table_variadic_function(): check_type_table("int sin(int, ...);", "(FUNCTION 1)(PRIMITIVE 7)(FUNCTION_END 1)(POINTER 0)") def test_type_table_array(): check_type_table("extern int a[100];", "(PRIMITIVE 7)(ARRAY 0)(None 100)") def test_type_table_typedef(): check_type_table("typedef int foo_t;", "(PRIMITIVE 7)") def test_type_table_prebuilt_type(): check_type_table("int32_t f(void);", "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 21)") def test_type_table_struct_opaque(): check_type_table("struct foo_s;", "(STRUCT_UNION 0)") def test_type_table_struct(): check_type_table("struct foo_s { int a; long b; };", "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)") def test_type_table_union(): check_type_table("union foo_u { int a; long b; };", "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)") def test_type_table_struct_used(): check_type_table("struct foo_s { int a; long b; }; int f(struct foo_s*);", "(FUNCTION 3)(POINTER 5)(FUNCTION_END 0)" "(PRIMITIVE 7)(PRIMITIVE 9)" "(STRUCT_UNION 0)") def test_type_table_anonymous_struct_with_typedef(): check_type_table("typedef struct { int a; long b; } foo_t;", "(STRUCT_UNION 0)(PRIMITIVE 7)(PRIMITIVE 9)") def test_type_table_enum(): check_type_table("enum foo_e { AA, BB, ... };", "(ENUM 0)") def test_type_table_include_1(): check_type_table("foo_t sin(foo_t);", "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)", included="typedef double foo_t;") def test_type_table_include_2(): check_type_table("struct foo_s *sin(struct foo_s *);", "(FUNCTION 1)(POINTER 3)(FUNCTION_END 0)(STRUCT_UNION 0)", included="struct foo_s { int x, y; };") def test_math_sin(): import math ffi = FFI() ffi.cdef("float sin(double); double cos(double);") lib = verify(ffi, 'test_math_sin', '#include ', ignore_warnings=True) assert lib.cos(1.43) == math.cos(1.43) def test_repr_lib(): ffi = FFI() lib = verify(ffi, 'test_repr_lib', '') assert repr(lib) == "" def test_funcarg_ptr(): ffi = FFI() ffi.cdef("int foo(int *);") lib = verify(ffi, 'test_funcarg_ptr', 'int foo(int *p) { return *p; }') assert lib.foo([-12345]) == -12345 def test_funcres_ptr(): ffi = FFI() ffi.cdef("int *foo(void);") lib = verify(ffi, 'test_funcres_ptr', 'int *foo(void) { static int x=-12345; return &x; }') assert lib.foo()[0] == -12345 def test_global_var_array(): ffi = FFI() ffi.cdef("extern int a[100];") lib = verify(ffi, 'test_global_var_array', 'int a[100] = { 9999 };') lib.a[42] = 123456 assert lib.a[42] == 123456 assert lib.a[0] == 9999 def test_verify_typedef(): ffi = FFI() ffi.cdef("typedef int **foo_t;") lib = verify(ffi, 'test_verify_typedef', 'typedef int **foo_t;') assert ffi.sizeof("foo_t") == ffi.sizeof("void *") def test_verify_typedef_dotdotdot(): ffi = FFI() ffi.cdef("typedef ... foo_t;") verify(ffi, 'test_verify_typedef_dotdotdot', 'typedef int **foo_t;') def test_verify_typedef_star_dotdotdot(): ffi = FFI() ffi.cdef("typedef ... *foo_t;") verify(ffi, 'test_verify_typedef_star_dotdotdot', 'typedef int **foo_t;') def test_global_var_int(): ffi = FFI() ffi.cdef("extern int a, b, c;") lib = verify(ffi, 'test_global_var_int', 'int a = 999, b, c;') assert lib.a == 999 lib.a -= 1001 assert lib.a == -2 lib.a = -2147483648 assert lib.a == -2147483648 with pytest.raises(OverflowError): lib.a = 2147483648 with pytest.raises(OverflowError): lib.a = -2147483649 lib.b = 525 # try with the first access being in setattr, too assert lib.b == 525 with pytest.raises(AttributeError): del lib.a with pytest.raises(AttributeError): del lib.c with pytest.raises(AttributeError): del lib.foobarbaz def test_macro(): ffi = FFI() ffi.cdef("#define FOOBAR ...") lib = verify(ffi, 'test_macro', "#define FOOBAR (-6912)") assert lib.FOOBAR == -6912 with pytest.raises(AttributeError): lib.FOOBAR = 2 def test_macro_check_value(): # the value '-0x80000000' in C sources does not have a clear meaning # to me; it appears to have a different effect than '-2147483648'... # Moreover, on 32-bits, -2147483648 is actually equal to # -2147483648U, which in turn is equal to 2147483648U and so positive. vals = ['42', '-42', '0x80000000', '-2147483648', '0', '9223372036854775809ULL', '-9223372036854775807LL'] if sys.maxsize <= 2**32 or sys.platform == 'win32': vals.remove('-2147483648') ffi = FFI() cdef_lines = ['#define FOO_%d_%d %s' % (i, j, vals[i]) for i in range(len(vals)) for j in range(len(vals))] ffi.cdef('\n'.join(cdef_lines)) verify_lines = ['#define FOO_%d_%d %s' % (i, j, vals[j]) # [j], not [i] for i in range(len(vals)) for j in range(len(vals))] lib = verify(ffi, 'test_macro_check_value_ok', '\n'.join(verify_lines)) # for j in range(len(vals)): c_got = int(vals[j].replace('U', '').replace('L', ''), 0) c_compiler_msg = str(c_got) if c_got > 0: c_compiler_msg += ' (0x%x)' % (c_got,) # for i in range(len(vals)): attrname = 'FOO_%d_%d' % (i, j) if i == j: x = getattr(lib, attrname) assert x == c_got else: e = py.test.raises(ffi.error, getattr, lib, attrname) assert str(e.value) == ( "the C compiler says '%s' is equal to " "%s, but the cdef disagrees" % (attrname, c_compiler_msg)) def test_constant(): ffi = FFI() ffi.cdef("static const int FOOBAR;") lib = verify(ffi, 'test_constant', "#define FOOBAR (-6912)") assert lib.FOOBAR == -6912 with pytest.raises(AttributeError): lib.FOOBAR = 2 def test_check_value_of_static_const(): ffi = FFI() ffi.cdef("static const int FOOBAR = 042;") lib = verify(ffi, 'test_check_value_of_static_const', "#define FOOBAR (-6912)") e = py.test.raises(ffi.error, getattr, lib, 'FOOBAR') assert str(e.value) == ( "the C compiler says 'FOOBAR' is equal to -6912, but the cdef disagrees") def test_constant_nonint(): ffi = FFI() ffi.cdef("static const double FOOBAR;") lib = verify(ffi, 'test_constant_nonint', "#define FOOBAR (-6912.5)") assert lib.FOOBAR == -6912.5 with pytest.raises(AttributeError): lib.FOOBAR = 2 def test_constant_ptr(): ffi = FFI() ffi.cdef("static double *const FOOBAR;") lib = verify(ffi, 'test_constant_ptr', "#define FOOBAR NULL") assert lib.FOOBAR == ffi.NULL assert ffi.typeof(lib.FOOBAR) == ffi.typeof("double *") def test_dir(): ffi = FFI() ffi.cdef("int ff(int); extern int aa; static const int my_constant;") lib = verify(ffi, 'test_dir', """ #define my_constant (-45) int aa; int ff(int x) { return x+aa; } """) lib.aa = 5 assert dir(lib) == ['aa', 'ff', 'my_constant'] # aaobj = lib.__dict__['aa'] assert not isinstance(aaobj, int) # some internal object instead assert lib.__dict__ == { 'ff': lib.ff, 'aa': aaobj, 'my_constant': -45} lib.__dict__['ff'] = "??" assert lib.ff(10) == 15 def test_verify_opaque_struct(): ffi = FFI() ffi.cdef("struct foo_s;") lib = verify(ffi, 'test_verify_opaque_struct', "struct foo_s;") assert ffi.typeof("struct foo_s").cname == "struct foo_s" def test_verify_opaque_union(): ffi = FFI() ffi.cdef("union foo_s;") lib = verify(ffi, 'test_verify_opaque_union', "union foo_s;") assert ffi.typeof("union foo_s").cname == "union foo_s" def test_verify_struct(): ffi = FFI() ffi.cdef("""struct foo_s { int b; short a; ...; }; struct bar_s { struct foo_s *f; };""") lib = verify(ffi, 'test_verify_struct', """struct foo_s { short a; int b; }; struct bar_s { struct foo_s *f; };""") ffi.typeof("struct bar_s *") p = ffi.new("struct foo_s *", {'a': -32768, 'b': -2147483648}) assert p.a == -32768 assert p.b == -2147483648 with pytest.raises(OverflowError): p.a -= 1 with pytest.raises(OverflowError): p.b -= 1 q = ffi.new("struct bar_s *", {'f': p}) assert q.f == p # assert ffi.offsetof("struct foo_s", "a") == 0 assert ffi.offsetof("struct foo_s", "b") == 4 assert ffi.offsetof(u+"struct foo_s", u+"b") == 4 # py.test.raises(TypeError, ffi.addressof, p) assert ffi.addressof(p[0]) == p assert ffi.typeof(ffi.addressof(p[0])) is ffi.typeof("struct foo_s *") assert ffi.typeof(ffi.addressof(p, "b")) is ffi.typeof("int *") assert ffi.addressof(p, "b")[0] == p.b def test_verify_exact_field_offset(): ffi = FFI() ffi.cdef("""struct foo_s { int b; short a; };""") lib = verify(ffi, 'test_verify_exact_field_offset', """struct foo_s { short a; int b; };""") e = py.test.raises(ffi.error, ffi.new, "struct foo_s *", []) # lazily assert str(e.value).startswith( "struct foo_s: wrong offset for field 'b' (cdef " 'says 0, but C compiler says 4). fix it or use "...;" ') def test_type_caching(): ffi1 = FFI(); ffi1.cdef("struct foo_s;") ffi2 = FFI(); ffi2.cdef("struct foo_s;") # different one! lib1 = verify(ffi1, 'test_type_caching_1', 'struct foo_s;') lib2 = verify(ffi2, 'test_type_caching_2', 'struct foo_s;') # shared types assert ffi1.typeof("long") is ffi2.typeof("long") assert ffi1.typeof("long**") is ffi2.typeof("long * *") assert ffi1.typeof("long(*)(int, ...)") is ffi2.typeof("long(*)(int, ...)") # non-shared types assert ffi1.typeof("struct foo_s") is not ffi2.typeof("struct foo_s") assert ffi1.typeof("struct foo_s *") is not ffi2.typeof("struct foo_s *") assert ffi1.typeof("struct foo_s*(*)()") is not ( ffi2.typeof("struct foo_s*(*)()")) assert ffi1.typeof("void(*)(struct foo_s*)") is not ( ffi2.typeof("void(*)(struct foo_s*)")) def test_verify_enum(): ffi = FFI() ffi.cdef("""enum e1 { B1, A1, ... }; enum e2 { B2, A2, ... };""") lib = verify(ffi, 'test_verify_enum', "enum e1 { A1, B1, C1=%d };" % sys.maxsize + "enum e2 { A2, B2, C2 };") ffi.typeof("enum e1") ffi.typeof("enum e2") assert lib.A1 == 0 assert lib.B1 == 1 assert lib.A2 == 0 assert lib.B2 == 1 assert ffi.sizeof("enum e1") == ffi.sizeof("long") assert ffi.sizeof("enum e2") == ffi.sizeof("int") assert repr(ffi.cast("enum e1", 0)) == "" def test_duplicate_enum(): ffi = FFI() ffi.cdef("enum e1 { A1, ... }; enum e2 { A1, ... };") py.test.raises(VerificationError, verify, ffi, 'test_duplicate_enum', "enum e1 { A1 }; enum e2 { B1 };") def test_dotdotdot_length_of_array_field(): ffi = FFI() ffi.cdef("struct foo_s { int a[...]; int b[...]; };") verify(ffi, 'test_dotdotdot_length_of_array_field', "struct foo_s { int a[42]; int b[11]; };") assert ffi.sizeof("struct foo_s") == (42 + 11) * 4 p = ffi.new("struct foo_s *") assert p.a[41] == p.b[10] == 0 with pytest.raises(IndexError): p.a[42] with pytest.raises(IndexError): p.b[11] def test_dotdotdot_global_array(): ffi = FFI() ffi.cdef("extern int aa[...]; extern int bb[...];") lib = verify(ffi, 'test_dotdotdot_global_array', "int aa[41]; int bb[12];") assert ffi.sizeof(lib.aa) == 41 * 4 assert ffi.sizeof(lib.bb) == 12 * 4 assert lib.aa[40] == lib.bb[11] == 0 with pytest.raises(IndexError): lib.aa[41] with pytest.raises(IndexError): lib.bb[12] def test_misdeclared_field_1(): ffi = FFI() ffi.cdef("struct foo_s { int a[5]; };") try: verify(ffi, 'test_misdeclared_field_1', "struct foo_s { int a[6]; };") except VerificationError: pass # ok, fail during compilation already (e.g. C++) else: assert ffi.sizeof("struct foo_s") == 24 # found by the actual C code try: # lazily build the fields and boom: p = ffi.new("struct foo_s *") p.a assert False, "should have raised" except ffi.error as e: assert str(e).startswith("struct foo_s: wrong size for field 'a' " "(cdef says 20, but C compiler says 24)") def test_open_array_in_struct(): ffi = FFI() ffi.cdef("struct foo_s { int b; int a[]; };") verify(ffi, 'test_open_array_in_struct', "struct foo_s { int b; int a[]; };") assert ffi.sizeof("struct foo_s") == 4 p = ffi.new("struct foo_s *", [5, [10, 20, 30, 40]]) assert p.a[2] == 30 assert ffi.sizeof(p) == ffi.sizeof("void *") assert ffi.sizeof(p[0]) == 5 * ffi.sizeof("int") def test_math_sin_type(): ffi = FFI() ffi.cdef("double sin(double); void *xxtestfunc();") lib = verify(ffi, 'test_math_sin_type', """ #include void *xxtestfunc(void) { return 0; } """) # 'lib.sin' is typed as a object on lib assert ffi.typeof(lib.sin).cname == "double(*)(double)" # 'x' is another object on lib, made very indirectly x = type(lib).__dir__.__get__(lib) py.test.raises(TypeError, ffi.typeof, x) # # present on built-in functions on CPython; must be emulated on PyPy: assert lib.sin.__name__ == 'sin' assert lib.sin.__module__ == '_CFFI_test_math_sin_type' assert lib.sin.__doc__ == ( "double sin(double);\n" "\n" "CFFI C function from _CFFI_test_math_sin_type.lib") assert ffi.typeof(lib.xxtestfunc).cname == "void *(*)()" assert lib.xxtestfunc.__doc__ == ( "void *xxtestfunc();\n" "\n" "CFFI C function from _CFFI_test_math_sin_type.lib") def test_verify_anonymous_struct_with_typedef(): ffi = FFI() ffi.cdef("typedef struct { int a; long b; ...; } foo_t;") verify(ffi, 'test_verify_anonymous_struct_with_typedef', "typedef struct { long b; int hidden, a; } foo_t;") p = ffi.new("foo_t *", {'b': 42}) assert p.b == 42 assert repr(p).startswith("" # ffi = FFI() ffi.cdef("typedef enum { AA=%d } e1;" % sys.maxsize) lib = verify(ffi, 'test_verify_anonymous_enum_with_typedef2', "typedef enum { AA=%d } e1;" % sys.maxsize) assert lib.AA == int(ffi.cast("long", sys.maxsize)) assert ffi.sizeof("e1") == ffi.sizeof("long") def test_unique_types(): CDEF = "struct foo_s; union foo_u; enum foo_e { AA };" ffi1 = FFI(); ffi1.cdef(CDEF); verify(ffi1, "test_unique_types_1", CDEF) ffi2 = FFI(); ffi2.cdef(CDEF); verify(ffi2, "test_unique_types_2", CDEF) # assert ffi1.typeof("char") is ffi2.typeof("char ") assert ffi1.typeof("long") is ffi2.typeof("signed long int") assert ffi1.typeof("double *") is ffi2.typeof("double*") assert ffi1.typeof("int ***") is ffi2.typeof(" int * * *") assert ffi1.typeof("int[]") is ffi2.typeof("signed int[]") assert ffi1.typeof("signed int*[17]") is ffi2.typeof("int *[17]") assert ffi1.typeof("void") is ffi2.typeof("void") assert ffi1.typeof("int(*)(int,int)") is ffi2.typeof("int(*)(int,int)") # # these depend on user-defined data, so should not be shared for name in ["struct foo_s", "union foo_u *", "enum foo_e", "struct foo_s *(*)()", "void(*)(struct foo_s *)", "struct foo_s *(*[5])[8]", ]: assert ffi1.typeof(name) is not ffi2.typeof(name) # sanity check: twice 'ffi1' assert ffi1.typeof("struct foo_s*") is ffi1.typeof("struct foo_s *") def test_module_name_in_package(): ffi = FFI() ffi.cdef("int foo(int);") recompiler.recompile(ffi, "test_module_name_in_package.mymod", "int foo(int x) { return x + 32; }", tmpdir=str(udir)) old_sys_path = sys.path[:] try: package_dir = udir.join('test_module_name_in_package') for name in os.listdir(str(udir)): assert not name.startswith('test_module_name_in_package.') assert os.path.isdir(str(package_dir)) assert len(os.listdir(str(package_dir))) > 0 assert os.path.exists(str(package_dir.join('mymod.c'))) package_dir.join('__init__.py').write('') # getattr(importlib, 'invalidate_caches', object)() # sys.path.insert(0, str(udir)) import test_module_name_in_package.mymod assert test_module_name_in_package.mymod.lib.foo(10) == 42 assert test_module_name_in_package.mymod.__name__ == ( 'test_module_name_in_package.mymod') finally: sys.path[:] = old_sys_path def test_bad_size_of_global_1(): ffi = FFI() ffi.cdef("extern short glob;") py.test.raises(VerificationError, verify, ffi, "test_bad_size_of_global_1", "long glob;") def test_bad_size_of_global_2(): ffi = FFI() ffi.cdef("extern int glob[10];") py.test.raises(VerificationError, verify, ffi, "test_bad_size_of_global_2", "int glob[9];") def test_unspecified_size_of_global_1(): ffi = FFI() ffi.cdef("extern int glob[];") lib = verify(ffi, "test_unspecified_size_of_global_1", "int glob[10];") assert ffi.typeof(lib.glob) == ffi.typeof("int *") def test_unspecified_size_of_global_2(): ffi = FFI() ffi.cdef("extern int glob[][5];") lib = verify(ffi, "test_unspecified_size_of_global_2", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") def test_unspecified_size_of_global_3(): ffi = FFI() ffi.cdef("extern int glob[][...];") lib = verify(ffi, "test_unspecified_size_of_global_3", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") def test_unspecified_size_of_global_4(): ffi = FFI() ffi.cdef("extern int glob[...][...];") lib = verify(ffi, "test_unspecified_size_of_global_4", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int[10][5]") def test_include_1(): ffi1 = FFI() ffi1.cdef("typedef double foo_t;") verify(ffi1, "test_include_1_parent", "typedef double foo_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("foo_t ff1(foo_t);") lib = verify(ffi, "test_include_1", "double ff1(double x) { return 42.5; }") assert lib.ff1(0) == 42.5 assert ffi1.typeof("foo_t") is ffi.typeof("foo_t") is ffi.typeof("double") def test_include_1b(): ffi1 = FFI() ffi1.cdef("int foo1(int);") lib1 = verify(ffi1, "test_include_1b_parent", "int foo1(int x) { return x + 10; }") ffi = FFI() ffi.include(ffi1) ffi.cdef("int foo2(int);") lib = verify(ffi, "test_include_1b", "int foo2(int x) { return x - 5; }") assert lib.foo2(42) == 37 assert lib.foo1(42) == 52 assert lib.foo1 is lib1.foo1 def test_include_2(): ffi1 = FFI() ffi1.cdef("struct foo_s { int x, y; };") verify(ffi1, "test_include_2_parent", "struct foo_s { int x, y; };") ffi = FFI() ffi.include(ffi1) ffi.cdef("struct foo_s *ff2(struct foo_s *);") lib = verify(ffi, "test_include_2", "struct foo_s { int x, y; }; //usually from a #include\n" "struct foo_s *ff2(struct foo_s *p) { p->y++; return p; }") p = ffi.new("struct foo_s *") p.y = 41 q = lib.ff2(p) assert q == p assert p.y == 42 assert ffi1.typeof("struct foo_s") is ffi.typeof("struct foo_s") def test_include_3(): ffi1 = FFI() ffi1.cdef("typedef short sshort_t;") verify(ffi1, "test_include_3_parent", "typedef short sshort_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("sshort_t ff3(sshort_t);") lib = verify(ffi, "test_include_3", "typedef short sshort_t; //usually from a #include\n" "sshort_t ff3(sshort_t x) { return (sshort_t)(x + 42); }") assert lib.ff3(10) == 52 assert ffi.typeof(ffi.cast("sshort_t", 42)) is ffi.typeof("short") assert ffi1.typeof("sshort_t") is ffi.typeof("sshort_t") def test_include_4(): ffi1 = FFI() ffi1.cdef("typedef struct { int x; } mystruct_t;") verify(ffi1, "test_include_4_parent", "typedef struct { int x; } mystruct_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_t *ff4(mystruct_t *);") lib = verify(ffi, "test_include_4", "typedef struct {int x; } mystruct_t; //usually from a #include\n" "mystruct_t *ff4(mystruct_t *p) { p->x += 42; return p; }") p = ffi.new("mystruct_t *", [10]) q = lib.ff4(p) assert q == p assert p.x == 52 assert ffi1.typeof("mystruct_t") is ffi.typeof("mystruct_t") def test_include_5(): ffi1 = FFI() ffi1.cdef("typedef struct { int x[2]; int y; } *mystruct_p;") verify(ffi1, "test_include_5_parent", "typedef struct { int x[2]; int y; } *mystruct_p;") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_p ff5(mystruct_p);") lib = verify(ffi, "test_include_5", "typedef struct {int x[2]; int y; } *mystruct_p; //usually #include\n" "mystruct_p ff5(mystruct_p p) { p->x[1] += 42; return p; }") assert ffi.alignof(ffi.typeof("mystruct_p").item) == 4 assert ffi1.typeof("mystruct_p") is ffi.typeof("mystruct_p") p = ffi.new("mystruct_p", [[5, 10], -17]) q = lib.ff5(p) assert q == p assert p.x[0] == 5 assert p.x[1] == 52 assert p.y == -17 assert ffi.alignof(ffi.typeof(p[0])) == 4 def test_include_6(): ffi1 = FFI() ffi1.cdef("typedef ... mystruct_t;") verify(ffi1, "test_include_6_parent", "typedef struct _mystruct_s mystruct_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_t *ff6(void); int ff6b(mystruct_t *);") lib = verify(ffi, "test_include_6", "typedef struct _mystruct_s mystruct_t; //usually from a #include\n" "struct _mystruct_s { int x; };\n" "static mystruct_t result_struct = { 42 };\n" "mystruct_t *ff6(void) { return &result_struct; }\n" "int ff6b(mystruct_t *p) { return p->x; }") p = lib.ff6() assert ffi.cast("int *", p)[0] == 42 assert lib.ff6b(p) == 42 def test_include_7(): ffi1 = FFI() ffi1.cdef("typedef ... mystruct_t;\n" "int ff7b(mystruct_t *);") verify(ffi1, "test_include_7_parent", "typedef struct { int x; } mystruct_t;\n" "int ff7b(mystruct_t *p) { return p->x; }") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_t *ff7(void);") lib = verify(ffi, "test_include_7", "typedef struct { int x; } mystruct_t; //usually from a #include\n" "static mystruct_t result_struct = { 42 };" "mystruct_t *ff7(void) { return &result_struct; }") p = lib.ff7() assert ffi.cast("int *", p)[0] == 42 assert lib.ff7b(p) == 42 def test_include_8(): ffi1 = FFI() ffi1.cdef("struct foo_s;") verify(ffi1, "test_include_8_parent", "struct foo_s;") ffi = FFI() ffi.include(ffi1) ffi.cdef("struct foo_s { int x, y; };") verify(ffi, "test_include_8", "struct foo_s { int x, y; };") e = py.test.raises(NotImplementedError, ffi.new, "struct foo_s *") assert str(e.value) == ( "'struct foo_s' is opaque in the ffi.include(), but no longer in " "the ffi doing the include (workaround: don't use ffi.include() but" " duplicate the declarations of everything using struct foo_s)") def test_unicode_libraries(): try: unicode except NameError: py.test.skip("for python 2.x") # import math lib_m = "m" if sys.platform == 'win32': #there is a small chance this fails on Mingw via environ $CC import distutils.ccompiler if distutils.ccompiler.get_default_compiler() == 'msvc': lib_m = 'msvcrt' ffi = FFI() ffi.cdef(unicode("float sin(double); double cos(double);")) lib = verify(ffi, 'test_math_sin_unicode', unicode('#include '), libraries=[unicode(lib_m)], ignore_warnings=True) assert lib.cos(1.43) == math.cos(1.43) def test_incomplete_struct_as_arg(): ffi = FFI() ffi.cdef("struct foo_s { int x; ...; }; int f(int, struct foo_s);") lib = verify(ffi, "test_incomplete_struct_as_arg", "struct foo_s { int a, x, z; };\n" "int f(int b, struct foo_s s) { return s.x * b; }") s = ffi.new("struct foo_s *", [21]) assert s.x == 21 assert ffi.sizeof(s[0]) == 12 assert ffi.offsetof(ffi.typeof(s), 'x') == 4 assert lib.f(2, s[0]) == 42 assert ffi.typeof(lib.f) == ffi.typeof("int(*)(int, struct foo_s)") def test_incomplete_struct_as_result(): ffi = FFI() ffi.cdef("struct foo_s { int x; ...; }; struct foo_s f(int);") lib = verify(ffi, "test_incomplete_struct_as_result", "struct foo_s { int a, x, z; };\n" "struct foo_s f(int x) { struct foo_s r; r.x = x * 2; return r; }") s = lib.f(21) assert s.x == 42 assert ffi.typeof(lib.f) == ffi.typeof("struct foo_s(*)(int)") def test_incomplete_struct_as_both(): ffi = FFI() ffi.cdef("struct foo_s { int x; ...; }; struct bar_s { int y; ...; };\n" "struct foo_s f(int, struct bar_s);") lib = verify(ffi, "test_incomplete_struct_as_both", "struct foo_s { int a, x, z; };\n" "struct bar_s { int b, c, y, d; };\n" "struct foo_s f(int x, struct bar_s b) {\n" " struct foo_s r; r.x = x * b.y; return r;\n" "}") b = ffi.new("struct bar_s *", [7]) s = lib.f(6, b[0]) assert s.x == 42 assert ffi.typeof(lib.f) == ffi.typeof( "struct foo_s(*)(int, struct bar_s)") s = lib.f(14, {'y': -3}) assert s.x == -42 def test_name_of_unnamed_struct(): ffi = FFI() ffi.cdef("typedef struct { int x; } foo_t;\n" "typedef struct { int y; } *bar_p;\n" "typedef struct { int y; } **baz_pp;\n") verify(ffi, "test_name_of_unnamed_struct", "typedef struct { int x; } foo_t;\n" "typedef struct { int y; } *bar_p;\n" "typedef struct { int y; } **baz_pp;\n") assert repr(ffi.typeof("foo_t")) == "" assert repr(ffi.typeof("bar_p")) == "" assert repr(ffi.typeof("baz_pp")) == "" def test_address_of_global_var(): ffi = FFI() ffi.cdef(""" extern long bottom, bottoms[2]; long FetchRectBottom(void); long FetchRectBottoms1(void); #define FOOBAR 42 """) lib = verify(ffi, "test_address_of_global_var", """ long bottom, bottoms[2]; long FetchRectBottom(void) { return bottom; } long FetchRectBottoms1(void) { return bottoms[1]; } #define FOOBAR 42 """) lib.bottom = 300 assert lib.FetchRectBottom() == 300 lib.bottom += 1 assert lib.FetchRectBottom() == 301 lib.bottoms[1] = 500 assert lib.FetchRectBottoms1() == 500 lib.bottoms[1] += 2 assert lib.FetchRectBottoms1() == 502 # p = ffi.addressof(lib, 'bottom') assert ffi.typeof(p) == ffi.typeof("long *") assert p[0] == 301 p[0] += 1 assert lib.FetchRectBottom() == 302 p = ffi.addressof(lib, 'bottoms') assert ffi.typeof(p) == ffi.typeof("long(*)[2]") assert p[0] == lib.bottoms # py.test.raises(AttributeError, ffi.addressof, lib, 'unknown_var') py.test.raises(AttributeError, ffi.addressof, lib, "FOOBAR") def test_defines__CFFI_(): # Check that we define the macro _CFFI_ automatically. # It should be done before including Python.h, so that PyPy's Python.h # can check for it. ffi = FFI() ffi.cdef(""" #define CORRECT 1 """) lib = verify(ffi, "test_defines__CFFI_", """ #ifdef _CFFI_ # define CORRECT 1 #endif """) assert lib.CORRECT == 1 def test_unpack_args(): ffi = FFI() ffi.cdef("void foo0(void); void foo1(int); void foo2(int, int);") lib = verify(ffi, "test_unpack_args", """ void foo0(void) { } void foo1(int x) { } void foo2(int x, int y) { } """) assert 'foo0' in repr(lib.foo0) assert 'foo1' in repr(lib.foo1) assert 'foo2' in repr(lib.foo2) lib.foo0() lib.foo1(42) lib.foo2(43, 44) e1 = py.test.raises(TypeError, lib.foo0, 42) e2 = py.test.raises(TypeError, lib.foo0, 43, 44) e3 = py.test.raises(TypeError, lib.foo1) e4 = py.test.raises(TypeError, lib.foo1, 43, 44) e5 = py.test.raises(TypeError, lib.foo2) e6 = py.test.raises(TypeError, lib.foo2, 42) e7 = py.test.raises(TypeError, lib.foo2, 45, 46, 47) def st1(s): s = str(s) if s.startswith("_CFFI_test_unpack_args.Lib."): s = s[len("_CFFI_test_unpack_args.Lib."):] return s assert st1(e1.value) == "foo0() takes no arguments (1 given)" assert st1(e2.value) == "foo0() takes no arguments (2 given)" assert st1(e3.value) == "foo1() takes exactly one argument (0 given)" assert st1(e4.value) == "foo1() takes exactly one argument (2 given)" assert st1(e5.value) in ["foo2 expected 2 arguments, got 0", "foo2() takes exactly 2 arguments (0 given)"] assert st1(e6.value) in ["foo2 expected 2 arguments, got 1", "foo2() takes exactly 2 arguments (1 given)"] assert st1(e7.value) in ["foo2 expected 2 arguments, got 3", "foo2() takes exactly 2 arguments (3 given)"] def test_address_of_function(): ffi = FFI() ffi.cdef("long myfunc(long x);") lib = verify(ffi, "test_addressof_function", """ char myfunc(char x) { return (char)(x + 42); } """, ignore_warnings=True) assert lib.myfunc(5) == 47 assert lib.myfunc(0xABC05) == 47 assert not isinstance(lib.myfunc, ffi.CData) assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(long)") addr = ffi.addressof(lib, 'myfunc') assert addr(5) == 47 assert addr(0xABC05) == 47 assert isinstance(addr, ffi.CData) assert ffi.typeof(addr) == ffi.typeof("long(*)(long)") def test_address_of_function_with_struct(): ffi = FFI() ffi.cdef("struct foo_s { int x; }; long myfunc(struct foo_s);") lib = verify(ffi, "test_addressof_function_with_struct", """ struct foo_s { int x; }; char myfunc(struct foo_s input) { return (char)(input.x + 42); } """) s = ffi.new("struct foo_s *", [5])[0] assert lib.myfunc(s) == 47 assert not isinstance(lib.myfunc, ffi.CData) assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(struct foo_s)") addr = ffi.addressof(lib, 'myfunc') assert addr(s) == 47 assert isinstance(addr, ffi.CData) assert ffi.typeof(addr) == ffi.typeof("long(*)(struct foo_s)") def test_issue198(): ffi = FFI() ffi.cdef(""" typedef struct{...;} opaque_t; const opaque_t CONSTANT; int toint(opaque_t); """) lib = verify(ffi, 'test_issue198', """ typedef int opaque_t; #define CONSTANT ((opaque_t)42) static int toint(opaque_t o) { return o; } """) def random_stuff(): pass assert lib.toint(lib.CONSTANT) == 42 random_stuff() assert lib.toint(lib.CONSTANT) == 42 def test_constant_is_not_a_compiler_constant(): ffi = FFI() ffi.cdef("static const float almost_forty_two;") lib = verify(ffi, 'test_constant_is_not_a_compiler_constant', """ static float f(void) { return 42.25; } #define almost_forty_two (f()) """) assert lib.almost_forty_two == 42.25 def test_constant_of_unknown_size(): ffi = FFI() ffi.cdef(""" typedef ... opaque_t; const opaque_t CONSTANT; """) lib = verify(ffi, 'test_constant_of_unknown_size', "typedef int opaque_t;" "const int CONSTANT = 42;") e = py.test.raises(ffi.error, getattr, lib, 'CONSTANT') assert str(e.value) == ("constant 'CONSTANT' is of " "type 'opaque_t', whose size is not known") def test_variable_of_unknown_size(): ffi = FFI() ffi.cdef(""" typedef ... opaque_t; extern opaque_t globvar; """) lib = verify(ffi, 'test_variable_of_unknown_size', """ typedef char opaque_t[6]; opaque_t globvar = "hello"; """) # can't read or write it at all e = py.test.raises(TypeError, getattr, lib, 'globvar') assert str(e.value) in ["cdata 'opaque_t' is opaque", "'opaque_t' is opaque or not completed yet"] #pypy e = py.test.raises(TypeError, setattr, lib, 'globvar', []) assert str(e.value) in ["'opaque_t' is opaque", "'opaque_t' is opaque or not completed yet"] #pypy # but we can get its address p = ffi.addressof(lib, 'globvar') assert ffi.typeof(p) == ffi.typeof('opaque_t *') assert ffi.string(ffi.cast("char *", p), 8) == b"hello" def test_constant_of_value_unknown_to_the_compiler(): extra_c_source = udir.join( 'extra_test_constant_of_value_unknown_to_the_compiler.c') extra_c_source.write('const int external_foo = 42;\n') ffi = FFI() ffi.cdef("const int external_foo;") lib = verify(ffi, 'test_constant_of_value_unknown_to_the_compiler', """ extern const int external_foo; """, sources=[str(extra_c_source)]) assert lib.external_foo == 42 def test_dotdot_in_source_file_names(): extra_c_source = udir.join( 'extra_test_dotdot_in_source_file_names.c') extra_c_source.write('const int external_foo = 42;\n') ffi = FFI() ffi.cdef("const int external_foo;") lib = verify(ffi, 'test_dotdot_in_source_file_names', """ extern const int external_foo; """, sources=[os.path.join(os.path.dirname(str(extra_c_source)), 'foobar', '..', os.path.basename(str(extra_c_source)))]) assert lib.external_foo == 42 def test_call_with_incomplete_structs(): ffi = FFI() ffi.cdef("typedef struct {...;} foo_t; " "extern foo_t myglob; " "foo_t increment(foo_t s); " "double getx(foo_t s);") lib = verify(ffi, 'test_call_with_incomplete_structs', """ typedef double foo_t; double myglob = 42.5; double getx(double x) { return x; } double increment(double x) { return x + 1; } """) assert lib.getx(lib.myglob) == 42.5 assert lib.getx(lib.increment(lib.myglob)) == 43.5 def test_struct_array_guess_length_2(): ffi = FFI() ffi.cdef("struct foo_s { int a[...][...]; };") lib = verify(ffi, 'test_struct_array_guess_length_2', "struct foo_s { int x; int a[5][8]; int y; };") assert ffi.sizeof('struct foo_s') == 42 * ffi.sizeof('int') s = ffi.new("struct foo_s *") assert ffi.typeof(s.a) == ffi.typeof("int[5][8]") assert ffi.sizeof(s.a) == 40 * ffi.sizeof('int') assert s.a[4][7] == 0 with pytest.raises(IndexError): s.a[4][8] with pytest.raises(IndexError): s.a[5][0] assert ffi.typeof(s.a) == ffi.typeof("int[5][8]") assert ffi.typeof(s.a[0]) == ffi.typeof("int[8]") def test_struct_array_guess_length_3(): ffi = FFI() ffi.cdef("struct foo_s { int a[][...]; };") lib = verify(ffi, 'test_struct_array_guess_length_3', "struct foo_s { int x; int a[5][7]; int y; };") assert ffi.sizeof('struct foo_s') == 37 * ffi.sizeof('int') s = ffi.new("struct foo_s *") assert ffi.typeof(s.a) == ffi.typeof("int[][7]") assert s.a[4][6] == 0 with pytest.raises(IndexError): s.a[4][7] assert ffi.typeof(s.a[0]) == ffi.typeof("int[7]") def test_global_var_array_2(): ffi = FFI() ffi.cdef("extern int a[...][...];") lib = verify(ffi, 'test_global_var_array_2', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 with pytest.raises(IndexError): lib.a[0][8] with pytest.raises(IndexError): lib.a[10][0] assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]") assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") def test_global_var_array_3(): ffi = FFI() ffi.cdef("extern int a[][...];") lib = verify(ffi, 'test_global_var_array_3', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 with pytest.raises(IndexError): lib.a[0][8] assert ffi.typeof(lib.a) == ffi.typeof("int(*)[8]") assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") def test_global_var_array_4(): ffi = FFI() ffi.cdef("extern int a[10][...];") lib = verify(ffi, 'test_global_var_array_4', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 with pytest.raises(IndexError): lib.a[0][8] with pytest.raises(IndexError): lib.a[10][8] assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]") assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") def test_some_integer_type(): ffi = FFI() ffi.cdef(""" typedef int... foo_t; typedef unsigned long... bar_t; typedef struct { foo_t a, b; } mystruct_t; foo_t foobar(bar_t, mystruct_t); static const bar_t mu = -20; static const foo_t nu = 20; """) lib = verify(ffi, 'test_some_integer_type', """ typedef unsigned long long foo_t; typedef short bar_t; typedef struct { foo_t a, b; } mystruct_t; static foo_t foobar(bar_t x, mystruct_t s) { return (foo_t)x + s.a + s.b; } static const bar_t mu = -20; static const foo_t nu = 20; """) assert ffi.sizeof("foo_t") == ffi.sizeof("unsigned long long") assert ffi.sizeof("bar_t") == ffi.sizeof("short") maxulonglong = 2 ** 64 - 1 assert int(ffi.cast("foo_t", -1)) == maxulonglong assert int(ffi.cast("bar_t", -1)) == -1 assert lib.foobar(-1, [0, 0]) == maxulonglong assert lib.foobar(2 ** 15 - 1, [0, 0]) == 2 ** 15 - 1 assert lib.foobar(10, [20, 31]) == 61 assert lib.foobar(0, [0, maxulonglong]) == maxulonglong py.test.raises(OverflowError, lib.foobar, 2 ** 15, [0, 0]) py.test.raises(OverflowError, lib.foobar, -(2 ** 15) - 1, [0, 0]) py.test.raises(OverflowError, ffi.new, "mystruct_t *", [0, -1]) assert lib.mu == -20 assert lib.nu == 20 def test_some_float_type(): ffi = FFI() ffi.cdef(""" typedef double... foo_t; typedef float... bar_t; foo_t sum(foo_t[]); bar_t neg(bar_t); """) lib = verify(ffi, 'test_some_float_type', """ typedef float foo_t; static foo_t sum(foo_t x[]) { return x[0] + x[1]; } typedef double bar_t; static double neg(double x) { return -x; } """) assert lib.sum([40.0, 2.25]) == 42.25 assert lib.sum([12.3, 45.6]) != 12.3 + 45.6 # precision loss assert lib.neg(12.3) == -12.3 # no precision loss assert ffi.sizeof("foo_t") == ffi.sizeof("float") assert ffi.sizeof("bar_t") == ffi.sizeof("double") def test_some_float_invalid_1(): ffi = FFI() py.test.raises((FFIError, # with pycparser <= 2.17 CDefError), # with pycparser >= 2.18 ffi.cdef, "typedef long double... foo_t;") def test_some_float_invalid_2(): ffi = FFI() ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);") lib = verify(ffi, 'test_some_float_invalid_2', """ typedef unsigned long foo_t; foo_t neg(foo_t x) { return -x; } """) e = py.test.raises(ffi.error, getattr, lib, 'neg') assert str(e.value) == ("primitive floating-point type with an unexpected " "size (or not a float type at all)") def test_some_float_invalid_3(): ffi = FFI() ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);") lib = verify(ffi, 'test_some_float_invalid_3', """ typedef long double foo_t; foo_t neg(foo_t x) { return -x; } """, ignore_warnings=True) if ffi.sizeof("long double") == ffi.sizeof("double"): assert lib.neg(12.3) == -12.3 else: e = py.test.raises(ffi.error, getattr, lib, 'neg') assert str(e.value) == ("primitive floating-point type is " "'long double', not supported for now with " "the syntax 'typedef double... xxx;'") def test_issue200(): ffi = FFI() ffi.cdef(""" typedef void (function_t)(void*); void function(void *); """) lib = verify(ffi, 'test_issue200', """ static void function(void *p) { (void)p; } """) ffi.typeof('function_t*') lib.function(ffi.NULL) # assert did not crash def test_alignment_of_longlong(): ffi = FFI() x1 = ffi.alignof('unsigned long long') assert x1 in [4, 8] ffi.cdef("struct foo_s { unsigned long long x; };") lib = verify(ffi, 'test_alignment_of_longlong', "struct foo_s { unsigned long long x; };") assert ffi.alignof('unsigned long long') == x1 assert ffi.alignof('struct foo_s') == x1 def test_import_from_lib(): ffi = FFI() ffi.cdef("int mybar(int); static int myvar;\n#define MYFOO ...") lib = verify(ffi, 'test_import_from_lib', "#define MYFOO 42\n" "static int mybar(int x) { return x + 1; }\n" "static int myvar = -5;") assert sys.modules['_CFFI_test_import_from_lib'].lib is lib assert sys.modules['_CFFI_test_import_from_lib.lib'] is lib from _CFFI_test_import_from_lib.lib import MYFOO assert MYFOO == 42 assert hasattr(lib, '__dict__') assert lib.__all__ == ['MYFOO', 'mybar'] # but not 'myvar' assert lib.__name__ == '_CFFI_test_import_from_lib.lib' assert lib.__class__ is type(sys) # !! hack for help() def test_macro_var_callback(): ffi = FFI() ffi.cdef("extern int my_value; extern int *(*get_my_value)(void);") lib = verify(ffi, 'test_macro_var_callback', "int *(*get_my_value)(void);\n" "#define my_value (*get_my_value())") # values = ffi.new("int[50]") def it(): for i in range(50): yield i it = it() # @ffi.callback("int *(*)(void)") def get_my_value(): for nextvalue in it: return values + nextvalue lib.get_my_value = get_my_value # values[0] = 41 assert lib.my_value == 41 # [0] p = ffi.addressof(lib, 'my_value') # [1] assert p == values + 1 assert p[-1] == 41 assert p[+1] == 0 lib.my_value = 42 # [2] assert values[2] == 42 assert p[-1] == 41 assert p[+1] == 42 # # if get_my_value raises or returns nonsense, the exception is printed # to stderr like with any callback, but then the C expression 'my_value' # expand to '*NULL'. We assume here that '&my_value' will return NULL # without segfaulting, and check for NULL when accessing the variable. @ffi.callback("int *(*)(void)") def get_my_value(): raise LookupError lib.get_my_value = get_my_value py.test.raises(ffi.error, getattr, lib, 'my_value') py.test.raises(ffi.error, setattr, lib, 'my_value', 50) py.test.raises(ffi.error, ffi.addressof, lib, 'my_value') @ffi.callback("int *(*)(void)") def get_my_value(): return "hello" lib.get_my_value = get_my_value py.test.raises(ffi.error, getattr, lib, 'my_value') e = py.test.raises(ffi.error, setattr, lib, 'my_value', 50) assert str(e.value) == "global variable 'my_value' is at address NULL" def test_const_fields(): ffi = FFI() ffi.cdef("""struct foo_s { const int a; void *const b; };""") lib = verify(ffi, 'test_const_fields', """ struct foo_s { const int a; void *const b; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int") assert foo_s.fields[1][0] == 'b' assert foo_s.fields[1][1].type is ffi.typeof("void *") def test_restrict_fields(): ffi = FFI() ffi.cdef("""struct foo_s { void * restrict b; };""") lib = verify(ffi, 'test_restrict_fields', """ struct foo_s { void * __restrict b; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'b' assert foo_s.fields[0][1].type is ffi.typeof("void *") def test_volatile_fields(): ffi = FFI() ffi.cdef("""struct foo_s { void * volatile b; };""") lib = verify(ffi, 'test_volatile_fields', """ struct foo_s { void * volatile b; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'b' assert foo_s.fields[0][1].type is ffi.typeof("void *") def test_const_array_fields(): ffi = FFI() ffi.cdef("""struct foo_s { const int a[4]; };""") lib = verify(ffi, 'test_const_array_fields', """ struct foo_s { const int a[4]; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int[4]") def test_const_array_fields_varlength(): ffi = FFI() ffi.cdef("""struct foo_s { const int a[]; ...; };""") lib = verify(ffi, 'test_const_array_fields_varlength', """ struct foo_s { const int a[4]; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int[]") def test_const_array_fields_unknownlength(): ffi = FFI() ffi.cdef("""struct foo_s { const int a[...]; ...; };""") lib = verify(ffi, 'test_const_array_fields_unknownlength', """ struct foo_s { const int a[4]; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int[4]") def test_const_function_args(): ffi = FFI() ffi.cdef("""int foobar(const int a, const int *b, const int c[]);""") lib = verify(ffi, 'test_const_function_args', """ int foobar(const int a, const int *b, const int c[]) { return a + *b + *c; } """) assert lib.foobar(100, ffi.new("int *", 40), ffi.new("int *", 2)) == 142 def test_const_function_type_args(): ffi = FFI() ffi.cdef("""extern int(*foobar)(const int a,const int*b,const int c[]);""") lib = verify(ffi, 'test_const_function_type_args', """ int (*foobar)(const int a, const int *b, const int c[]); """) t = ffi.typeof(lib.foobar) assert t.args[0] is ffi.typeof("int") assert t.args[1] is ffi.typeof("int *") assert t.args[2] is ffi.typeof("int *") def test_const_constant(): ffi = FFI() ffi.cdef("""struct foo_s { int x,y; }; const struct foo_s myfoo;""") lib = verify(ffi, 'test_const_constant', """ struct foo_s { int x,y; }; const struct foo_s myfoo = { 40, 2 }; """) assert lib.myfoo.x == 40 assert lib.myfoo.y == 2 def test_const_via_typedef(): ffi = FFI() ffi.cdef("""typedef const int const_t; const_t aaa;""") lib = verify(ffi, 'test_const_via_typedef', """ typedef const int const_t; #define aaa 42 """) assert lib.aaa == 42 with pytest.raises(AttributeError): lib.aaa = 43 def test_win32_calling_convention_0(): ffi = FFI() ffi.cdef(""" int call1(int(__cdecl *cb)(int)); int (*const call2)(int(__stdcall *cb)(int)); """) lib = verify(ffi, 'test_win32_calling_convention_0', r""" #ifndef _MSC_VER # define __stdcall /* nothing */ #endif int call1(int(*cb)(int)) { int i, result = 0; //printf("call1: cb = %p\n", cb); for (i = 0; i < 1000; i++) result += cb(i); //printf("result = %d\n", result); return result; } int call2(int(__stdcall *cb)(int)) { int i, result = 0; //printf("call2: cb = %p\n", cb); for (i = 0; i < 1000; i++) result += cb(-i); //printf("result = %d\n", result); return result; } """) @ffi.callback("int(int)") def cb1(x): return x * 2 @ffi.callback("int __stdcall(int)") def cb2(x): return x * 3 res = lib.call1(cb1) assert res == 500*999*2 assert res == ffi.addressof(lib, 'call1')(cb1) res = lib.call2(cb2) assert res == -500*999*3 assert res == ffi.addressof(lib, 'call2')(cb2) if sys.platform == 'win32' and not sys.maxsize > 2**32: assert '__stdcall' in str(ffi.typeof(cb2)) assert '__stdcall' not in str(ffi.typeof(cb1)) py.test.raises(TypeError, lib.call1, cb2) py.test.raises(TypeError, lib.call2, cb1) else: assert '__stdcall' not in str(ffi.typeof(cb2)) assert ffi.typeof(cb2) is ffi.typeof(cb1) def test_win32_calling_convention_1(): ffi = FFI() ffi.cdef(""" int __cdecl call1(int(__cdecl *cb)(int)); int __stdcall call2(int(__stdcall *cb)(int)); int (__cdecl *const cb1)(int); int (__stdcall *const cb2)(int); """) lib = verify(ffi, 'test_win32_calling_convention_1', r""" #ifndef _MSC_VER # define __cdecl # define __stdcall #endif int __cdecl cb1(int x) { return x * 2; } int __stdcall cb2(int x) { return x * 3; } int __cdecl call1(int(__cdecl *cb)(int)) { int i, result = 0; //printf("here1\n"); //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); for (i = 0; i < 1000; i++) result += cb(i); //printf("result = %d\n", result); return result; } int __stdcall call2(int(__stdcall *cb)(int)) { int i, result = 0; //printf("here1\n"); //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); for (i = 0; i < 1000; i++) result += cb(-i); //printf("result = %d\n", result); return result; } """) #print '<<< cb1 =', ffi.addressof(lib, 'cb1') ptr_call1 = ffi.addressof(lib, 'call1') assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 #print '<<< cb2 =', ffi.addressof(lib, 'cb2') ptr_call2 = ffi.addressof(lib, 'call2') assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 #print '<<< done' def test_win32_calling_convention_2(): # any mistake in the declaration of plain function (including the # precise argument types and, here, the calling convention) are # automatically corrected. But this does not apply to the 'cb' # function pointer argument. ffi = FFI() ffi.cdef(""" int __stdcall call1(int(__cdecl *cb)(int)); int __cdecl call2(int(__stdcall *cb)(int)); int (__cdecl *const cb1)(int); int (__stdcall *const cb2)(int); """) lib = verify(ffi, 'test_win32_calling_convention_2', """ #ifndef _MSC_VER # define __cdecl # define __stdcall #endif int __cdecl call1(int(__cdecl *cb)(int)) { int i, result = 0; for (i = 0; i < 1000; i++) result += cb(i); return result; } int __stdcall call2(int(__stdcall *cb)(int)) { int i, result = 0; for (i = 0; i < 1000; i++) result += cb(-i); return result; } int __cdecl cb1(int x) { return x * 2; } int __stdcall cb2(int x) { return x * 3; } """) ptr_call1 = ffi.addressof(lib, 'call1') ptr_call2 = ffi.addressof(lib, 'call2') if sys.platform == 'win32' and not sys.maxsize > 2**32: py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 def test_win32_calling_convention_3(): ffi = FFI() ffi.cdef(""" struct point { int x, y; }; int (*const cb1)(struct point); int (__stdcall *const cb2)(struct point); struct point __stdcall call1(int(*cb)(struct point)); struct point call2(int(__stdcall *cb)(struct point)); """) lib = verify(ffi, 'test_win32_calling_convention_3', r""" #ifndef _MSC_VER # define __cdecl # define __stdcall #endif struct point { int x, y; }; int cb1(struct point pt) { return pt.x + 10 * pt.y; } int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } struct point __stdcall call1(int(__cdecl *cb)(struct point)) { int i; struct point result = { 0, 0 }; //printf("here1\n"); //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); for (i = 0; i < 1000; i++) { struct point p = { i, -i }; int r = cb(p); result.x += r; result.y -= r; } return result; } struct point __cdecl call2(int(__stdcall *cb)(struct point)) { int i; struct point result = { 0, 0 }; for (i = 0; i < 1000; i++) { struct point p = { -i, i }; int r = cb(p); result.x += r; result.y -= r; } return result; } """) ptr_call1 = ffi.addressof(lib, 'call1') ptr_call2 = ffi.addressof(lib, 'call2') if sys.platform == 'win32' and not sys.maxsize > 2**32: py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) pt = lib.call1(ffi.addressof(lib, 'cb1')) assert (pt.x, pt.y) == (-9*500*999, 9*500*999) pt = ptr_call1(ffi.addressof(lib, 'cb1')) assert (pt.x, pt.y) == (-9*500*999, 9*500*999) pt = lib.call2(ffi.addressof(lib, 'cb2')) assert (pt.x, pt.y) == (99*500*999, -99*500*999) pt = ptr_call2(ffi.addressof(lib, 'cb2')) assert (pt.x, pt.y) == (99*500*999, -99*500*999) def test_extern_python_1(): import warnings ffi = FFI() with warnings.catch_warnings(record=True) as log: ffi.cdef(""" extern "Python" { int bar(int, int); void baz(int, int); int bok(void); void boz(void); } """) assert len(log) == 0, "got a warning: %r" % (log,) lib = verify(ffi, 'test_extern_python_1', """ static void baz(int, int); /* forward */ """) assert ffi.typeof(lib.bar) == ffi.typeof("int(*)(int, int)") with FdWriteCapture() as f: res = lib.bar(4, 5) assert res == 0 assert f.getvalue() == ( b"extern \"Python\": function _CFFI_test_extern_python_1.bar() called, " b"but no code was attached " b"to it yet with @ffi.def_extern(). Returning 0.\n") @ffi.def_extern("bar") def my_bar(x, y): seen.append(("Bar", x, y)) return x * y assert my_bar != lib.bar seen = [] res = lib.bar(6, 7) assert seen == [("Bar", 6, 7)] assert res == 42 def baz(x, y): seen.append(("Baz", x, y)) baz1 = ffi.def_extern()(baz) assert baz1 is baz seen = [] baz(long(40), long(4)) res = lib.baz(long(50), long(8)) assert res is None assert seen == [("Baz", 40, 4), ("Baz", 50, 8)] assert type(seen[0][1]) is type(seen[0][2]) is long assert type(seen[1][1]) is type(seen[1][2]) is int @ffi.def_extern(name="bok") def bokk(): seen.append("Bok") return 42 seen = [] assert lib.bok() == 42 assert seen == ["Bok"] @ffi.def_extern() def boz(): seen.append("Boz") seen = [] assert lib.boz() is None assert seen == ["Boz"] def test_extern_python_bogus_name(): ffi = FFI() ffi.cdef("extern int abc;") lib = verify(ffi, 'test_extern_python_bogus_name', "int abc;") def fn(): pass py.test.raises(ffi.error, ffi.def_extern("unknown_name"), fn) py.test.raises(ffi.error, ffi.def_extern("abc"), fn) assert lib.abc == 0 e = py.test.raises(ffi.error, ffi.def_extern("abc"), fn) assert str(e.value) == ("ffi.def_extern('abc'): no 'extern \"Python\"' " "function with this name") e = py.test.raises(ffi.error, ffi.def_extern(), fn) assert str(e.value) == ("ffi.def_extern('fn'): no 'extern \"Python\"' " "function with this name") # py.test.raises(TypeError, ffi.def_extern(42), fn) py.test.raises((TypeError, AttributeError), ffi.def_extern(), "foo") class X: pass x = X() x.__name__ = x py.test.raises(TypeError, ffi.def_extern(), x) def test_extern_python_bogus_result_type(): ffi = FFI() ffi.cdef("""extern "Python" void bar(int);""") lib = verify(ffi, 'test_extern_python_bogus_result_type', "") # @ffi.def_extern() def bar(n): return n * 10 with StdErrCapture() as f: res = lib.bar(321) assert res is None msg = f.getvalue() assert "rom cffi callback %r" % (bar,) in msg assert "rying to convert the result back to C:\n" in msg assert msg.endswith( "TypeError: callback with the return type 'void' must return None\n") def test_extern_python_redefine(): ffi = FFI() ffi.cdef("""extern "Python" int bar(int);""") lib = verify(ffi, 'test_extern_python_redefine', "") # @ffi.def_extern() def bar(n): return n * 10 assert lib.bar(42) == 420 # @ffi.def_extern() def bar(n): return -n assert lib.bar(42) == -42 def test_extern_python_struct(): ffi = FFI() ffi.cdef(""" struct foo_s { int a, b, c; }; extern "Python" int bar(int, struct foo_s, int); extern "Python" { struct foo_s baz(int, int); struct foo_s bok(void); } """) lib = verify(ffi, 'test_extern_python_struct', "struct foo_s { int a, b, c; };") # @ffi.def_extern() def bar(x, s, z): return x + s.a + s.b + s.c + z res = lib.bar(1000, [1001, 1002, 1004], 1008) assert res == 5015 # @ffi.def_extern() def baz(x, y): return [x + y, x - y, x * y] res = lib.baz(1000, 42) assert res.a == 1042 assert res.b == 958 assert res.c == 42000 # @ffi.def_extern() def bok(): return [10, 20, 30] res = lib.bok() assert [res.a, res.b, res.c] == [10, 20, 30] def test_extern_python_long_double(): ffi = FFI() ffi.cdef(""" extern "Python" int bar(int, long double, int); extern "Python" long double baz(int, int); extern "Python" long double bok(void); """) lib = verify(ffi, 'test_extern_python_long_double', "") # @ffi.def_extern() def bar(x, l, z): seen.append((x, l, z)) return 6 seen = [] lib.bar(10, 3.5, 20) expected = ffi.cast("long double", 3.5) assert repr(seen) == repr([(10, expected, 20)]) # @ffi.def_extern() def baz(x, z): assert x == 10 and z == 20 return expected res = lib.baz(10, 20) assert repr(res) == repr(expected) # @ffi.def_extern() def bok(): return expected res = lib.bok() assert repr(res) == repr(expected) def test_extern_python_signature(): ffi = FFI() lib = verify(ffi, 'test_extern_python_signature', "") py.test.raises(TypeError, ffi.def_extern(425), None) py.test.raises(TypeError, ffi.def_extern, 'a', 'b', 'c', 'd') def test_extern_python_errors(): ffi = FFI() ffi.cdef(""" extern "Python" int bar(int); """) lib = verify(ffi, 'test_extern_python_errors', "") seen = [] def oops(*args): seen.append(args) @ffi.def_extern(onerror=oops) def bar(x): return x + "" assert lib.bar(10) == 0 @ffi.def_extern(name="bar", onerror=oops, error=-66) def bar2(x): return x + "" assert lib.bar(10) == -66 assert len(seen) == 2 exc, val, tb = seen[0] assert exc is TypeError assert isinstance(val, TypeError) assert tb.tb_frame.f_code.co_name == "bar" exc, val, tb = seen[1] assert exc is TypeError assert isinstance(val, TypeError) assert tb.tb_frame.f_code.co_name == "bar2" # # a case where 'onerror' is not callable py.test.raises(TypeError, ffi.def_extern(name='bar', onerror=42), lambda x: x) def test_extern_python_stdcall(): ffi = FFI() ffi.cdef(""" extern "Python" int __stdcall foo(int); extern "Python" int WINAPI bar(int); static int (__stdcall * mycb1)(int); static int indirect_call(int); """) lib = verify(ffi, 'test_extern_python_stdcall', """ #ifndef _MSC_VER # define __stdcall #endif static int (__stdcall * mycb1)(int); static int indirect_call(int x) { return mycb1(x); } """) # @ffi.def_extern() def foo(x): return x + 42 @ffi.def_extern() def bar(x): return x + 43 assert lib.foo(100) == 142 assert lib.bar(100) == 143 lib.mycb1 = lib.foo assert lib.mycb1(200) == 242 assert lib.indirect_call(300) == 342 def test_extern_python_plus_c(): ffi = FFI() ffi.cdef(""" extern "Python+C" int foo(int); extern "C +\tPython" int bar(int); int call_me(int); """) lib = verify(ffi, 'test_extern_python_plus_c', """ int foo(int); #ifdef __GNUC__ __attribute__((visibility("hidden"))) #endif int bar(int); static int call_me(int x) { return foo(x) - bar(x); } """) # @ffi.def_extern() def foo(x): return x * 42 @ffi.def_extern() def bar(x): return x * 63 assert lib.foo(100) == 4200 assert lib.bar(100) == 6300 assert lib.call_me(100) == -2100 def test_introspect_function(): ffi = FFI() ffi.cdef("float f1(double);") lib = verify(ffi, 'test_introspect_function', """ float f1(double x) { return (float)x; } """) assert dir(lib) == ['f1'] FUNC = ffi.typeof(lib.f1) assert FUNC.kind == 'function' assert FUNC.args[0].cname == 'double' assert FUNC.result.cname == 'float' assert ffi.typeof(ffi.addressof(lib, 'f1')) is FUNC def test_introspect_global_var(): ffi = FFI() ffi.cdef("extern float g1;") lib = verify(ffi, 'test_introspect_global_var', """ float g1; """) assert dir(lib) == ['g1'] FLOATPTR = ffi.typeof(ffi.addressof(lib, 'g1')) assert FLOATPTR.kind == 'pointer' assert FLOATPTR.item.cname == 'float' def test_introspect_global_var_array(): ffi = FFI() ffi.cdef("extern float g1[100];") lib = verify(ffi, 'test_introspect_global_var_array', """ float g1[100]; """) assert dir(lib) == ['g1'] FLOATARRAYPTR = ffi.typeof(ffi.addressof(lib, 'g1')) assert FLOATARRAYPTR.kind == 'pointer' assert FLOATARRAYPTR.item.kind == 'array' assert FLOATARRAYPTR.item.length == 100 assert ffi.typeof(lib.g1) is FLOATARRAYPTR.item def test_introspect_integer_const(): ffi = FFI() ffi.cdef("#define FOO 42") lib = verify(ffi, 'test_introspect_integer_const', """ #define FOO 42 """) assert dir(lib) == ['FOO'] assert lib.FOO == ffi.integer_const('FOO') == 42 def test_introspect_typedef(): ffi = FFI() ffi.cdef("typedef int foo_t;") lib = verify(ffi, 'test_introspect_typedef', """ typedef int foo_t; """) assert ffi.list_types() == (['foo_t'], [], []) assert ffi.typeof('foo_t').kind == 'primitive' assert ffi.typeof('foo_t').cname == 'int' def test_introspect_typedef_multiple(): ffi = FFI() ffi.cdef("typedef signed char a_t, c_t, g_t, b_t;") lib = verify(ffi, 'test_introspect_typedef_multiple', """ typedef signed char a_t, c_t, g_t, b_t; """) assert ffi.list_types() == (['a_t', 'b_t', 'c_t', 'g_t'], [], []) def test_introspect_struct(): ffi = FFI() ffi.cdef("struct foo_s { int a; };") lib = verify(ffi, 'test_introspect_struct', """ struct foo_s { int a; }; """) assert ffi.list_types() == ([], ['foo_s'], []) assert ffi.typeof('struct foo_s').kind == 'struct' assert ffi.typeof('struct foo_s').cname == 'struct foo_s' def test_introspect_union(): ffi = FFI() ffi.cdef("union foo_s { int a; };") lib = verify(ffi, 'test_introspect_union', """ union foo_s { int a; }; """) assert ffi.list_types() == ([], [], ['foo_s']) assert ffi.typeof('union foo_s').kind == 'union' assert ffi.typeof('union foo_s').cname == 'union foo_s' def test_introspect_struct_and_typedef(): ffi = FFI() ffi.cdef("typedef struct { int a; } foo_t;") lib = verify(ffi, 'test_introspect_struct_and_typedef', """ typedef struct { int a; } foo_t; """) assert ffi.list_types() == (['foo_t'], [], []) assert ffi.typeof('foo_t').kind == 'struct' assert ffi.typeof('foo_t').cname == 'foo_t' def test_introspect_included_type(): SOURCE = """ typedef signed char schar_t; struct sint_t { int x; }; """ ffi1 = FFI() ffi1.cdef(SOURCE) ffi2 = FFI() ffi2.include(ffi1) verify(ffi1, "test_introspect_included_type_parent", SOURCE) verify(ffi2, "test_introspect_included_type", SOURCE) assert ffi1.list_types() == ffi2.list_types() == ( ['schar_t'], ['sint_t'], []) def test_introspect_order(): ffi = FFI() ffi.cdef("union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb;") ffi.cdef("union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb;") ffi.cdef("union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb;") verify(ffi, "test_introspect_order", """ union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb; union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb; union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb; """) assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'], ['CFFIa', 'CFFIcc', 'CFFIccc'], ['CFFIaa', 'CFFIaaa', 'CFFIg']) def test_bool_in_cpp(): # this works when compiled as C, but in cffi < 1.7 it fails as C++ ffi = FFI() ffi.cdef("bool f(void);") lib = verify(ffi, "test_bool_in_cpp", "char f(void) { return 2; }") assert lib.f() is True def test_bool_in_cpp_2(): ffi = FFI() ffi.cdef('int add(int a, int b);') lib = verify(ffi, "test_bool_bug_cpp", ''' typedef bool _Bool; /* there is a Windows header with this line */ int add(int a, int b) { return a + b; }''', source_extension='.cpp') c = lib.add(2, 3) assert c == 5 def test_struct_field_opaque(): ffi = FFI() ffi.cdef("struct a { struct b b; };") e = py.test.raises(TypeError, verify, ffi, "test_struct_field_opaque", "?") assert str(e.value) == ("struct a: field 'a.b' is of an opaque" " type (not declared in cdef())") ffi = FFI() ffi.cdef("struct a { struct b b[2]; };") e = py.test.raises(TypeError, verify, ffi, "test_struct_field_opaque", "?") assert str(e.value) == ("struct a: field 'a.b' is of an opaque" " type (not declared in cdef())") ffi = FFI() ffi.cdef("struct a { struct b b[]; };") e = py.test.raises(TypeError, verify, ffi, "test_struct_field_opaque", "?") assert str(e.value) == ("struct a: field 'a.b' is of an opaque" " type (not declared in cdef())") def test_function_arg_opaque(): py.test.skip("can currently declare a function with an opaque struct " "as argument, but AFAICT it's impossible to call it later") def test_function_returns_opaque(): ffi = FFI() ffi.cdef("struct a foo(int);") e = py.test.raises(TypeError, verify, ffi, "test_function_returns_opaque", "?") assert str(e.value) == ("function foo: 'struct a' is used as result type," " but is opaque") def test_function_returns_union(): ffi = FFI() ffi.cdef("union u1 { int a, b; }; union u1 f1(int);") lib = verify(ffi, "test_function_returns_union", """ union u1 { int a, b; }; static union u1 f1(int x) { union u1 u; u.b = x; return u; } """) assert lib.f1(51).a == 51 def test_function_returns_partial_struct(): ffi = FFI() ffi.cdef("struct aaa { int a; ...; }; struct aaa f1(int);") lib = verify(ffi, "test_function_returns_partial_struct", """ struct aaa { int b, a, c; }; static struct aaa f1(int x) { struct aaa s = {0}; s.a = x; return s; } """) assert lib.f1(52).a == 52 def test_function_returns_float_complex(): if sys.platform == 'win32': py.test.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("float _Complex f1(float a, float b);"); lib = verify(ffi, "test_function_returns_float_complex", """ #include static float _Complex f1(float a, float b) { return a + I*2.0f*b; } """, no_cpp=True) # fails on some systems with C++ result = lib.f1(1.25, 5.1) assert type(result) == complex assert result.real == 1.25 # exact assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact def test_function_returns_double_complex(): if sys.platform == 'win32': py.test.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("double _Complex f1(double a, double b);"); lib = verify(ffi, "test_function_returns_double_complex", """ #include static double _Complex f1(double a, double b) { return a + I*2.0*b; } """, no_cpp=True) # fails on some systems with C++ result = lib.f1(1.25, 5.1) assert type(result) == complex assert result.real == 1.25 # exact assert result.imag == 2*5.1 # exact def test_function_argument_float_complex(): if sys.platform == 'win32': py.test.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("float f1(float _Complex x);"); lib = verify(ffi, "test_function_argument_float_complex", """ #include static float f1(float _Complex x) { return cabsf(x); } """, no_cpp=True) # fails on some systems with C++ x = complex(12.34, 56.78) result = lib.f1(x) assert abs(result - abs(x)) < 1e-5 def test_function_argument_double_complex(): if sys.platform == 'win32': py.test.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("double f1(double _Complex);"); lib = verify(ffi, "test_function_argument_double_complex", """ #include static double f1(double _Complex x) { return cabs(x); } """, no_cpp=True) # fails on some systems with C++ x = complex(12.34, 56.78) result = lib.f1(x) assert abs(result - abs(x)) < 1e-11 def test_typedef_array_dotdotdot(): ffi = FFI() ffi.cdef(""" typedef int foo_t[...], bar_t[...]; extern int gv[...]; typedef int mat_t[...][...]; typedef int vmat_t[][...]; """) lib = verify(ffi, "test_typedef_array_dotdotdot", """ typedef int foo_t[50], bar_t[50]; int gv[23]; typedef int mat_t[6][7]; typedef int vmat_t[][8]; """) assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int") assert ffi.sizeof("bar_t") == 50 * ffi.sizeof("int") assert len(ffi.new("foo_t")) == 50 assert len(ffi.new("bar_t")) == 50 assert ffi.sizeof(lib.gv) == 23 * ffi.sizeof("int") assert ffi.sizeof("mat_t") == 6 * 7 * ffi.sizeof("int") assert len(ffi.new("mat_t")) == 6 assert len(ffi.new("mat_t")[3]) == 7 py.test.raises(ffi.error, ffi.sizeof, "vmat_t") p = ffi.new("vmat_t", 4) assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int") def test_typedef_array_dotdotdot_usage(): ffi = FFI() ffi.cdef(""" typedef int foo_t[...]; typedef int mat_t[...][...]; struct s { foo_t a; foo_t *b; foo_t **c; }; int myfunc(foo_t a, foo_t *b, foo_t **c); struct sm { mat_t a; mat_t *b; mat_t **c; }; int myfuncm(mat_t a, mat_t *b, mat_t **c); """) lib = verify(ffi, "test_typedef_array_dotdotdot_usage", """ typedef int foo_t[50]; typedef int mat_t[6][7]; struct s { foo_t a; foo_t *b; foo_t **c; }; static int myfunc(foo_t a, foo_t *b, foo_t **c) { return (**c)[49]; } struct sm { mat_t a; mat_t *b; mat_t **c; }; static int myfuncm(mat_t a, mat_t *b, mat_t **c) { return (**c)[5][6]; } """) assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int") p = ffi.new("struct s *") assert ffi.sizeof(p[0]) == 50 * ffi.sizeof("int") + 2 * ffi.sizeof("void *") p.a[49] = 321 p.b = ffi.addressof(p, 'a') p.c = ffi.addressof(p, 'b') assert lib.myfunc(ffi.NULL, ffi.NULL, p.c) == 321 # assert ffi.sizeof("mat_t") == 42 * ffi.sizeof("int") p = ffi.new("struct sm *") assert ffi.sizeof(p[0]) == 42 * ffi.sizeof("int") + 2 * ffi.sizeof("void *") p.a[5][6] = -321 p.b = ffi.addressof(p, 'a') p.c = ffi.addressof(p, 'b') assert lib.myfuncm(ffi.NULL, ffi.NULL, p.c) == -321 def test_call_with_custom_field_pos(): ffi = FFI() ffi.cdef(""" struct foo { int x; ...; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_custom_field_pos", """ struct foo { int y, x; }; struct foo f(void) { struct foo s = { 40, 200 }; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().x == 200 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( 'ctype \'struct foo\' not supported as return value. It is a ' 'struct declared with "...;", but the C calling convention may ' 'depend on the missing fields; or, it contains anonymous ' 'struct/unions. Such structs are only supported ' 'as return value if the function is \'API mode\' and non-variadic ' '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() ' 'and not taking a final \'...\' argument)') def test_call_with_nested_anonymous_struct(): if sys.platform == 'win32': py.test.skip("needs a GCC extension") ffi = FFI() ffi.cdef(""" struct foo { int a; union { int b, c; }; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_nested_anonymous_struct", """ struct foo { int a; union { int b, c; }; }; struct foo f(void) { struct foo s; s.a = 40; s.b = 200; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().b == 200 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( 'ctype \'struct foo\' not supported as return value. It is a ' 'struct declared with "...;", but the C calling convention may ' 'depend on the missing fields; or, it contains anonymous ' 'struct/unions. Such structs are only supported ' 'as return value if the function is \'API mode\' and non-variadic ' '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() ' 'and not taking a final \'...\' argument)') def test_call_with_bitfield(): ffi = FFI() ffi.cdef(""" struct foo { int x:5; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_bitfield", """ struct foo { int x:5; }; struct foo f(void) { struct foo s = { 11 }; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().x == 11 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'struct foo' not supported as return value. It is a struct " "with bit fields, which libffi does not support. Such structs are " "only supported as return value if the function is 'API mode' and " "non-variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder." "set_source() and not taking a final '...' argument)") def test_call_with_zero_length_field(): if sys.platform == 'win32': py.test.skip("zero-length field not supported by MSVC") ffi = FFI() ffi.cdef(""" struct foo { int a; int x[0]; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_zero_length_field", """ struct foo { int a; int x[0]; }; struct foo f(void) { struct foo s = { 42 }; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().a == 42 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'struct foo' not supported as return value. It is a " "struct with a zero-length array, which libffi does not support." " Such structs are only supported as return value if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" "+ffibuilder.set_source() and not taking a final '...' argument)") def test_call_with_union(): ffi = FFI() ffi.cdef(""" union foo { int a; char b; }; union foo f(void); union foo g(int, ...); """) lib = verify(ffi, "test_call_with_union", """ union foo { int a; char b; }; union foo f(void) { union foo s = { 42 }; return s; } union foo g(int a, ...) { return f(); } """) assert lib.f().a == 42 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'union foo' not supported as return value by libffi. " "Unions are only supported as return value if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" "+ffibuilder.set_source() and not taking a final '...' argument)") def test_call_with_packed_struct(): if sys.platform == 'win32': py.test.skip("needs a GCC extension") ffi = FFI() ffi.cdef(""" struct foo { char y; int x; }; struct foo f(void); struct foo g(int, ...); """, packed=True) lib = verify(ffi, "test_call_with_packed_struct", """ struct foo { char y; int x; } __attribute__((packed)); struct foo f(void) { struct foo s = { 40, 200 }; return s; } struct foo g(int a, ...) { struct foo s = { 41, 201 }; return s; } """) assert ord(lib.f().y) == 40 assert lib.f().x == 200 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'struct foo' not supported as return value. It is a " "'packed' structure, with a different layout than expected by libffi." " Such structs are only supported as return value if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" "+ffibuilder.set_source() and not taking a final '...' argument)") def test_pack_not_supported(): ffi = FFI() ffi.cdef("""struct foo { char y; int x; };""", pack=2) py.test.raises(NotImplementedError, verify, ffi, "test_pack_not_supported", "") def test_gcc_visibility_hidden(): if sys.platform == 'win32': py.test.skip("test for gcc/clang") ffi = FFI() ffi.cdef(""" int f(int); """) lib = verify(ffi, "test_gcc_visibility_hidden", """ int f(int a) { return a + 40; } """, extra_compile_args=['-fvisibility=hidden']) assert lib.f(2) == 42 def test_override_default_definition(): ffi = FFI() ffi.cdef("typedef long int16_t, char16_t;") lib = verify(ffi, "test_override_default_definition", "") assert ffi.typeof("int16_t") is ffi.typeof("char16_t") is ffi.typeof("long") def test_char16_char32_type(no_cpp=False): if no_cpp is False and sys.platform == "win32": py.test.skip("aaaaaaa why do modern MSVC compilers still define " "a very old __cplusplus value") ffi = FFI() ffi.cdef(""" char16_t foo_2bytes(char16_t); char32_t foo_4bytes(char32_t); """) lib = verify(ffi, "test_char16_char32_type" + no_cpp * "_nocpp", """ #if !defined(__cplusplus) || (!defined(_LIBCPP_VERSION) && __cplusplus < 201103L) typedef uint_least16_t char16_t; typedef uint_least32_t char32_t; #endif char16_t foo_2bytes(char16_t a) { return (char16_t)(a + 42); } char32_t foo_4bytes(char32_t a) { return (char32_t)(a + 42); } """, no_cpp=no_cpp) assert lib.foo_2bytes(u+'\u1234') == u+'\u125e' assert lib.foo_4bytes(u+'\u1234') == u+'\u125e' assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f' py.test.raises(TypeError, lib.foo_2bytes, u+'\U00012345') py.test.raises(TypeError, lib.foo_2bytes, 1234) py.test.raises(TypeError, lib.foo_4bytes, 1234) def test_char16_char32_plain_c(): test_char16_char32_type(no_cpp=True) def test_loader_spec(): ffi = FFI() lib = verify(ffi, "test_loader_spec", "") if sys.version_info < (3,): assert not hasattr(lib, '__loader__') assert not hasattr(lib, '__spec__') else: assert lib.__loader__ is None assert lib.__spec__ is None def test_realize_struct_error(): ffi = FFI() ffi.cdef("""typedef ... foo_t; struct foo_s { void (*x)(foo_t); };""") lib = verify(ffi, "test_realize_struct_error", """ typedef int foo_t; struct foo_s { void (*x)(foo_t); }; """) py.test.raises(TypeError, ffi.new, "struct foo_s *") def test_from_buffer_struct(): ffi = FFI() ffi.cdef("""struct foo_s { int a, b; };""") lib = verify(ffi, "test_from_buffer_struct_p", """ struct foo_s { int a, b; }; """) p = ffi.new("struct foo_s *", [-219239, 58974983]) q = ffi.from_buffer("struct foo_s[]", ffi.buffer(p)) assert ffi.typeof(q) == ffi.typeof("struct foo_s[]") assert len(q) == 1 assert q[0].a == p.a assert q[0].b == p.b assert q == p q = ffi.from_buffer("struct foo_s *", ffi.buffer(p)) assert ffi.typeof(q) == ffi.typeof("struct foo_s *") assert q.a == p.a assert q.b == p.b assert q[0].a == p.a assert q[0].b == p.b assert q == p def test_unnamed_bitfield_1(): ffi = FFI() ffi.cdef("""struct A { char : 1; };""") lib = verify(ffi, "test_unnamed_bitfield_1", """ struct A { char : 1; }; """) p = ffi.new("struct A *") assert ffi.sizeof(p[0]) == 1 # Note: on gcc, the type name is ignored for anonymous bitfields # and that's why the result is 1. On MSVC, the result is # sizeof("char") which is also 1. def test_unnamed_bitfield_2(): ffi = FFI() ffi.cdef("""struct A { short c : 1; short : 1; short d : 1; short : 1; };""") lib = verify(ffi, "test_unnamed_bitfield_2", """ struct A { short c : 1; short : 1; short d : 1; short : 1; }; """) p = ffi.new("struct A *") assert ffi.sizeof(p[0]) == ffi.sizeof("short") def test_unnamed_bitfield_3(): ffi = FFI() ffi.cdef("""struct A { struct { char : 1; char : 1; } b; };""") lib = verify(ffi, "test_unnamed_bitfield_3", """ struct A { struct { char : 1; char : 1; } b; }; """) p = ffi.new("struct A *") assert ffi.sizeof(p[0]) == 1 # Note: on gcc, the type name is ignored for anonymous bitfields # and that's why the result is 1. On MSVC, the result is # sizeof("char") which is also 1. def test_unnamed_bitfield_4(): ffi = FFI() ffi.cdef("""struct A { struct { unsigned c : 1; unsigned : 1; unsigned d : 1; unsigned : 1; } a; }; struct B { struct A a; };""") lib = verify(ffi, "test_unnamed_bitfield_4", """ struct A { struct { unsigned c : 1; unsigned : 1; unsigned d : 1; unsigned : 1; } a; }; struct B { struct A a; }; """) b = ffi.new("struct B *") a = ffi.new("struct A *") assert ffi.sizeof(a[0]) == ffi.sizeof("unsigned") assert ffi.sizeof(b[0]) == ffi.sizeof(a[0]) def test_struct_with_func_with_struct_pointer_arg(): ffi = FFI() ffi.cdef("""struct BinaryTree { int (* CompareKey)(struct BinaryTree *tree); };""") lib = verify(ffi, "test_struct_with_func_with_struct_pointer_arg", """ struct BinaryTree { int (* CompareKey)(struct BinaryTree *tree); }; """) ffi.new("struct BinaryTree *") def test_struct_with_func_with_struct_arg(): ffi = FFI() ffi.cdef("""struct BinaryTree { int (* CompareKey)(struct BinaryTree tree); };""") lib = verify(ffi, "test_struct_with_func_with_struct_arg", """ struct BinaryTree { int (* CompareKey)(struct BinaryTree tree); }; """) py.test.raises(RuntimeError, ffi.new, "struct BinaryTree *") def test_passing_large_list(): ffi = FFI() ffi.cdef("""void passing_large_list(long[]);""") lib = verify(ffi, "test_passing_large_list", """ static void passing_large_list(long a[]) { } """) arg = list(range(20000000)) lib.passing_large_list(arg) # assert did not segfault