diff options
author | Armin Rigo <arigo@tunes.org> | 2016-09-03 19:29:47 +0200 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2016-09-03 19:29:47 +0200 |
commit | 3b4972b89c6f5efc62d90e2d5969c280e2bdd613 (patch) | |
tree | ea6d513a2baf87bd812ef9678cb831add13e7a60 | |
parent | bc99d488c73e2cfc4907088ddf4bf055821864e5 (diff) | |
download | cffi-3b4972b89c6f5efc62d90e2d5969c280e2bdd613.tar.gz |
Backed out changeset 0087e2aec9ef
Un-kill the ctypes backend. Issue #282 for a justification.
-rw-r--r-- | cffi/api.py | 30 | ||||
-rw-r--r-- | cffi/backend_ctypes.py | 1097 | ||||
-rw-r--r-- | testing/cffi0/backend_tests.py (renamed from testing/cffi0/test_backend.py) | 274 | ||||
-rw-r--r-- | testing/cffi0/test_cdata.py | 39 | ||||
-rw-r--r-- | testing/cffi0/test_ctypes.py | 40 | ||||
-rw-r--r-- | testing/cffi0/test_ffi_backend.py | 25 | ||||
-rw-r--r-- | testing/cffi0/test_function.py | 93 | ||||
-rw-r--r-- | testing/cffi0/test_ownlib.py | 22 | ||||
-rw-r--r-- | testing/cffi0/test_verify.py | 9 |
9 files changed, 1431 insertions, 198 deletions
diff --git a/cffi/api.py b/cffi/api.py index 6d1eb48..eda7209 100644 --- a/cffi/api.py +++ b/cffi/api.py @@ -46,21 +46,20 @@ class FFI(object): ''' def __init__(self, backend=None): - """Create an FFI instance. - - The 'backend' argument is not used any more and must be set to None. - It is still present only so that 'FFI(None)' still works, and - for a few tests. + """Create an FFI instance. The 'backend' argument is used to + select a non-default backend, mostly for tests. """ from . import cparser, model - if backend is None: - # You need the corresponding version of PyPy, or CPython - # with the '_cffi_backend' C extension module compiled. + # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with + # _cffi_backend.so compiled. import _cffi_backend as backend from . import __version__ assert backend.__version__ == __version__, \ "version mismatch, %s != %s" % (backend.__version__, __version__) + # (If you insist you can also try to pass the option + # 'backend=backend_ctypes.CTypesBackend()', but don't + # rely on it! It's probably not going to work well.) self._backend = backend self._lock = allocate_lock() @@ -76,6 +75,8 @@ class FFI(object): self._init_once_cache = {} self._cdef_version = None self._embedding = None + if hasattr(backend, 'set_ffi'): + backend.set_ffi(self) for name in backend.__dict__: if name.startswith('RTLD_'): setattr(self, name, getattr(backend, name)) @@ -83,10 +84,15 @@ class FFI(object): with self._lock: self.BVoidP = self._get_cached_btype(model.voidp_type) self.BCharA = self._get_cached_btype(model.char_array_type) - # attach these constants to the class - if not hasattr(FFI, 'NULL'): - FFI.NULL = self.cast(self.BVoidP, 0) - FFI.CData, FFI.CType = backend._get_types() + if isinstance(backend, types.ModuleType): + # _cffi_backend: attach these constants to the class + if not hasattr(FFI, 'NULL'): + FFI.NULL = self.cast(self.BVoidP, 0) + FFI.CData, FFI.CType = backend._get_types() + else: + # ctypes backend: attach these constants to the instance + self.NULL = self.cast(self.BVoidP, 0) + self.CData, self.CType = backend._get_types() def cdef(self, csource, override=False, packed=False): """Parse the given C source. This registers all declared functions, diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py new file mode 100644 index 0000000..b2579b3 --- /dev/null +++ b/cffi/backend_ctypes.py @@ -0,0 +1,1097 @@ +import ctypes, ctypes.util, operator, sys +from . import model + +if sys.version_info < (3,): + bytechr = chr +else: + unicode = str + long = int + xrange = range + bytechr = lambda num: bytes([num]) + +class CTypesType(type): + pass + +class CTypesData(object): + __metaclass__ = CTypesType + __slots__ = ['__weakref__'] + __name__ = '<cdata>' + + def __init__(self, *args): + raise TypeError("cannot instantiate %r" % (self.__class__,)) + + @classmethod + def _newp(cls, init): + raise TypeError("expected a pointer or array ctype, got '%s'" + % (cls._get_c_name(),)) + + @staticmethod + def _to_ctypes(value): + raise TypeError + + @classmethod + def _arg_to_ctypes(cls, *value): + try: + ctype = cls._ctype + except AttributeError: + raise TypeError("cannot create an instance of %r" % (cls,)) + if value: + res = cls._to_ctypes(*value) + if not isinstance(res, ctype): + res = cls._ctype(res) + else: + res = cls._ctype() + return res + + @classmethod + def _create_ctype_obj(cls, init): + if init is None: + return cls._arg_to_ctypes() + else: + return cls._arg_to_ctypes(init) + + @staticmethod + def _from_ctypes(ctypes_value): + raise TypeError + + @classmethod + def _get_c_name(cls, replace_with=''): + return cls._reftypename.replace(' &', replace_with) + + @classmethod + def _fix_class(cls): + cls.__name__ = 'CData<%s>' % (cls._get_c_name(),) + cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),) + cls.__module__ = 'ffi' + + def _get_own_repr(self): + raise NotImplementedError + + def _addr_repr(self, address): + if address == 0: + return 'NULL' + else: + if address < 0: + address += 1 << (8*ctypes.sizeof(ctypes.c_void_p)) + return '0x%x' % address + + def __repr__(self, c_name=None): + own = self._get_own_repr() + return '<cdata %r %s>' % (c_name or self._get_c_name(), own) + + def _convert_to_address(self, BClass): + if BClass is None: + raise TypeError("cannot convert %r to an address" % ( + self._get_c_name(),)) + else: + raise TypeError("cannot convert %r to %r" % ( + self._get_c_name(), BClass._get_c_name())) + + @classmethod + def _get_size(cls): + return ctypes.sizeof(cls._ctype) + + def _get_size_of_instance(self): + return ctypes.sizeof(self._ctype) + + @classmethod + def _cast_from(cls, source): + raise TypeError("cannot cast to %r" % (cls._get_c_name(),)) + + def _cast_to_integer(self): + return self._convert_to_address(None) + + @classmethod + def _alignment(cls): + return ctypes.alignment(cls._ctype) + + def __iter__(self): + raise TypeError("cdata %r does not support iteration" % ( + self._get_c_name()),) + + def _make_cmp(name): + cmpfunc = getattr(operator, name) + def cmp(self, other): + if isinstance(other, CTypesData): + return cmpfunc(self._convert_to_address(None), + other._convert_to_address(None)) + else: + return NotImplemented + cmp.func_name = name + return cmp + + __eq__ = _make_cmp('__eq__') + __ne__ = _make_cmp('__ne__') + __lt__ = _make_cmp('__lt__') + __le__ = _make_cmp('__le__') + __gt__ = _make_cmp('__gt__') + __ge__ = _make_cmp('__ge__') + + def __hash__(self): + return hash(type(self)) ^ hash(self._convert_to_address(None)) + + def _to_string(self, maxlen): + raise TypeError("string(): %r" % (self,)) + + +class CTypesGenericPrimitive(CTypesData): + __slots__ = [] + + def __eq__(self, other): + return self is other + + def __ne__(self, other): + return self is not other + + def __hash__(self): + return object.__hash__(self) + + def _get_own_repr(self): + return repr(self._from_ctypes(self._value)) + + +class CTypesGenericArray(CTypesData): + __slots__ = [] + + @classmethod + def _newp(cls, init): + return cls(init) + + def __iter__(self): + for i in xrange(len(self)): + yield self[i] + + def _get_own_repr(self): + return self._addr_repr(ctypes.addressof(self._blob)) + + +class CTypesGenericPtr(CTypesData): + __slots__ = ['_address', '_as_ctype_ptr'] + _automatic_casts = False + kind = "pointer" + + @classmethod + def _newp(cls, init): + return cls(init) + + @classmethod + def _cast_from(cls, source): + if source is None: + address = 0 + elif isinstance(source, CTypesData): + address = source._cast_to_integer() + elif isinstance(source, (int, long)): + address = source + else: + raise TypeError("bad type for cast to %r: %r" % + (cls, type(source).__name__)) + return cls._new_pointer_at(address) + + @classmethod + def _new_pointer_at(cls, address): + self = cls.__new__(cls) + self._address = address + self._as_ctype_ptr = ctypes.cast(address, cls._ctype) + return self + + def _get_own_repr(self): + try: + return self._addr_repr(self._address) + except AttributeError: + return '???' + + def _cast_to_integer(self): + return self._address + + def __nonzero__(self): + return bool(self._address) + __bool__ = __nonzero__ + + @classmethod + def _to_ctypes(cls, value): + if not isinstance(value, CTypesData): + raise TypeError("unexpected %s object" % type(value).__name__) + address = value._convert_to_address(cls) + return ctypes.cast(address, cls._ctype) + + @classmethod + def _from_ctypes(cls, ctypes_ptr): + address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0 + return cls._new_pointer_at(address) + + @classmethod + def _initialize(cls, ctypes_ptr, value): + if value: + ctypes_ptr.contents = cls._to_ctypes(value).contents + + def _convert_to_address(self, BClass): + if (BClass in (self.__class__, None) or BClass._automatic_casts + or self._automatic_casts): + return self._address + else: + return CTypesData._convert_to_address(self, BClass) + + +class CTypesBaseStructOrUnion(CTypesData): + __slots__ = ['_blob'] + + @classmethod + def _create_ctype_obj(cls, init): + # may be overridden + raise TypeError("cannot instantiate opaque type %s" % (cls,)) + + def _get_own_repr(self): + return self._addr_repr(ctypes.addressof(self._blob)) + + @classmethod + def _offsetof(cls, fieldname): + return getattr(cls._ctype, fieldname).offset + + def _convert_to_address(self, BClass): + if getattr(BClass, '_BItem', None) is self.__class__: + return ctypes.addressof(self._blob) + else: + return CTypesData._convert_to_address(self, BClass) + + @classmethod + def _from_ctypes(cls, ctypes_struct_or_union): + self = cls.__new__(cls) + self._blob = ctypes_struct_or_union + return self + + @classmethod + def _to_ctypes(cls, value): + return value._blob + + def __repr__(self, c_name=None): + return CTypesData.__repr__(self, c_name or self._get_c_name(' &')) + + +class CTypesBackend(object): + + PRIMITIVE_TYPES = { + 'char': ctypes.c_char, + 'short': ctypes.c_short, + 'int': ctypes.c_int, + 'long': ctypes.c_long, + 'long long': ctypes.c_longlong, + 'signed char': ctypes.c_byte, + 'unsigned char': ctypes.c_ubyte, + 'unsigned short': ctypes.c_ushort, + 'unsigned int': ctypes.c_uint, + 'unsigned long': ctypes.c_ulong, + 'unsigned long long': ctypes.c_ulonglong, + 'float': ctypes.c_float, + 'double': ctypes.c_double, + '_Bool': ctypes.c_bool, + } + + for _name in ['unsigned long long', 'unsigned long', + 'unsigned int', 'unsigned short', 'unsigned char']: + _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) + PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_void_p): + PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_size_t): + PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name] + + for _name in ['long long', 'long', 'int', 'short', 'signed char']: + _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) + PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_void_p): + PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name] + PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_size_t): + PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name] + + + def __init__(self): + self.RTLD_LAZY = 0 # not supported anyway by ctypes + self.RTLD_NOW = 0 + self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL + self.RTLD_LOCAL = ctypes.RTLD_LOCAL + + def set_ffi(self, ffi): + self.ffi = ffi + + def _get_types(self): + return CTypesData, CTypesType + + def load_library(self, path, flags=0): + cdll = ctypes.CDLL(path, flags) + return CTypesLibrary(self, cdll) + + def new_void_type(self): + class CTypesVoid(CTypesData): + __slots__ = [] + _reftypename = 'void &' + @staticmethod + def _from_ctypes(novalue): + return None + @staticmethod + def _to_ctypes(novalue): + if novalue is not None: + raise TypeError("None expected, got %s object" % + (type(novalue).__name__,)) + return None + CTypesVoid._fix_class() + return CTypesVoid + + def new_primitive_type(self, name): + if name == 'wchar_t': + raise NotImplementedError(name) + ctype = self.PRIMITIVE_TYPES[name] + if name == 'char': + kind = 'char' + elif name in ('float', 'double'): + kind = 'float' + else: + if name in ('signed char', 'unsigned char'): + kind = 'byte' + elif name == '_Bool': + kind = 'bool' + else: + kind = 'int' + is_signed = (ctype(-1).value == -1) + # + def _cast_source_to_int(source): + if isinstance(source, (int, long, float)): + source = int(source) + elif isinstance(source, CTypesData): + source = source._cast_to_integer() + elif isinstance(source, bytes): + source = ord(source) + elif source is None: + source = 0 + else: + raise TypeError("bad type for cast to %r: %r" % + (CTypesPrimitive, type(source).__name__)) + return source + # + kind1 = kind + class CTypesPrimitive(CTypesGenericPrimitive): + __slots__ = ['_value'] + _ctype = ctype + _reftypename = '%s &' % name + kind = kind1 + + def __init__(self, value): + self._value = value + + @staticmethod + def _create_ctype_obj(init): + if init is None: + return ctype() + return ctype(CTypesPrimitive._to_ctypes(init)) + + if kind == 'int' or kind == 'byte': + @classmethod + def _cast_from(cls, source): + source = _cast_source_to_int(source) + source = ctype(source).value # cast within range + return cls(source) + def __int__(self): + return self._value + + if kind == 'bool': + @classmethod + def _cast_from(cls, source): + if not isinstance(source, (int, long, float)): + source = _cast_source_to_int(source) + return cls(bool(source)) + def __int__(self): + return self._value + + if kind == 'char': + @classmethod + def _cast_from(cls, source): + source = _cast_source_to_int(source) + source = bytechr(source & 0xFF) + return cls(source) + def __int__(self): + return ord(self._value) + + if kind == 'float': + @classmethod + def _cast_from(cls, source): + if isinstance(source, float): + pass + elif isinstance(source, CTypesGenericPrimitive): + if hasattr(source, '__float__'): + source = float(source) + else: + source = int(source) + else: + source = _cast_source_to_int(source) + source = ctype(source).value # fix precision + return cls(source) + def __int__(self): + return int(self._value) + def __float__(self): + return self._value + + _cast_to_integer = __int__ + + if kind == 'int' or kind == 'byte' or kind == 'bool': + @staticmethod + def _to_ctypes(x): + if not isinstance(x, (int, long)): + if isinstance(x, CTypesData): + x = int(x) + else: + raise TypeError("integer expected, got %s" % + type(x).__name__) + if ctype(x).value != x: + if not is_signed and x < 0: + raise OverflowError("%s: negative integer" % name) + else: + raise OverflowError("%s: integer out of bounds" + % name) + return x + + if kind == 'char': + @staticmethod + def _to_ctypes(x): + if isinstance(x, bytes) and len(x) == 1: + return x + if isinstance(x, CTypesPrimitive): # <CData <char>> + return x._value + raise TypeError("character expected, got %s" % + type(x).__name__) + def __nonzero__(self): + return ord(self._value) != 0 + else: + def __nonzero__(self): + return self._value != 0 + __bool__ = __nonzero__ + + if kind == 'float': + @staticmethod + def _to_ctypes(x): + if not isinstance(x, (int, long, float, CTypesData)): + raise TypeError("float expected, got %s" % + type(x).__name__) + return ctype(x).value + + @staticmethod + def _from_ctypes(value): + return getattr(value, 'value', value) + + @staticmethod + def _initialize(blob, init): + blob.value = CTypesPrimitive._to_ctypes(init) + + if kind == 'char': + def _to_string(self, maxlen): + return self._value + if kind == 'byte': + def _to_string(self, maxlen): + return chr(self._value & 0xff) + # + CTypesPrimitive._fix_class() + return CTypesPrimitive + + def new_pointer_type(self, BItem): + getbtype = self.ffi._get_cached_btype + if BItem is getbtype(model.PrimitiveType('char')): + kind = 'charp' + elif BItem in (getbtype(model.PrimitiveType('signed char')), + getbtype(model.PrimitiveType('unsigned char'))): + kind = 'bytep' + elif BItem is getbtype(model.void_type): + kind = 'voidp' + else: + kind = 'generic' + # + class CTypesPtr(CTypesGenericPtr): + __slots__ = ['_own'] + if kind == 'charp': + __slots__ += ['__as_strbuf'] + _BItem = BItem + if hasattr(BItem, '_ctype'): + _ctype = ctypes.POINTER(BItem._ctype) + _bitem_size = ctypes.sizeof(BItem._ctype) + else: + _ctype = ctypes.c_void_p + if issubclass(BItem, CTypesGenericArray): + _reftypename = BItem._get_c_name('(* &)') + else: + _reftypename = BItem._get_c_name(' * &') + + def __init__(self, init): + ctypeobj = BItem._create_ctype_obj(init) + if kind == 'charp': + self.__as_strbuf = ctypes.create_string_buffer( + ctypeobj.value + b'\x00') + self._as_ctype_ptr = ctypes.cast( + self.__as_strbuf, self._ctype) + else: + self._as_ctype_ptr = ctypes.pointer(ctypeobj) + self._address = ctypes.cast(self._as_ctype_ptr, + ctypes.c_void_p).value + self._own = True + + def __add__(self, other): + if isinstance(other, (int, long)): + return self._new_pointer_at(self._address + + other * self._bitem_size) + else: + return NotImplemented + + def __sub__(self, other): + if isinstance(other, (int, long)): + return self._new_pointer_at(self._address - + other * self._bitem_size) + elif type(self) is type(other): + return (self._address - other._address) // self._bitem_size + else: + return NotImplemented + + def __getitem__(self, index): + if getattr(self, '_own', False) and index != 0: + raise IndexError + return BItem._from_ctypes(self._as_ctype_ptr[index]) + + def __setitem__(self, index, value): + self._as_ctype_ptr[index] = BItem._to_ctypes(value) + + if kind == 'charp' or kind == 'voidp': + @classmethod + def _arg_to_ctypes(cls, *value): + if value and isinstance(value[0], bytes): + return ctypes.c_char_p(value[0]) + else: + return super(CTypesPtr, cls)._arg_to_ctypes(*value) + + if kind == 'charp' or kind == 'bytep': + def _to_string(self, maxlen): + if maxlen < 0: + maxlen = sys.maxsize + p = ctypes.cast(self._as_ctype_ptr, + ctypes.POINTER(ctypes.c_char)) + n = 0 + while n < maxlen and p[n] != b'\x00': + n += 1 + return b''.join([p[i] for i in range(n)]) + + def _get_own_repr(self): + if getattr(self, '_own', False): + return 'owning %d bytes' % ( + ctypes.sizeof(self._as_ctype_ptr.contents),) + return super(CTypesPtr, self)._get_own_repr() + # + if (BItem is self.ffi._get_cached_btype(model.void_type) or + BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))): + CTypesPtr._automatic_casts = True + # + CTypesPtr._fix_class() + return CTypesPtr + + def new_array_type(self, CTypesPtr, length): + if length is None: + brackets = ' &[]' + else: + brackets = ' &[%d]' % length + BItem = CTypesPtr._BItem + getbtype = self.ffi._get_cached_btype + if BItem is getbtype(model.PrimitiveType('char')): + kind = 'char' + elif BItem in (getbtype(model.PrimitiveType('signed char')), + getbtype(model.PrimitiveType('unsigned char'))): + kind = 'byte' + else: + kind = 'generic' + # + class CTypesArray(CTypesGenericArray): + __slots__ = ['_blob', '_own'] + if length is not None: + _ctype = BItem._ctype * length + else: + __slots__.append('_ctype') + _reftypename = BItem._get_c_name(brackets) + _declared_length = length + _CTPtr = CTypesPtr + + def __init__(self, init): + if length is None: + if isinstance(init, (int, long)): + len1 = init + init = None + elif kind == 'char' and isinstance(init, bytes): + len1 = len(init) + 1 # extra null + else: + init = tuple(init) + len1 = len(init) + self._ctype = BItem._ctype * len1 + self._blob = self._ctype() + self._own = True + if init is not None: + self._initialize(self._blob, init) + + @staticmethod + def _initialize(blob, init): + if isinstance(init, bytes): + init = [init[i:i+1] for i in range(len(init))] + else: + init = tuple(init) + if len(init) > len(blob): + raise IndexError("too many initializers") + addr = ctypes.cast(blob, ctypes.c_void_p).value + PTR = ctypes.POINTER(BItem._ctype) + itemsize = ctypes.sizeof(BItem._ctype) + for i, value in enumerate(init): + p = ctypes.cast(addr + i * itemsize, PTR) + BItem._initialize(p.contents, value) + + def __len__(self): + return len(self._blob) + + def __getitem__(self, index): + if not (0 <= index < len(self._blob)): + raise IndexError + return BItem._from_ctypes(self._blob[index]) + + def __setitem__(self, index, value): + if not (0 <= index < len(self._blob)): + raise IndexError + self._blob[index] = BItem._to_ctypes(value) + + if kind == 'char' or kind == 'byte': + def _to_string(self, maxlen): + if maxlen < 0: + maxlen = len(self._blob) + p = ctypes.cast(self._blob, + ctypes.POINTER(ctypes.c_char)) + n = 0 + while n < maxlen and p[n] != b'\x00': + n += 1 + return b''.join([p[i] for i in range(n)]) + + def _get_own_repr(self): + if getattr(self, '_own', False): + return 'owning %d bytes' % (ctypes.sizeof(self._blob),) + return super(CTypesArray, self)._get_own_repr() + + def _convert_to_address(self, BClass): + if BClass in (CTypesPtr, None) or BClass._automatic_casts: + return ctypes.addressof(self._blob) + else: + return CTypesData._convert_to_address(self, BClass) + + @staticmethod + def _from_ctypes(ctypes_array): + self = CTypesArray.__new__(CTypesArray) + self._blob = ctypes_array + return self + + @staticmethod + def _arg_to_ctypes(value): + return CTypesPtr._arg_to_ctypes(value) + + def __add__(self, other): + if isinstance(other, (int, long)): + return CTypesPtr._new_pointer_at( + ctypes.addressof(self._blob) + + other * ctypes.sizeof(BItem._ctype)) + else: + return NotImplemented + + @classmethod + def _cast_from(cls, source): + raise NotImplementedError("casting to %r" % ( + cls._get_c_name(),)) + # + CTypesArray._fix_class() + return CTypesArray + + def _new_struct_or_union(self, kind, name, base_ctypes_class): + # + class struct_or_union(base_ctypes_class): + pass + struct_or_union.__name__ = '%s_%s' % (kind, name) + kind1 = kind + # + class CTypesStructOrUnion(CTypesBaseStructOrUnion): + __slots__ = ['_blob'] + _ctype = struct_or_union + _reftypename = '%s &' % (name,) + _kind = kind = kind1 + # + CTypesStructOrUnion._fix_class() + return CTypesStructOrUnion + + def new_struct_type(self, name): + return self._new_struct_or_union('struct', name, ctypes.Structure) + + def new_union_type(self, name): + return self._new_struct_or_union('union', name, ctypes.Union) + + def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, + totalsize=-1, totalalignment=-1, sflags=0): + if totalsize >= 0 or totalalignment >= 0: + raise NotImplementedError("the ctypes backend of CFFI does not support " + "structures completed by verify(); please " + "compile and install the _cffi_backend module.") + struct_or_union = CTypesStructOrUnion._ctype + fnames = [fname for (fname, BField, bitsize) in fields] + btypes = [BField for (fname, BField, bitsize) in fields] + bitfields = [bitsize for (fname, BField, bitsize) in fields] + # + bfield_types = {} + cfields = [] + for (fname, BField, bitsize) in fields: + if bitsize < 0: + cfields.append((fname, BField._ctype)) + bfield_types[fname] = BField + else: + cfields.append((fname, BField._ctype, bitsize)) + bfield_types[fname] = Ellipsis + if sflags & 8: + struct_or_union._pack_ = 1 + struct_or_union._fields_ = cfields + CTypesStructOrUnion._bfield_types = bfield_types + # + @staticmethod + def _create_ctype_obj(init): + result = struct_or_union() + if init is not None: + initialize(result, init) + return result + CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj + # + def initialize(blob, init): + if is_union: + if len(init) > 1: + raise ValueError("union initializer: %d items given, but " + "only one supported (use a dict if needed)" + % (len(init),)) + if not isinstance(init, dict): + if isinstance(init, (bytes, unicode)): + raise TypeError("union initializer: got a str") + init = tuple(init) + if len(init) > len(fnames): + raise ValueError("too many values for %s initializer" % + CTypesStructOrUnion._get_c_name()) + init = dict(zip(fnames, init)) + addr = ctypes.addressof(blob) + for fname, value in init.items(): + BField, bitsize = name2fieldtype[fname] + assert bitsize < 0, \ + "not implemented: initializer with bit fields" + offset = CTypesStructOrUnion._offsetof(fname) + PTR = ctypes.POINTER(BField._ctype) + p = ctypes.cast(addr + offset, PTR) + BField._initialize(p.contents, value) + is_union = CTypesStructOrUnion._kind == 'union' + name2fieldtype = dict(zip(fnames, zip(btypes, bitfields))) + # + for fname, BField, bitsize in fields: + if fname == '': + raise NotImplementedError("nested anonymous structs/unions") + if hasattr(CTypesStructOrUnion, fname): + raise ValueError("the field name %r conflicts in " + "the ctypes backend" % fname) + if bitsize < 0: + def getter(self, fname=fname, BField=BField, + offset=CTypesStructOrUnion._offsetof(fname), + PTR=ctypes.POINTER(BField._ctype)): + addr = ctypes.addressof(self._blob) + p = ctypes.cast(addr + offset, PTR) + return BField._from_ctypes(p.contents) + def setter(self, value, fname=fname, BField=BField): + setattr(self._blob, fname, BField._to_ctypes(value)) + # + if issubclass(BField, CTypesGenericArray): + setter = None + if BField._declared_length == 0: + def getter(self, fname=fname, BFieldPtr=BField._CTPtr, + offset=CTypesStructOrUnion._offsetof(fname), + PTR=ctypes.POINTER(BField._ctype)): + addr = ctypes.addressof(self._blob) + p = ctypes.cast(addr + offset, PTR) + return BFieldPtr._from_ctypes(p) + # + else: + def getter(self, fname=fname, BField=BField): + return BField._from_ctypes(getattr(self._blob, fname)) + def setter(self, value, fname=fname, BField=BField): + # xxx obscure workaround + value = BField._to_ctypes(value) + oldvalue = getattr(self._blob, fname) + setattr(self._blob, fname, value) + if value != getattr(self._blob, fname): + setattr(self._blob, fname, oldvalue) + raise OverflowError("value too large for bitfield") + setattr(CTypesStructOrUnion, fname, property(getter, setter)) + # + CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp)) + for fname in fnames: + if hasattr(CTypesPtr, fname): + raise ValueError("the field name %r conflicts in " + "the ctypes backend" % fname) + def getter(self, fname=fname): + return getattr(self[0], fname) + def setter(self, value, fname=fname): + setattr(self[0], fname, value) + setattr(CTypesPtr, fname, property(getter, setter)) + + def new_function_type(self, BArgs, BResult, has_varargs): + nameargs = [BArg._get_c_name() for BArg in BArgs] + if has_varargs: + nameargs.append('...') + nameargs = ', '.join(nameargs) + # + class CTypesFunctionPtr(CTypesGenericPtr): + __slots__ = ['_own_callback', '_name'] + _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None), + *[BArg._ctype for BArg in BArgs], + use_errno=True) + _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,)) + + def __init__(self, init, error=None): + # create a callback to the Python callable init() + import traceback + assert not has_varargs, "varargs not supported for callbacks" + if getattr(BResult, '_ctype', None) is not None: + error = BResult._from_ctypes( + BResult._create_ctype_obj(error)) + else: + error = None + def callback(*args): + args2 = [] + for arg, BArg in zip(args, BArgs): + args2.append(BArg._from_ctypes(arg)) + try: + res2 = init(*args2) + res2 = BResult._to_ctypes(res2) + except: + traceback.print_exc() + res2 = error + if issubclass(BResult, CTypesGenericPtr): + if res2: + res2 = ctypes.cast(res2, ctypes.c_void_p).value + # .value: http://bugs.python.org/issue1574593 + else: + res2 = None + #print repr(res2) + return res2 + if issubclass(BResult, CTypesGenericPtr): + # The only pointers callbacks can return are void*s: + # http://bugs.python.org/issue5710 + callback_ctype = ctypes.CFUNCTYPE( + ctypes.c_void_p, + *[BArg._ctype for BArg in BArgs], + use_errno=True) + else: + callback_ctype = CTypesFunctionPtr._ctype + self._as_ctype_ptr = callback_ctype(callback) + self._address = ctypes.cast(self._as_ctype_ptr, + ctypes.c_void_p).value + self._own_callback = init + + @staticmethod + def _initialize(ctypes_ptr, value): + if value: + raise NotImplementedError("ctypes backend: not supported: " + "initializers for function pointers") + + def __repr__(self): + c_name = getattr(self, '_name', None) + if c_name: + i = self._reftypename.index('(* &)') + if self._reftypename[i-1] not in ' )*': + c_name = ' ' + c_name + c_name = self._reftypename.replace('(* &)', c_name) + return CTypesData.__repr__(self, c_name) + + def _get_own_repr(self): + if getattr(self, '_own_callback', None) is not None: + return 'calling %r' % (self._own_callback,) + return super(CTypesFunctionPtr, self)._get_own_repr() + + def __call__(self, *args): + if has_varargs: + assert len(args) >= len(BArgs) + extraargs = args[len(BArgs):] + args = args[:len(BArgs)] + else: + assert len(args) == len(BArgs) + ctypes_args = [] + for arg, BArg in zip(args, BArgs): + ctypes_args.append(BArg._arg_to_ctypes(arg)) + if has_varargs: + for i, arg in enumerate(extraargs): + if arg is None: + ctypes_args.append(ctypes.c_void_p(0)) # NULL + continue + if not isinstance(arg, CTypesData): + raise TypeError( + "argument %d passed in the variadic part " + "needs to be a cdata object (got %s)" % + (1 + len(BArgs) + i, type(arg).__name__)) + ctypes_args.append(arg._arg_to_ctypes(arg)) + result = self._as_ctype_ptr(*ctypes_args) + return BResult._from_ctypes(result) + # + CTypesFunctionPtr._fix_class() + return CTypesFunctionPtr + + def new_enum_type(self, name, enumerators, enumvalues, CTypesInt): + assert isinstance(name, str) + reverse_mapping = dict(zip(reversed(enumvalues), + reversed(enumerators))) + # + class CTypesEnum(CTypesInt): + __slots__ = [] + _reftypename = '%s &' % name + + def _get_own_repr(self): + value = self._value + try: + return '%d: %s' % (value, reverse_mapping[value]) + except KeyError: + return str(value) + + def _to_string(self, maxlen): + value = self._value + try: + return reverse_mapping[value] + except KeyError: + return str(value) + # + CTypesEnum._fix_class() + return CTypesEnum + + def get_errno(self): + return ctypes.get_errno() + + def set_errno(self, value): + ctypes.set_errno(value) + + def string(self, b, maxlen=-1): + return b._to_string(maxlen) + + def buffer(self, bptr, size=-1): + raise NotImplementedError("buffer() with ctypes backend") + + def sizeof(self, cdata_or_BType): + if isinstance(cdata_or_BType, CTypesData): + return cdata_or_BType._get_size_of_instance() + else: + assert issubclass(cdata_or_BType, CTypesData) + return cdata_or_BType._get_size() + + def alignof(self, BType): + assert issubclass(BType, CTypesData) + return BType._alignment() + + def newp(self, BType, source): + if not issubclass(BType, CTypesData): + raise TypeError + return BType._newp(source) + + def cast(self, BType, source): + return BType._cast_from(source) + + def callback(self, BType, source, error, onerror): + assert onerror is None # XXX not implemented + return BType(source, error) + + def gcp(self, cdata, destructor): + BType = self.typeof(cdata) + + if destructor is None: + if not (hasattr(BType, '_gcp_type') and + BType._gcp_type is BType): + raise TypeError("Can remove destructor only on a object " + "previously returned by ffi.gc()") + cdata._destructor = None + return None + + try: + gcp_type = BType._gcp_type + except AttributeError: + class CTypesDataGcp(BType): + __slots__ = ['_orig', '_destructor'] + def __del__(self): + if self._destructor is not None: + self._destructor(self._orig) + gcp_type = BType._gcp_type = CTypesDataGcp + new_cdata = self.cast(gcp_type, cdata) + new_cdata._orig = cdata + new_cdata._destructor = destructor + return new_cdata + + typeof = type + + def getcname(self, BType, replace_with): + return BType._get_c_name(replace_with) + + def typeoffsetof(self, BType, fieldname, num=0): + if isinstance(fieldname, str): + if num == 0 and issubclass(BType, CTypesGenericPtr): + BType = BType._BItem + if not issubclass(BType, CTypesBaseStructOrUnion): + raise TypeError("expected a struct or union ctype") + BField = BType._bfield_types[fieldname] + if BField is Ellipsis: + raise TypeError("not supported for bitfields") + return (BField, BType._offsetof(fieldname)) + elif isinstance(fieldname, (int, long)): + if issubclass(BType, CTypesGenericArray): + BType = BType._CTPtr + if not issubclass(BType, CTypesGenericPtr): + raise TypeError("expected an array or ptr ctype") + BItem = BType._BItem + offset = BItem._get_size() * fieldname + if offset > sys.maxsize: + raise OverflowError + return (BItem, offset) + else: + raise TypeError(type(fieldname)) + + def rawaddressof(self, BTypePtr, cdata, offset=None): + if isinstance(cdata, CTypesBaseStructOrUnion): + ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) + elif isinstance(cdata, CTypesGenericPtr): + if offset is None or not issubclass(type(cdata)._BItem, + CTypesBaseStructOrUnion): + raise TypeError("unexpected cdata type") + ptr = type(cdata)._to_ctypes(cdata) + elif isinstance(cdata, CTypesGenericArray): + ptr = type(cdata)._to_ctypes(cdata) + else: + raise TypeError("expected a <cdata 'struct-or-union'>") + if offset: + ptr = ctypes.cast( + ctypes.c_void_p( + ctypes.cast(ptr, ctypes.c_void_p).value + offset), + type(ptr)) + return BTypePtr._from_ctypes(ptr) + + +class CTypesLibrary(object): + + def __init__(self, backend, cdll): + self.backend = backend + self.cdll = cdll + + def load_function(self, BType, name): + c_func = getattr(self.cdll, name) + funcobj = BType._from_ctypes(c_func) + funcobj._name = name + return funcobj + + def read_variable(self, BType, name): + try: + ctypes_obj = BType._ctype.in_dll(self.cdll, name) + except AttributeError as e: + raise NotImplementedError(e) + return BType._from_ctypes(ctypes_obj) + + def write_variable(self, BType, name, value): + new_ctypes_obj = BType._to_ctypes(value) + ctypes_obj = BType._ctype.in_dll(self.cdll, name) + ctypes.memmove(ctypes.addressof(ctypes_obj), + ctypes.addressof(new_ctypes_obj), + ctypes.sizeof(BType._ctype)) diff --git a/testing/cffi0/test_backend.py b/testing/cffi0/backend_tests.py index 8eee5c1..9f51988 100644 --- a/testing/cffi0/test_backend.py +++ b/testing/cffi0/backend_tests.py @@ -11,10 +11,10 @@ SIZE_OF_PTR = ctypes.sizeof(ctypes.c_void_p) SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar) -class TestBackend(object): +class BackendTests: def test_integer_ranges(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) for (c_type, size) in [('char', 1), ('short', 2), ('short int', 2), @@ -34,7 +34,7 @@ class TestBackend(object): self._test_int_type(ffi, c_decl, size, unsigned) def test_fixedsize_int(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) for size in [1, 2, 4, 8]: self._test_int_type(ffi, 'int%d_t' % (8*size), size, False) self._test_int_type(ffi, 'uint%d_t' % (8*size), size, True) @@ -79,12 +79,12 @@ class TestBackend(object): assert ffi.new(c_decl_ptr, long(max))[0] == max def test_new_unsupported_type(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) e = py.test.raises(TypeError, ffi.new, "int") assert str(e.value) == "expected a pointer or array ctype, got 'int'" def test_new_single_integer(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int *") # similar to ffi.new("int[1]") assert p[0] == 0 p[0] = -123 @@ -94,14 +94,14 @@ class TestBackend(object): assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT def test_new_array_no_arg(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int[10]") # the object was zero-initialized: for i in range(10): assert p[i] == 0 def test_array_indexing(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int[10]") p[0] = 42 p[9] = 43 @@ -113,7 +113,7 @@ class TestBackend(object): py.test.raises(IndexError, "p[-1] = 44") def test_new_array_args(self): - ffi = FFI() + 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]) @@ -132,7 +132,7 @@ class TestBackend(object): assert repr(p) == "<cdata 'int[4]' owning %d bytes>" % (4*SIZE_OF_INT) def test_new_array_varsize(self): - ffi = FFI() + 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]") @@ -151,7 +151,7 @@ class TestBackend(object): assert repr(p) == "<cdata 'int[]' owning 0 bytes>" def test_pointer_init(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) n = ffi.new("int *", 24) a = ffi.new("int *[10]", [ffi.NULL, ffi.NULL, n, n, ffi.NULL]) for i in range(10): @@ -160,14 +160,14 @@ class TestBackend(object): assert a[2] == a[3] == n def test_cannot_cast(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a = ffi.new("short int[10]") e = py.test.raises(TypeError, ffi.new, "long int **", a) msg = str(e.value) assert "'short[10]'" in msg and "'long *'" in msg def test_new_pointer_to_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a = ffi.new("int[4]", [100, 102, 104, 106]) p = ffi.new("int **", a) assert p[0] == ffi.cast("int *", a) @@ -180,7 +180,7 @@ class TestBackend(object): # keepalive: a def test_pointer_direct(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.cast("int*", 0) assert p is not None assert bool(p) is False @@ -195,11 +195,9 @@ class TestBackend(object): assert p[0] == 123 assert p[1] == 456 - TypeRepr = "<ctype '%s'>" - def test_repr(self): typerepr = self.TypeRepr - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { short a, b, c; };") p = ffi.cast("short unsigned int", 0) assert repr(p) == "<cdata 'unsigned short' 0>" @@ -250,7 +248,7 @@ class TestBackend(object): assert repr(ffi.typeof(q)) == typerepr % "struct foo" def test_new_array_of_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int[3][4]") p[0][0] = 10 p[2][3] = 33 @@ -259,12 +257,12 @@ class TestBackend(object): py.test.raises(IndexError, "p[1][-1]") def test_constructor_array_of_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int[3][2]", [[10, 11], [12, 13], [14, 15]]) assert p[2][1] == 15 def test_new_array_of_pointer_1(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) n = ffi.new("int*", 99) p = ffi.new("int*[4]") p[3] = n @@ -273,7 +271,7 @@ class TestBackend(object): assert a[0] == 99 def test_new_array_of_pointer_2(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) n = ffi.new("int[1]", [99]) p = ffi.new("int*[4]") p[3] = n @@ -282,7 +280,7 @@ class TestBackend(object): assert a[0] == 99 def test_char(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) assert ffi.new("char*", b"\xff")[0] == b'\xff' assert ffi.new("char*")[0] == b'\x00' assert int(ffi.cast("char", 300)) == 300 - 256 @@ -319,7 +317,7 @@ class TestBackend(object): py.test.skip("NotImplementedError: wchar_t") def test_wchar_t(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) self.check_wchar_t(ffi) assert ffi.new("wchar_t*", u+'x')[0] == u+'x' assert ffi.new("wchar_t*", u+'\u1234')[0] == u+'\u1234' @@ -374,7 +372,7 @@ class TestBackend(object): py.test.raises(IndexError, ffi.new, "wchar_t[2]", u+"abc") def test_none_as_null_doesnt_work(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int*[1]") assert p[0] is not None assert p[0] != None @@ -389,7 +387,7 @@ class TestBackend(object): assert p[0] == ffi.NULL def test_float(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("float[]", [-2, -2.5]) assert p[0] == -2.0 assert p[1] == -2.5 @@ -414,7 +412,7 @@ class TestBackend(object): assert p[0] == INF # infinite, not enough precision def test_struct_simple(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a; short b, c; };") s = ffi.new("struct foo*") assert s.a == s.b == s.c == 0 @@ -433,7 +431,7 @@ class TestBackend(object): py.test.raises(ValueError, ffi.new, "struct foo*", [1, 2, 3, 4]) def test_constructor_struct_from_dict(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a; short b, c; };") s = ffi.new("struct foo*", {'b': 123, 'c': 456}) assert s.a == 0 @@ -442,7 +440,7 @@ class TestBackend(object): py.test.raises(KeyError, ffi.new, "struct foo*", {'d': 456}) def test_struct_pointer(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a; short b, c; };") s = ffi.new("struct foo*") assert s[0].a == s[0].b == s[0].c == 0 @@ -452,13 +450,13 @@ class TestBackend(object): py.test.raises(IndexError, "s[1]") def test_struct_opaque(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) py.test.raises(TypeError, ffi.new, "struct baz*") p = ffi.new("struct baz **") # this works assert p[0] == ffi.NULL def test_pointer_to_struct(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a; short b, c; };") s = ffi.new("struct foo *") s.a = -42 @@ -480,7 +478,7 @@ class TestBackend(object): assert p[0][0].a == -46 def test_constructor_struct_of_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a[2]; char b[3]; };") s = ffi.new("struct foo *", [[10, 11], [b'a', b'b', b'c']]) assert s.a[1] == 11 @@ -491,7 +489,7 @@ class TestBackend(object): assert s.b[2] == b'c' def test_recursive_struct(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int value; struct foo *next; };") s = ffi.new("struct foo*") t = ffi.new("struct foo*") @@ -502,7 +500,7 @@ class TestBackend(object): assert s.next.value == 456 def test_union_simple(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("union foo { int a; short b, c; };") u = ffi.new("union foo*") assert u.a == u.b == u.c == 0 @@ -517,13 +515,13 @@ class TestBackend(object): assert repr(u) == "<cdata 'union foo *' owning %d bytes>" % SIZE_OF_INT def test_union_opaque(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) py.test.raises(TypeError, ffi.new, "union baz *") u = ffi.new("union baz **") # this works assert u[0] == ffi.NULL def test_union_initializer(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("union foo { char a; int b; };") py.test.raises(TypeError, ffi.new, "union foo*", b'A') py.test.raises(TypeError, ffi.new, "union foo*", 5) @@ -538,7 +536,7 @@ class TestBackend(object): assert u.b == 0 def test_sizeof_type(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" struct foo { int a; short b, c, d; }; union foo { int a; short b, c, d; }; @@ -555,7 +553,7 @@ class TestBackend(object): assert size == expected_size, (size, expected_size, ctype) def test_sizeof_cdata(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) assert ffi.sizeof(ffi.new("short*")) == SIZE_OF_PTR assert ffi.sizeof(ffi.cast("short", 123)) == SIZE_OF_SHORT # @@ -564,7 +562,7 @@ class TestBackend(object): assert ffi.sizeof(a) == 5 * SIZE_OF_INT def test_string_from_char_pointer(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) x = ffi.new("char*", b"x") assert str(x) == repr(x) assert ffi.string(x) == b"x" @@ -572,7 +570,7 @@ class TestBackend(object): py.test.raises(TypeError, ffi.new, "char*", unicode("foo")) def test_unicode_from_wchar_pointer(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) self.check_wchar_t(ffi) x = ffi.new("wchar_t*", u+"x") assert unicode(x) == unicode(repr(x)) @@ -580,7 +578,7 @@ class TestBackend(object): assert ffi.string(ffi.new("wchar_t*", u+"\x00")) == u+"" def test_string_from_char_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("char[]", b"hello.") p[5] = b'!' assert ffi.string(p) == b"hello!" @@ -597,7 +595,7 @@ class TestBackend(object): assert ffi.string(p) == b'hello' def test_string_from_wchar_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) self.check_wchar_t(ffi) assert ffi.string(ffi.cast("wchar_t", "x")) == u+"x" assert ffi.string(ffi.cast("wchar_t", u+"x")) == u+"x" @@ -625,7 +623,7 @@ class TestBackend(object): def test_fetch_const_char_p_field(self): # 'const' is ignored so far - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { const char *name; };") t = ffi.new("const char[]", b"testing") s = ffi.new("struct foo*", [t]) @@ -637,7 +635,7 @@ class TestBackend(object): def test_fetch_const_wchar_p_field(self): # 'const' is ignored so far - ffi = FFI() + ffi = FFI(backend=self.Backend()) self.check_wchar_t(ffi) ffi.cdef("struct foo { const wchar_t *name; };") t = ffi.new("const wchar_t[]", u+"testing") @@ -648,7 +646,7 @@ class TestBackend(object): assert s.name == ffi.NULL def test_voidp(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) py.test.raises(TypeError, ffi.new, "void*") p = ffi.new("void **") assert p[0] == ffi.NULL @@ -669,7 +667,7 @@ class TestBackend(object): py.test.raises(TypeError, "s.r = b") # fails def test_functionptr_simple(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) py.test.raises(TypeError, ffi.callback, "int(*)(int)", 0) def cb(n): return n + 1 @@ -694,12 +692,12 @@ class TestBackend(object): assert res == 46 def test_functionptr_advanced(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) t = ffi.typeof("int(*(*)(int))(int)") assert repr(t) == self.TypeRepr % "int(*(*)(int))(int)" def test_functionptr_voidptr_return(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) def cb(): return ffi.NULL p = ffi.callback("void*(*)()", cb) @@ -715,7 +713,7 @@ class TestBackend(object): assert res == void_ptr def test_functionptr_intptr_return(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) def cb(): return ffi.NULL p = ffi.callback("int*(*)()", cb) @@ -737,7 +735,7 @@ class TestBackend(object): assert res == int_array_ptr def test_functionptr_void_return(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) def foo(): pass foo_cb = ffi.callback("void foo()", foo) @@ -745,7 +743,7 @@ class TestBackend(object): assert result is None def test_char_cast(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.cast("int", b'\x01') assert ffi.typeof(p) is ffi.typeof("int") assert int(p) == 1 @@ -757,7 +755,7 @@ class TestBackend(object): assert int(p) == 0x81 def test_wchar_cast(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) self.check_wchar_t(ffi) p = ffi.cast("int", ffi.cast("wchar_t", u+'\u1234')) assert int(p) == 0x1234 @@ -773,7 +771,7 @@ class TestBackend(object): assert int(p) == 0x1234 def test_cast_array_to_charp(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a = ffi.new("short int[]", [0x1234, 0x5678]) p = ffi.cast("char*", a) data = b''.join([p[i] for i in range(4)]) @@ -783,7 +781,7 @@ class TestBackend(object): assert data == b'\x12\x34\x56\x78' def test_cast_between_pointers(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a = ffi.new("short int[]", [0x1234, 0x5678]) p = ffi.cast("short*", a) p2 = ffi.cast("int*", p) @@ -795,7 +793,7 @@ class TestBackend(object): assert data == b'\x12\x34\x56\x78' def test_cast_pointer_and_int(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a = ffi.new("short int[]", [0x1234, 0x5678]) l1 = ffi.cast("intptr_t", a) p = ffi.cast("short*", a) @@ -807,7 +805,7 @@ class TestBackend(object): assert int(ffi.cast("intptr_t", ffi.NULL)) == 0 def test_cast_functionptr_and_int(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) def cb(n): return n + 1 a = ffi.callback("int(*)(int)", cb) @@ -819,7 +817,7 @@ class TestBackend(object): assert hash(a) == hash(b) def test_callback_crash(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) def cb(n): raise Exception a = ffi.callback("int(*)(int)", cb, error=42) @@ -827,7 +825,7 @@ class TestBackend(object): assert res == 42 def test_structptr_argument(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int a, b; };") def cb(p): return p[0].a * 1000 + p[0].b * 100 + p[1].a * 10 + p[1].b @@ -838,7 +836,7 @@ class TestBackend(object): assert res == 5008 def test_array_argument_as_list(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int a, b; };") seen = [] def cb(argv): @@ -849,7 +847,7 @@ class TestBackend(object): assert seen == [b"foobar", b"baz"] def test_cast_float(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a = ffi.cast("float", 12) assert float(a) == 12.0 a = ffi.cast("float", 12.5) @@ -873,7 +871,7 @@ class TestBackend(object): assert ffi.string(a) == b"B" def test_enum(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("enum foo { A0, B0, CC0, D0 };") assert ffi.string(ffi.cast("enum foo", 0)) == "A0" assert ffi.string(ffi.cast("enum foo", 2)) == "CC0" @@ -895,7 +893,7 @@ class TestBackend(object): assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B2" def test_enum_in_struct(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("enum foo { A, B, C, D }; struct bar { enum foo e; };") s = ffi.new("struct bar *") s.e = 0 @@ -916,7 +914,7 @@ class TestBackend(object): py.test.raises(TypeError, "s.e = '#7'") def test_enum_non_contiguous(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("enum foo { A, B=42, C };") assert ffi.string(ffi.cast("enum foo", 0)) == "A" assert ffi.string(ffi.cast("enum foo", 42)) == "B" @@ -926,7 +924,7 @@ class TestBackend(object): assert ffi.string(invalid_value) == "2" def test_enum_char_hex_oct(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(r"enum foo{A='!', B='\'', C=0x10, D=010, E=- 0x10, F=-010};") assert ffi.string(ffi.cast("enum foo", ord('!'))) == "A" assert ffi.string(ffi.cast("enum foo", ord("'"))) == "B" @@ -936,7 +934,7 @@ class TestBackend(object): assert ffi.string(ffi.cast("enum foo", -8)) == "F" def test_enum_partial(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(r"enum foo {A, ...}; enum bar { B, C };") lib = ffi.dlopen(None) assert lib.B == 0 @@ -944,7 +942,7 @@ class TestBackend(object): assert lib.C == 1 def test_array_of_struct(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a, b; };") s = ffi.new("struct foo[1]") py.test.raises(AttributeError, 's.b') @@ -954,12 +952,12 @@ class TestBackend(object): py.test.raises(IndexError, 's[1]') def test_pointer_to_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int(**)[5]") assert repr(p) == "<cdata 'int(* *)[5]' owning %d bytes>" % SIZE_OF_PTR def test_iterate_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a = ffi.new("char[]", b"hello") assert list(a) == [b"h", b"e", b"l", b"l", b"o", b"\0"] assert list(iter(a)) == [b"h", b"e", b"l", b"l", b"o", b"\0"] @@ -970,14 +968,14 @@ class TestBackend(object): py.test.raises(TypeError, list, ffi.new("int *")) def test_offsetof(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a, b, c; };") assert ffi.offsetof("struct foo", "a") == 0 assert ffi.offsetof("struct foo", "b") == 4 assert ffi.offsetof("struct foo", "c") == 8 def test_offsetof_nested(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a, b, c; };" "struct bar { struct foo d, e; };") assert ffi.offsetof("struct bar", "e") == 12 @@ -987,7 +985,7 @@ class TestBackend(object): assert ffi.offsetof("struct bar", "e", "c") == 20 def test_offsetof_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int") assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int") ffi.cdef("struct bar { int a, b; int c[99]; };") @@ -996,14 +994,14 @@ class TestBackend(object): assert ffi.offsetof("struct bar", "c", 51) == 53 * ffi.sizeof("int") def test_alignof(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { char a; short b; char c; };") assert ffi.alignof("int") == 4 assert ffi.alignof("double") in (4, 8) assert ffi.alignof("struct foo") == 2 def test_bitfield(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a:10, b:20, c:3; };") assert ffi.sizeof("struct foo") == 8 s = ffi.new("struct foo *") @@ -1023,7 +1021,7 @@ class TestBackend(object): assert s.c == -4 def test_bitfield_enum(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef enum { AA, BB, CC } foo_e; typedef struct { foo_e f:2; } foo_s; @@ -1033,7 +1031,7 @@ class TestBackend(object): assert s.f == 2 def test_anonymous_struct(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("typedef struct { int a; } foo_t;") ffi.cdef("typedef struct { char b, c; } bar_t;") f = ffi.new("foo_t *", [12345]) @@ -1045,13 +1043,13 @@ class TestBackend(object): def test_struct_with_two_usages(self): for name in ['foo_s', '']: # anonymous or not - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("typedef struct %s { int a; } foo_t, *foo_p;" % name) f = ffi.new("foo_t *", [12345]) ps = ffi.new("foo_p[]", [f]) def test_pointer_arithmetic(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) s = ffi.new("short[]", list(range(100, 110))) p = ffi.cast("short *", s) assert p[2] == 102 @@ -1065,7 +1063,7 @@ class TestBackend(object): assert p+1 == s+1 def test_pointer_comparison(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) s = ffi.new("short[]", list(range(100))) p = ffi.cast("short *", s) assert (p < s) is False @@ -1116,7 +1114,7 @@ class TestBackend(object): assert (q != None) is True def test_no_integer_comparison(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) x = ffi.cast("int", 123) y = ffi.cast("int", 456) py.test.raises(TypeError, "x < y") @@ -1126,7 +1124,7 @@ class TestBackend(object): py.test.raises(TypeError, "z < y") def test_ffi_buffer_ptr(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a = ffi.new("short *", 100) try: b = ffi.buffer(a) @@ -1145,7 +1143,7 @@ class TestBackend(object): assert a[0] == 101 def test_ffi_buffer_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a = ffi.new("int[]", list(range(100, 110))) try: b = ffi.buffer(a) @@ -1162,7 +1160,7 @@ class TestBackend(object): assert a[1] == 0x45 def test_ffi_buffer_ptr_size(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a = ffi.new("short *", 0x4243) try: b = ffi.buffer(a, 1) @@ -1180,7 +1178,7 @@ class TestBackend(object): assert a[0] == 0x6343 def test_ffi_buffer_array_size(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) a1 = ffi.new("int[]", list(range(100, 110))) a2 = ffi.new("int[]", list(range(100, 115))) try: @@ -1190,7 +1188,7 @@ class TestBackend(object): assert ffi.buffer(a1)[:] == ffi.buffer(a2, 4*10)[:] def test_ffi_buffer_with_file(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) import tempfile, os, array fd, filename = tempfile.mkstemp() f = os.fdopen(fd, 'r+b') @@ -1210,7 +1208,7 @@ class TestBackend(object): os.unlink(filename) def test_ffi_buffer_with_io(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) import io, array f = io.BytesIO() a = ffi.new("int[]", list(range(1005))) @@ -1228,7 +1226,7 @@ class TestBackend(object): f.close() def test_array_in_struct(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int len; short data[5]; };") p = ffi.new("struct foo_s *") p.data[3] = 5 @@ -1236,7 +1234,7 @@ class TestBackend(object): assert repr(p.data).startswith("<cdata 'short[5]' 0x") def test_struct_containing_array_varsize_workaround(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int len; short data[0]; };") p = ffi.new("char[]", ffi.sizeof("struct foo_s") + 7 * SIZE_OF_SHORT) q = ffi.cast("struct foo_s *", p) @@ -1249,7 +1247,7 @@ class TestBackend(object): def test_new_struct_containing_array_varsize(self): py.test.skip("later?") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int len; short data[]; };") p = ffi.new("struct foo_s *", 10) # a single integer is the length assert p.len == 0 @@ -1257,7 +1255,7 @@ class TestBackend(object): py.test.raises(IndexError, "p.data[10]") def test_ffi_typeof_getcname(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) assert ffi.getctype("int") == "int" assert ffi.getctype("int", 'x') == "int x" assert ffi.getctype("int*") == "int *" @@ -1275,7 +1273,7 @@ class TestBackend(object): assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]" def test_array_of_func_ptr(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) f = ffi.cast("int(*)(int)", 42) assert f != ffi.NULL py.test.raises(CDefError, ffi.cast, "int(int)", 42) @@ -1296,7 +1294,7 @@ class TestBackend(object): def test_callback_as_function_argument(self): # In C, function arguments can be declared with a function type, # which is automatically replaced with the ptr-to-function type. - ffi = FFI() + ffi = FFI(backend=self.Backend()) def cb(a, b): return chr(ord(a) + ord(b)).encode() f = ffi.callback("char cb(char, char)", cb) @@ -1308,7 +1306,7 @@ class TestBackend(object): def test_vararg_callback(self): py.test.skip("callback with '...'") - ffi = FFI() + ffi = FFI(backend=self.Backend()) def cb(i, va_list): j = ffi.va_arg(va_list, "int") k = ffi.va_arg(va_list, "long long") @@ -1318,7 +1316,7 @@ class TestBackend(object): assert res == 20 + 300 + 5000 def test_callback_decorator(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) # @ffi.callback("long(long, long)", error=42) def cb(a, b): @@ -1329,8 +1327,8 @@ class TestBackend(object): assert cb((1 << (sz*8-1)) - 1, -10) == 42 def test_unique_types(self): - ffi1 = FFI() - ffi2 = FFI() + ffi1 = FFI(backend=self.Backend()) + ffi2 = FFI(backend=self.Backend()) 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*") @@ -1349,7 +1347,7 @@ class TestBackend(object): assert ffi1.typeof("struct foo*") is ffi1.typeof("struct foo *") def test_anonymous_enum(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("typedef enum { Value0 = 0 } e, *pe;\n" "typedef enum { Value1 = 1 } e1;") assert ffi.getctype("e*") == 'e *' @@ -1357,7 +1355,7 @@ class TestBackend(object): assert ffi.getctype("e1*") == 'e1 *' def test_opaque_enum(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("enum foo;") from cffi import __version_info__ if __version_info__ < (1, 8): @@ -1368,20 +1366,20 @@ class TestBackend(object): "which integer type it is meant to be (unsigned/signed, int/long)") def test_new_ctype(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int *") py.test.raises(TypeError, ffi.new, p) p = ffi.new(ffi.typeof("int *"), 42) assert p[0] == 42 def test_enum_with_non_injective_mapping(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("enum e { AA=0, BB=0, CC=0, DD=0 };") e = ffi.cast("enum e", 0) assert ffi.string(e) == "AA" # pick the first one arbitrarily def test_enum_refer_previous_enum_value(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("enum e { AA, BB=2, CC=4, DD=BB, EE, FF=CC, GG=FF };") assert ffi.string(ffi.cast("enum e", 2)) == "BB" assert ffi.string(ffi.cast("enum e", 3)) == "EE" @@ -1391,7 +1389,7 @@ class TestBackend(object): assert ffi.sizeof("char[GG]") == 4 def test_nested_anonymous_struct(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" struct foo_s { struct { int a, b; }; @@ -1418,7 +1416,7 @@ class TestBackend(object): assert p.d == 14 def test_nested_field_offset_align(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" struct foo_s { struct { int a; char b; }; @@ -1429,7 +1427,7 @@ class TestBackend(object): assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT def test_nested_anonymous_union(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" union foo_u { struct { int a, b; }; @@ -1465,14 +1463,14 @@ class TestBackend(object): # to give both 'a' and 'b' def test_cast_to_array_type(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int[4]", [-5]) q = ffi.cast("int[3]", p) assert q[0] == -5 assert repr(q).startswith("<cdata 'int[3]' 0x") def test_gc(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int *", 123) seen = [] def destructor(p1): @@ -1487,7 +1485,7 @@ class TestBackend(object): assert seen == [1] def test_gc_2(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int *", 123) seen = [] q1 = ffi.gc(p, lambda p: seen.append(1)) @@ -1499,7 +1497,7 @@ class TestBackend(object): assert seen == [2, 1] def test_gc_3(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int *", 123) r = ffi.new("int *", 123) seen = [] @@ -1518,7 +1516,7 @@ class TestBackend(object): assert seen_r == [5, 4] def test_gc_4(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int *", 123) seen = [] q1 = ffi.gc(p, lambda p: seen.append(1)) @@ -1531,7 +1529,7 @@ class TestBackend(object): assert seen == [3] def test_gc_disable(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int *", 123) py.test.raises(TypeError, ffi.gc, p, None) seen = [] @@ -1545,7 +1543,7 @@ class TestBackend(object): assert seen == [2] def test_gc_finite_list(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) p = ffi.new("int *", 123) keepalive = [] for i in range(10): @@ -1556,7 +1554,7 @@ class TestBackend(object): keepalive.append(ffi.gc(p, lambda p: None)) def test_CData_CType(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) assert isinstance(ffi.cast("int", 0), ffi.CData) assert isinstance(ffi.new("int *"), ffi.CData) assert not isinstance(ffi.typeof("int"), ffi.CData) @@ -1564,11 +1562,11 @@ class TestBackend(object): assert not isinstance(ffi.new("int *"), ffi.CType) def test_CData_CType_2(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) assert isinstance(ffi.typeof("int"), ffi.CType) def test_bool(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) assert int(ffi.cast("_Bool", 0.1)) == 1 assert int(ffi.cast("_Bool", -0.0)) == 0 assert int(ffi.cast("_Bool", b'\x02')) == 1 @@ -1580,11 +1578,11 @@ class TestBackend(object): py.test.raises(TypeError, ffi.string, ffi.cast("_Bool", 2)) def test_use_own_bool(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("""typedef int bool;""") def test_ordering_bug1(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" struct foo_s { struct bar_s *p; @@ -1599,7 +1597,7 @@ class TestBackend(object): assert q.p.foo.p == ffi.NULL def test_ordering_bug2(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" struct bar_s; @@ -1614,7 +1612,7 @@ class TestBackend(object): q = ffi.new("struct foo_s *") def test_addressof(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int x, y; };") p = ffi.new("struct foo_s *") a = ffi.addressof(p[0]) @@ -1625,7 +1623,7 @@ class TestBackend(object): py.test.raises(TypeError, ffi.addressof, ffi.cast("int", 5)) def test_addressof_field(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int x, y; };") p = ffi.new("struct foo_s *") a = ffi.addressof(p[0], 'y') @@ -1636,7 +1634,7 @@ class TestBackend(object): assert a != ffi.addressof(p, 'x') def test_addressof_field_nested(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int x, y; };" "struct bar_s { struct foo_s a, b; };") p = ffi.new("struct bar_s *") @@ -1706,38 +1704,43 @@ class TestBackend(object): assert foo2.z == 30 def test_missing_include(self): - ffi1 = FFI() - ffi2 = FFI() + backend = self.Backend() + ffi1 = FFI(backend=backend) + ffi2 = FFI(backend=backend) ffi1.cdef("typedef signed char schar_t;") py.test.raises(CDefError, ffi2.cast, "schar_t", 142) def test_include_typedef(self): - ffi1 = FFI() - ffi2 = FFI() + backend = self.Backend() + ffi1 = FFI(backend=backend) + ffi2 = FFI(backend=backend) ffi1.cdef("typedef signed char schar_t;") ffi2.include(ffi1) p = ffi2.cast("schar_t", 142) assert int(p) == 142 - 256 def test_include_struct(self): - ffi1 = FFI() - ffi2 = FFI() + backend = self.Backend() + ffi1 = FFI(backend=backend) + ffi2 = FFI(backend=backend) ffi1.cdef("struct foo { int x; };") ffi2.include(ffi1) p = ffi2.new("struct foo *", [142]) assert p.x == 142 def test_include_union(self): - ffi1 = FFI() - ffi2 = FFI() + backend = self.Backend() + ffi1 = FFI(backend=backend) + ffi2 = FFI(backend=backend) ffi1.cdef("union foo { int x; };") ffi2.include(ffi1) p = ffi2.new("union foo *", [142]) assert p.x == 142 def test_include_enum(self): - ffi1 = FFI() - ffi2 = FFI() + backend = self.Backend() + ffi1 = FFI(backend=backend) + ffi2 = FFI(backend=backend) ffi1.cdef("enum foo { FA, FB, FC };") ffi2.include(ffi1) p = ffi2.cast("enum foo", 1) @@ -1745,21 +1748,22 @@ class TestBackend(object): assert ffi2.sizeof("char[FC]") == 2 def test_include_typedef_2(self): - ffi1 = FFI() - ffi2 = FFI() + backend = self.Backend() + ffi1 = FFI(backend=backend) + ffi2 = FFI(backend=backend) ffi1.cdef("typedef struct { int x; } *foo_p;") ffi2.include(ffi1) p = ffi2.new("foo_p", [142]) assert p.x == 142 def test_ignore_multiple_declarations_of_constant(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("#define FOO 42") ffi.cdef("#define FOO 42") py.test.raises(FFIError, ffi.cdef, "#define FOO 43") def test_struct_packed(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct nonpacked { char a; int b; };") ffi.cdef("struct is_packed { char a; int b; };", packed=True) assert ffi.sizeof("struct nonpacked") == 8 @@ -1777,7 +1781,7 @@ class TestBackend(object): assert s[1].a == b'Y' def test_define_integer_constant(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" #define DOT_0 0 #define DOT 100 @@ -1800,14 +1804,14 @@ class TestBackend(object): # Issue #193: if we use a struct between the first cdef() where it is # declared and another cdef() where its fields are defined, then the # definition was ignored. - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s;") py.test.raises(TypeError, ffi.new, "struct foo_s *") ffi.cdef("struct foo_s { int x; };") ffi.new("struct foo_s *") def test_ffi_self_include(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) py.test.raises(ValueError, ffi.include, ffi) def test_anonymous_enum_include(self): @@ -1860,5 +1864,5 @@ class TestBackend(object): def test_sizeof_struct_directly(self): # only works with the Python FFI instances - ffi = FFI() + ffi = FFI(backend=self.Backend()) assert ffi.sizeof("struct{int a;}") == ffi.sizeof("int") diff --git a/testing/cffi0/test_cdata.py b/testing/cffi0/test_cdata.py new file mode 100644 index 0000000..116d0b3 --- /dev/null +++ b/testing/cffi0/test_cdata.py @@ -0,0 +1,39 @@ +import py +from cffi import FFI + +class FakeBackend(object): + + def nonstandard_integer_types(self): + return {} + + def sizeof(self, name): + return 1 + + def load_library(self, path): + return "fake library" + + def new_primitive_type(self, name): + return FakeType("primitive " + name) + + def new_void_type(self): + return FakeType("void") + def new_pointer_type(self, x): + return FakeType('ptr-to-%r' % (x,)) + def new_array_type(self, x, y): + return FakeType('array-from-%r-len-%r' % (x, y)) + def cast(self, x, y): + return 'casted!' + def _get_types(self): + return "CData", "CType" + + +class FakeType(object): + def __init__(self, cdecl): + self.cdecl = cdecl + + +def test_typeof(): + ffi = FFI(backend=FakeBackend()) + clong = ffi.typeof("signed long int") + assert isinstance(clong, FakeType) + assert clong.cdecl == 'primitive long' diff --git a/testing/cffi0/test_ctypes.py b/testing/cffi0/test_ctypes.py new file mode 100644 index 0000000..1b88473 --- /dev/null +++ b/testing/cffi0/test_ctypes.py @@ -0,0 +1,40 @@ +import py, sys +from testing.cffi0 import backend_tests +from cffi.backend_ctypes import CTypesBackend + + +class TestCTypes(backend_tests.BackendTests): + # for individual tests see + # ====> backend_tests.py + + Backend = CTypesBackend + TypeRepr = "<class 'ffi.CData<%s>'>" + + def test_array_of_func_ptr(self): + py.test.skip("ctypes backend: not supported: " + "initializers for function pointers") + + def test_structptr_argument(self): + py.test.skip("ctypes backend: not supported: passing a list " + "for a pointer argument") + + def test_array_argument_as_list(self): + py.test.skip("ctypes backend: not supported: passing a list " + "for a pointer argument") + + def test_cast_to_array_type(self): + py.test.skip("ctypes backend: not supported: casting to array") + + def test_nested_anonymous_struct(self): + py.test.skip("ctypes backend: not supported: nested anonymous struct") + + def test_nested_field_offset_align(self): + py.test.skip("ctypes backend: not supported: nested anonymous struct") + + def test_nested_anonymous_union(self): + py.test.skip("ctypes backend: not supported: nested anonymous union") + + def test_CData_CType_2(self): + if sys.version_info >= (3,): + py.test.skip("ctypes backend: not supported in Python 3: CType") + backend_tests.BackendTests.test_CData_CType_2(self) diff --git a/testing/cffi0/test_ffi_backend.py b/testing/cffi0/test_ffi_backend.py index 5a34876..0d25a0d 100644 --- a/testing/cffi0/test_ffi_backend.py +++ b/testing/cffi0/test_ffi_backend.py @@ -1,12 +1,21 @@ import py, sys, platform import pytest +from testing.cffi0 import backend_tests, test_function, test_ownlib from cffi import FFI +import _cffi_backend -class TestFFI(object): +class TestFFI(backend_tests.BackendTests, + test_function.TestFunction, + test_ownlib.TestOwnLib): + TypeRepr = "<ctype '%s'>" + + @staticmethod + def Backend(): + return _cffi_backend def test_not_supported_bitfield_in_result(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int a,b,c,d,e; int x:1; };") e = py.test.raises(NotImplementedError, ffi.callback, "struct foo_s foo(void)", lambda: 42) @@ -14,14 +23,14 @@ class TestFFI(object): "callback with unsupported argument or return type or with '...'") def test_inspecttype(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) assert ffi.typeof("long").kind == "primitive" assert ffi.typeof("long(*)(long, long**, ...)").cname == ( "long(*)(long, long * *, ...)") assert ffi.typeof("long(*)(long, long**, ...)").ellipsis is True def test_new_handle(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) o = [2, 3, 4] p = ffi.new_handle(o) assert ffi.typeof(p) == ffi.typeof("void *") @@ -30,7 +39,7 @@ class TestFFI(object): py.test.raises(RuntimeError, ffi.from_handle, ffi.NULL) def test_callback_onerror(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) seen = [] def oops(*args): seen.append(args) @@ -49,7 +58,7 @@ class TestFFI(object): assert tb.tb_frame.f_locals['n'] == 234 def test_ffi_new_allocator_2(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) seen = [] def myalloc(size): seen.append(size) @@ -81,7 +90,7 @@ class TestFFI(object): assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>" def test_ffi_new_allocator_3(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) seen = [] def myalloc(size): seen.append(size) @@ -94,7 +103,7 @@ class TestFFI(object): assert p1[5] == 0 def test_ffi_new_allocator_4(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None) # def myalloc2(size): diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py index c5bc3d1..0150703 100644 --- a/testing/cffi0/test_function.py +++ b/testing/cffi0/test_function.py @@ -2,6 +2,7 @@ import py from cffi import FFI, CDefError import math, os, sys import ctypes.util +from cffi.backend_ctypes import CTypesBackend from testing.udir import udir from testing.support import FdWriteCapture @@ -19,9 +20,10 @@ if sys.platform == 'win32': lib_m = 'msvcrt' class TestFunction(object): + Backend = CTypesBackend def test_sin(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" double sin(double x); """) @@ -32,7 +34,7 @@ class TestFunction(object): def test_sinf(self): if sys.platform == 'win32': py.test.skip("no sinf found in the Windows stdlib") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" float sinf(float x); """) @@ -44,7 +46,7 @@ class TestFunction(object): def test_sin_no_return_value(self): # check that 'void'-returning functions work too - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" void sin(double x); """) @@ -56,7 +58,7 @@ class TestFunction(object): path = ctypes.util.find_library(lib_m) if not path: py.test.skip("%s not found" % lib_m) - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" double cos(double x); """) @@ -69,7 +71,7 @@ class TestFunction(object): assert x == math.cos(1.23) def test_dlopen_flags(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" double cos(double x); """) @@ -78,7 +80,7 @@ class TestFunction(object): assert x == math.cos(1.23) def test_dlopen_constant(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" #define FOOBAR 42 static const float baz = 42.5; /* not visible */ @@ -91,7 +93,9 @@ class TestFunction(object): def test_tlsalloc(self): if sys.platform != 'win32': py.test.skip("win32 only") - ffi = FFI() + if self.Backend is CTypesBackend: + py.test.skip("ctypes complains on wrong calling conv") + ffi = FFI(backend=self.Backend()) ffi.cdef("long TlsAlloc(void); int TlsFree(long);") lib = ffi.dlopen('KERNEL32.DLL') x = lib.TlsAlloc() @@ -102,7 +106,7 @@ class TestFunction(object): def test_fputs(self): if not sys.platform.startswith('linux'): py.test.skip("probably no symbol 'stderr' in the lib") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fputs(const char *, void *); void *stderr; @@ -118,7 +122,7 @@ class TestFunction(object): def test_fputs_without_const(self): if not sys.platform.startswith('linux'): py.test.skip("probably no symbol 'stderr' in the lib") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fputs(char *, void *); void *stderr; @@ -134,7 +138,7 @@ class TestFunction(object): def test_vararg(self): if not sys.platform.startswith('linux'): py.test.skip("probably no symbol 'stderr' in the lib") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fprintf(void *, const char *format, ...); void *stderr; @@ -161,7 +165,7 @@ class TestFunction(object): b"hello (nil)\n") def test_must_specify_type_of_vararg(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int printf(const char *format, ...); """) @@ -171,17 +175,18 @@ class TestFunction(object): "needs to be a cdata object (got int)") def test_function_has_a_c_type(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int puts(const char *); """) ffi.C = ffi.dlopen(None) fptr = ffi.C.puts assert ffi.typeof(fptr) == ffi.typeof("int(*)(const char*)") - assert repr(fptr).startswith("<cdata 'int(*)(char *)' 0x") + if self.Backend is CTypesBackend: + assert repr(fptr).startswith("<cdata 'int puts(char *)' 0x") def test_function_pointer(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) def cb(charp): assert repr(charp).startswith("<cdata 'char *' 0x") return 42 @@ -207,7 +212,7 @@ class TestFunction(object): assert res == b'world\n' def test_callback_returning_void(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) for returnvalue in [None, 42]: def cb(): return returnvalue @@ -226,7 +231,7 @@ class TestFunction(object): assert "None" in printed def test_passing_array(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int strlen(char[]); """) @@ -238,7 +243,7 @@ class TestFunction(object): def test_write_variable(self): if not sys.platform.startswith('linux'): py.test.skip("probably no symbol 'stdout' in the lib") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" void *stdout; """) @@ -250,7 +255,7 @@ class TestFunction(object): assert C.stdout == pout def test_strchr(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" char *strchr(const char *s, int c); """) @@ -262,7 +267,10 @@ class TestFunction(object): def test_function_with_struct_argument(self): if sys.platform == 'win32': py.test.skip("no 'inet_ntoa'") - ffi = FFI() + if (self.Backend is CTypesBackend and + '__pypy__' in sys.builtin_module_names): + py.test.skip("ctypes limitation on pypy") + ffi = FFI(backend=self.Backend()) ffi.cdef(""" struct in_addr { unsigned int s_addr; }; char *inet_ntoa(struct in_addr in); @@ -273,7 +281,7 @@ class TestFunction(object): assert ffi.string(a) == b'4.4.4.4' def test_function_typedef(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef double func_t(double); func_t sin; @@ -283,8 +291,10 @@ class TestFunction(object): assert x == math.sin(1.23) def test_fputs_custom_FILE(self): + if self.Backend is CTypesBackend: + py.test.skip("FILE not supported with the ctypes backend") filename = str(udir.join('fputs_custom_FILE')) - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("int fputs(const char *, FILE *);") C = ffi.dlopen(None) with open(filename, 'wb') as f: @@ -298,7 +308,7 @@ class TestFunction(object): assert res == b'[hello from custom file][some more output]' def test_constants_on_lib(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("""enum foo_e { AA, BB, CC=5, DD }; typedef enum { EE=-5, FF } some_enum_t;""") lib = ffi.dlopen(None) @@ -310,28 +320,32 @@ class TestFunction(object): assert lib.FF == -4 def test_void_star_accepts_string(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("""int strlen(const void *);""") lib = ffi.dlopen(None) res = lib.strlen(b"hello") assert res == 5 def test_signed_char_star_accepts_string(self): - ffi = FFI() + if self.Backend is CTypesBackend: + py.test.skip("not supported by the ctypes backend") + ffi = FFI(backend=self.Backend()) ffi.cdef("""int strlen(signed char *);""") lib = ffi.dlopen(None) res = lib.strlen(b"hello") assert res == 5 def test_unsigned_char_star_accepts_string(self): - ffi = FFI() + if self.Backend is CTypesBackend: + py.test.skip("not supported by the ctypes backend") + ffi = FFI(backend=self.Backend()) ffi.cdef("""int strlen(unsigned char *);""") lib = ffi.dlopen(None) res = lib.strlen(b"hello") assert res == 5 def test_missing_function(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int nonexistent(); """) @@ -340,7 +354,7 @@ class TestFunction(object): def test_wraps_from_stdlib(self): import functools - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" double sin(double x); """) @@ -355,6 +369,8 @@ class TestFunction(object): assert x == math.sin(1.23) + 100 def test_free_callback_cycle(self): + if self.Backend is CTypesBackend: + py.test.skip("seems to fail with the ctypes backend on windows") import weakref def make_callback(data): container = [data] @@ -365,7 +381,7 @@ class TestFunction(object): class Data(object): pass - ffi = FFI() + ffi = FFI(backend=self.Backend()) data = Data() callback = make_callback(data) wr = weakref.ref(data) @@ -378,7 +394,9 @@ class TestFunction(object): def test_windows_stdcall(self): if sys.platform != 'win32': py.test.skip("Windows-only test") - ffi = FFI() + if self.Backend is CTypesBackend: + py.test.skip("not with the ctypes backend") + ffi = FFI(backend=self.Backend()) ffi.cdef(""" BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency); """) @@ -391,9 +409,11 @@ class TestFunction(object): def test_explicit_cdecl_stdcall(self): if sys.platform != 'win32': py.test.skip("Windows-only test") + if self.Backend is CTypesBackend: + py.test.skip("not with the ctypes backend") win64 = (sys.maxsize > 2**32) # - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency); """) @@ -401,7 +421,7 @@ class TestFunction(object): tp = ffi.typeof(m.QueryPerformanceFrequency) assert str(tp) == "<ctype 'int(*)(long long *)'>" # - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" BOOL __cdecl QueryPerformanceFrequency(LONGLONG *lpFrequency); """) @@ -409,7 +429,7 @@ class TestFunction(object): tpc = ffi.typeof(m.QueryPerformanceFrequency) assert tpc is tp # - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" BOOL WINAPI QueryPerformanceFrequency(LONGLONG *lpFrequency); """) @@ -421,7 +441,7 @@ class TestFunction(object): assert tps is not tpc assert str(tps) == "<ctype 'int(__stdcall *)(long long *)'>" # - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("typedef int (__cdecl *fnc_t)(int);") ffi.cdef("typedef int (__stdcall *fns_t)(int);") tpc = ffi.typeof("fnc_t") @@ -441,10 +461,11 @@ class TestFunction(object): ffi.new("fns_t[]", [fns]) def test_stdcall_only_on_windows(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef("double __stdcall sin(double x);") # stdcall ignored m = ffi.dlopen(lib_m) - if sys.platform == 'win32' and sys.maxsize < 2**32: + if (sys.platform == 'win32' and sys.maxsize < 2**32 and + self.Backend is not CTypesBackend): assert "double(__stdcall *)(double)" in str(ffi.typeof(m.sin)) else: assert "double(*)(double)" in str(ffi.typeof(m.sin)) @@ -452,7 +473,7 @@ class TestFunction(object): assert x == math.sin(1.23) def test_dir_on_dlopen_lib(self): - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef enum { MYE1, MYE2 } myenum_t; double myfunc(double); diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py index aa85ad6..afda543 100644 --- a/testing/cffi0/test_ownlib.py +++ b/testing/cffi0/test_ownlib.py @@ -1,6 +1,7 @@ import py, sys import subprocess, weakref from cffi import FFI +from cffi.backend_ctypes import CTypesBackend SOURCE = """\ @@ -94,6 +95,7 @@ EXPORT int my_array[7] = {0, 1, 2, 3, 4, 5, 6}; """ class TestOwnLib(object): + Backend = CTypesBackend def setup_class(cls): cls.module = None @@ -136,7 +138,7 @@ class TestOwnLib(object): py.test.skip("fix the auto-generation of the tiny test lib") if sys.platform == 'win32': py.test.skip("fails, errno at multiple addresses") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int test_getting_errno(void); """) @@ -150,7 +152,9 @@ class TestOwnLib(object): py.test.skip("fix the auto-generation of the tiny test lib") if sys.platform == 'win32': py.test.skip("fails, errno at multiple addresses") - ffi = FFI() + if self.Backend is CTypesBackend and '__pypy__' in sys.modules: + py.test.skip("XXX errno issue with ctypes on pypy?") + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int test_setting_errno(void); """) @@ -163,7 +167,7 @@ class TestOwnLib(object): def test_my_array_7(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int my_array[7]; """) @@ -171,6 +175,8 @@ class TestOwnLib(object): for i in range(7): assert ownlib.my_array[i] == i assert len(ownlib.my_array) == 7 + if self.Backend is CTypesBackend: + py.test.skip("not supported by the ctypes backend") ownlib.my_array = list(range(10, 17)) for i in range(7): assert ownlib.my_array[i] == 10 + i @@ -181,7 +187,9 @@ class TestOwnLib(object): def test_my_array_no_length(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") - ffi = FFI() + if self.Backend is CTypesBackend: + py.test.skip("not supported by the ctypes backend") + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int my_array[]; """) @@ -199,7 +207,7 @@ class TestOwnLib(object): def test_keepalive_lib(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int test_getting_errno(void); """) @@ -217,7 +225,7 @@ class TestOwnLib(object): def test_keepalive_ffi(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" int test_getting_errno(void); """) @@ -237,7 +245,7 @@ class TestOwnLib(object): def test_struct_by_value(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") - ffi = FFI() + ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef struct { long x; diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py index 9a8d671..73eb5c3 100644 --- a/testing/cffi0/test_verify.py +++ b/testing/cffi0/test_verify.py @@ -920,6 +920,15 @@ def test_access_callback_function_typedef(): lib.cb = my_callback assert lib.foo(4) == 887 +def test_ctypes_backend_forces_generic_engine(): + from cffi.backend_ctypes import CTypesBackend + ffi = FFI(backend=CTypesBackend()) + ffi.cdef("int func(int a);") + lib = ffi.verify("int func(int a) { return a * 42; }") + assert not hasattr(lib, '_cffi_python_module') + assert hasattr(lib, '_cffi_generic_module') + assert lib.func(100) == 4200 + def test_call_with_struct_ptr(): ffi = FFI() ffi.cdef("typedef struct { int x; ...; } foo_t; int foo(foo_t *);") |