def cast_int_addr(n):
"""Cast an address to a Python int
This could be a Python integer or a CFFI pointer
"""
if isinstance(n, (int, long)):
return n
try:
import cffi
except ImportError:
pass
else:
# from pyzmq, this is an FFI void *
ffi = cffi.FFI()
if isinstance(n, ffi.CData):
return int(ffi.cast("size_t", n))
raise ValueError("Cannot cast %r to int" % n)
python类FFI的实例源码
def compile_extension(name, header, sources=[], verbose=True, with_cuda=False,
**kwargs):
name, target_dir = _create_module_dir(name)
cffi_wrapper_name = '_' + name
wrapper_source, include_dirs = _setup_wrapper(with_cuda)
include_dirs.extend(kwargs.pop('include_dirs', []))
with open(header, 'r') as f:
header_source = f.read()
ffi = cffi.FFI()
sources = [os.path.abspath(src) for src in sources]
ffi.set_source(cffi_wrapper_name, wrapper_source + header_source,
sources=sources,
include_dirs=include_dirs, **kwargs)
ffi.cdef(_typedefs + header_source);
_build_extension(ffi, cffi_wrapper_name, target_dir, verbose)
_make_python_wrapper(name, cffi_wrapper_name, target_dir)
def test_import_from_lib(self):
ffi2 = cffi.FFI()
ffi2.cdef("int myfunc(int); int myvar;\n#define MYFOO ...\n")
outputfilename = recompile(ffi2, "_test_import_from_lib",
"int myfunc(int x) { return x + 1; }\n"
"int myvar = -5;\n"
"#define MYFOO 42", tmpdir=str(udir))
imp.load_dynamic("_test_import_from_lib", outputfilename)
from _test_import_from_lib.lib import myfunc, myvar, MYFOO
assert MYFOO == 42
assert myfunc(43) == 44
assert myvar == -5 # but can't be changed, so not very useful
py.test.raises(ImportError, "from _test_import_from_lib.lib import bar")
d = {}
exec("from _test_import_from_lib.lib import *", d)
assert (set(key for key in d if not key.startswith('_')) ==
set(['myfunc', 'MYFOO']))
#
# also test "import *" on the module itself, which should be
# equivalent to "import ffi, lib"
d = {}
exec("from _test_import_from_lib import *", d)
assert (sorted([x for x in d.keys() if not x.startswith('__')]) ==
['ffi', 'lib'])
def test_include_1():
sub_ffi = FFI()
sub_ffi.cdef("static const int k2 = 121212;")
sub_ffi.include(original_ffi)
assert 'macro FOOBAR' in original_ffi._parser._declarations
assert 'macro FOOBAZ' in original_ffi._parser._declarations
sub_ffi.set_source('re_python_pysrc', None)
sub_ffi.emit_python_code(str(tmpdir.join('_re_include_1.py')))
#
if sys.version_info[:2] >= (3, 3):
import importlib
importlib.invalidate_caches() # issue 197 (but can't reproduce myself)
#
from _re_include_1 import ffi
assert ffi.integer_const('FOOBAR') == -42
assert ffi.integer_const('FOOBAZ') == -43
assert ffi.integer_const('k2') == 121212
lib = ffi.dlopen(extmod) # <- a random unrelated library would be fine
assert lib.FOOBAR == -42
assert lib.FOOBAZ == -43
assert lib.k2 == 121212
#
p = ffi.new("bar_t *", [5, b"foobar"])
assert p.a[4] == ord('a')
def test_api_compile_explicit_target_3(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
x = ffi.compile(target="foo.bar.baz")
if sys.platform != 'win32':
self.check_produced_files({
'mod_name_in_package': {'foo.bar.baz': None,
'mymod.c': None,
'mymod.o': None}})
sofile = os.path.join(str(self.udir),
'mod_name_in_package', 'foo.bar.baz')
assert os.path.isabs(x) and os.path.samefile(x, sofile)
else:
self.check_produced_files({
'mod_name_in_package': {'foo.bar.baz': None,
'mymod.c': None},
'Release': '?'})
def _make_distutils_api(self):
os.mkdir("src")
os.mkdir(os.path.join("src", "pack1"))
with open(os.path.join("src", "pack1", "__init__.py"), "w") as f:
pass
with open("setup.py", "w") as f:
f.write("""if 1:
import cffi
ffi = cffi.FFI()
ffi.set_source("pack1.mymod", "/*code would be here*/")
from distutils.core import setup
setup(name='example1',
version='0.1',
packages=['pack1'],
package_dir={'': 'src'},
ext_modules=[ffi.distutils_extension()])
""")
def _make_setuptools_abi(self):
self._prepare_setuptools()
os.mkdir("src0")
os.mkdir(os.path.join("src0", "pack2"))
with open(os.path.join("src0", "pack2", "__init__.py"), "w") as f:
pass
with open(os.path.join("src0", "pack2", "_build.py"), "w") as f:
f.write("""if 1:
import cffi
ffi = cffi.FFI()
ffi.set_source("pack2.mymod", None)
""")
with open("setup.py", "w") as f:
f.write("""if 1:
from setuptools import setup
setup(name='example1',
version='0.1',
packages=['pack2'],
package_dir={'': 'src0'},
cffi_modules=["src0/pack2/_build.py:ffi"])
""")
def test_struct_included():
baseffi = FFI()
baseffi.cdef("struct foo_s { int x; };")
baseffi.set_source('test_struct_included_base', None)
#
ffi = FFI()
ffi.include(baseffi)
target = udir.join('test_struct_included.py')
make_py_source(ffi, 'test_struct_included', str(target))
assert target.read() == r"""# auto-generated file
import _cffi_backend
from test_struct_included_base import ffi as _ffi0
ffi = _cffi_backend.FFI('test_struct_included',
_version = 0x2601,
_types = b'\x00\x00\x00\x09',
_struct_unions = ((b'\x00\x00\x00\x00\x00\x00\x00\x08foo_s',),),
_includes = (_ffi0,),
)
"""
def test_global_var_int():
ffi = FFI()
ffi.cdef("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
py.test.raises(OverflowError, "lib.a = 2147483648")
py.test.raises(OverflowError, "lib.a = -2147483649")
lib.b = 525 # try with the first access being in setattr, too
assert lib.b == 525
py.test.raises(AttributeError, "del lib.a")
py.test.raises(AttributeError, "del lib.c")
py.test.raises(AttributeError, "del lib.foobarbaz")
def test_dir():
ffi = FFI()
ffi.cdef("int ff(int); 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_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
py.test.raises(OverflowError, "p.a -= 1")
py.test.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_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_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('')
#
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_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_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_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 <math.h>'),
libraries=[unicode(lib_m)])
assert lib.cos(1.43) == math.cos(1.43)
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_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_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_extern_python_bogus_name():
ffi = FFI()
ffi.cdef("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_callback_onerror(self):
ffi = FFI(backend=self.Backend())
seen = []
def oops(*args):
seen.append(args)
def otherfunc():
raise LookupError
def cb(n):
otherfunc()
a = ffi.callback("int(*)(int)", cb, error=42, onerror=oops)
res = a(234)
assert res == 42
assert len(seen) == 1
exc, val, tb = seen[0]
assert exc is LookupError
assert isinstance(val, LookupError)
assert tb.tb_frame.f_code.co_name == 'cb'
assert tb.tb_frame.f_locals['n'] == 234
def test_ffi_new_allocator_4(self):
ffi = FFI(backend=self.Backend())
py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None)
#
def myalloc2(size):
raise LookupError
alloc2 = ffi.new_allocator(myalloc2)
py.test.raises(LookupError, alloc2, "int[5]")
#
def myalloc3(size):
return 42
alloc3 = ffi.new_allocator(myalloc3)
e = py.test.raises(TypeError, alloc3, "int[5]")
assert str(e.value) == "alloc() must return a cdata object (got int)"
#
def myalloc4(size):
return ffi.cast("int", 42)
alloc4 = ffi.new_allocator(myalloc4)
e = py.test.raises(TypeError, alloc4, "int[5]")
assert str(e.value) == "alloc() must return a cdata pointer, not 'int'"
#
def myalloc5(size):
return ffi.NULL
alloc5 = ffi.new_allocator(myalloc5)
py.test.raises(MemoryError, alloc5, "int[5]")
def test_integer_ranges(self):
ffi = FFI(backend=self.Backend())
for (c_type, size) in [('char', 1),
('short', 2),
('short int', 2),
('', 4),
('int', 4),
('long', SIZE_OF_LONG),
('long int', SIZE_OF_LONG),
('long long', 8),
('long long int', 8),
]:
for unsigned in [None, False, True]:
c_decl = {None: '',
False: 'signed ',
True: 'unsigned '}[unsigned] + c_type
if c_decl == 'char' or c_decl == '':
continue
self._test_int_type(ffi, c_decl, size, unsigned)
def test_new_array_args(self):
ffi = FFI(backend=self.Backend())
# this tries to be closer to C: where we say "int x[5] = {10, 20, ..}"
# then here we must enclose the items in a list
p = ffi.new("int[5]", [10, 20, 30, 40, 50])
assert p[0] == 10
assert p[1] == 20
assert p[2] == 30
assert p[3] == 40
assert p[4] == 50
p = ffi.new("int[4]", [25])
assert p[0] == 25
assert p[1] == 0 # follow C convention rather than LuaJIT's
assert p[2] == 0
assert p[3] == 0
p = ffi.new("int[4]", [ffi.cast("int", -5)])
assert p[0] == -5
assert repr(p) == "<cdata 'int[4]' owning %d bytes>" % (4*SIZE_OF_INT)
def test_new_array_varsize(self):
ffi = FFI(backend=self.Backend())
p = ffi.new("int[]", 10) # a single integer is the length
assert p[9] == 0
py.test.raises(IndexError, "p[10]")
#
py.test.raises(TypeError, ffi.new, "int[]")
#
p = ffi.new("int[]", [-6, -7]) # a list is all the items, like C
assert p[0] == -6
assert p[1] == -7
py.test.raises(IndexError, "p[2]")
assert repr(p) == "<cdata 'int[]' owning %d bytes>" % (2*SIZE_OF_INT)
#
p = ffi.new("int[]", 0)
py.test.raises(IndexError, "p[0]")
py.test.raises(ValueError, ffi.new, "int[]", -1)
assert repr(p) == "<cdata 'int[]' owning 0 bytes>"
def test_float(self):
ffi = FFI(backend=self.Backend())
p = ffi.new("float[]", [-2, -2.5])
assert p[0] == -2.0
assert p[1] == -2.5
p[1] += 17.75
assert p[1] == 15.25
#
p = ffi.new("float*", 15.75)
assert p[0] == 15.75
py.test.raises(TypeError, int, p)
py.test.raises(TypeError, float, p)
p[0] = 0.0
assert bool(p) is True
#
p = ffi.new("float*", 1.1)
f = p[0]
assert f != 1.1 # because of rounding effect
assert abs(f - 1.1) < 1E-7
#
INF = 1E200 * 1E200
assert 1E200 != INF
p[0] = 1E200
assert p[0] == INF # infinite, not enough precision
def test_pointer_to_struct(self):
ffi = FFI(backend=self.Backend())
ffi.cdef("struct foo { int a; short b, c; };")
s = ffi.new("struct foo *")
s.a = -42
assert s[0].a == -42
p = ffi.new("struct foo **", s)
assert p[0].a == -42
assert p[0][0].a == -42
p[0].a = -43
assert s.a == -43
assert s[0].a == -43
p[0][0].a = -44
assert s.a == -44
assert s[0].a == -44
s.a = -45
assert p[0].a == -45
assert p[0][0].a == -45
s[0].a = -46
assert p[0].a == -46
assert p[0][0].a == -46
def test_sizeof_type(self):
ffi = FFI(backend=self.Backend())
ffi.cdef("""
struct foo { int a; short b, c, d; };
union foo { int a; short b, c, d; };
""")
for c_type, expected_size in [
('char', 1),
('unsigned int', 4),
('char *', SIZE_OF_PTR),
('int[5]', 20),
('struct foo', 12),
('union foo', 4),
]:
size = ffi.sizeof(c_type)
assert size == expected_size, (size, expected_size, ctype)