diff options
author | Armin Rigo <arigo@tunes.org> | 2014-03-07 07:52:39 +0100 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2014-03-07 07:52:39 +0100 |
commit | 9fc0398217b8738c01646c8182d171edb55d740b (patch) | |
tree | b186bff5a02dd5f44e1a82c85c1354b0715df92d | |
parent | 11d5c2fa7cce0fbfdb874aa8b462d6102e664b02 (diff) | |
parent | 5e77026a2d4224ab94b7fc0ed37b9823d34a9a60 (diff) | |
download | cffi-9fc0398217b8738c01646c8182d171edb55d740b.tar.gz |
Merge for release 0.8.2
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | c/_cffi_backend.c | 70 | ||||
-rw-r--r-- | c/minibuffer.h | 12 | ||||
-rw-r--r-- | c/test_c.py | 134 | ||||
-rw-r--r-- | cffi/__init__.py | 4 | ||||
-rw-r--r-- | cffi/api.py | 27 | ||||
-rw-r--r-- | cffi/backend_ctypes.py | 4 | ||||
-rw-r--r-- | cffi/cparser.py | 7 | ||||
-rw-r--r-- | cffi/model.py | 42 | ||||
-rw-r--r-- | cffi/vengine_cpy.py | 48 | ||||
-rw-r--r-- | doc/source/conf.py | 2 | ||||
-rw-r--r-- | doc/source/index.rst | 44 | ||||
-rw-r--r-- | setup.py | 86 | ||||
-rw-r--r-- | testing/backend_tests.py | 21 | ||||
-rw-r--r-- | testing/test_function.py | 48 | ||||
-rw-r--r-- | testing/test_parsing.py | 21 | ||||
-rw-r--r-- | testing/test_unicode_literals.py | 9 | ||||
-rw-r--r-- | testing/test_verify.py | 71 | ||||
-rw-r--r-- | testing/test_version.py | 4 | ||||
-rw-r--r-- | testing/test_zdistutils.py | 40 |
20 files changed, 462 insertions, 234 deletions
@@ -7,4 +7,4 @@ verify() handles "typedef ... some_integer_type", but this creates an opaque type that works like a struct (so we can't get the value out of it). -_cffi backend for PyPy +accept and kill "static inline" in the cdefs diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index a91ef2b..a3a2515 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -13,6 +13,9 @@ #include <errno.h> #include <ffi.h> #include <sys/mman.h> +#if (defined (__SVR4) && defined (__sun)) || defined(_AIX) +# include <alloca.h> +#endif #endif #include "malloc_closure.h" @@ -832,7 +835,7 @@ convert_to_object(char *data, CTypeDescrObject *ct) return new_simple_cdata(ptrdata, ct); } else if (ct->ct_flags & CT_IS_OPAQUE) { - PyErr_Format(PyExc_TypeError, "cannot return a cdata '%s'", + PyErr_Format(PyExc_TypeError, "cdata '%s' is opaque", ct->ct_name); return NULL; } @@ -3580,9 +3583,40 @@ _add_field(PyObject *interned_fields, PyObject *fname, CTypeDescrObject *ftype, return cf; /* borrowed reference */ } -#define SF_MSVC_BITFIELDS 1 -#define SF_GCC_ARM_BITFIELDS 2 -#define SF_GCC_BIG_ENDIAN 4 +#define SF_MSVC_BITFIELDS 0x01 +#define SF_GCC_ARM_BITFIELDS 0x02 +#define SF_GCC_X86_BITFIELDS 0x10 + +#define SF_GCC_BIG_ENDIAN 0x04 +#define SF_GCC_LITTLE_ENDIAN 0x40 + +#define SF_PACKED 0x08 + +static int complete_sflags(int sflags) +{ + /* add one of the SF_xxx_BITFIELDS flags if none is specified */ + if (!(sflags & (SF_MSVC_BITFIELDS | SF_GCC_ARM_BITFIELDS | + SF_GCC_X86_BITFIELDS))) { +#ifdef MS_WIN32 + sflags |= SF_MSVC_BITFIELDS; +#else +# ifdef __arm__ + sflags |= SF_GCC_ARM_BITFIELDS; +# else + sflags |= SF_GCC_X86_BITFIELDS; +# endif +#endif + } + /* add one of SF_GCC_xx_ENDIAN if none is specified */ + if (!(sflags & (SF_GCC_BIG_ENDIAN | SF_GCC_LITTLE_ENDIAN))) { + int _check_endian = 1; + if (*(char *)&_check_endian == 0) + sflags |= SF_GCC_BIG_ENDIAN; + else + sflags |= SF_GCC_LITTLE_ENDIAN; + } + return sflags; +} static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) { @@ -3594,18 +3628,7 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) int totalalignment = -1; CFieldObject **previous; int prev_bitfield_size, prev_bitfield_free; -#ifdef MS_WIN32 - int sflags = SF_MSVC_BITFIELDS; -#else -# ifdef __arm__ - int sflags = SF_GCC_ARM_BITFIELDS; -# else int sflags = 0; -# endif - int _check_endian = 1; - if (*(char *)&_check_endian == 0) - sflags |= SF_GCC_BIG_ENDIAN; -#endif if (!PyArg_ParseTuple(args, "O!O!|Onii:complete_struct_or_union", &CTypeDescr_Type, &ct, @@ -3613,6 +3636,8 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) &ignored, &totalsize, &totalalignment, &sflags)) return NULL; + sflags = complete_sflags(sflags); + if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) == (CT_STRUCT|CT_IS_OPAQUE)) { is_union = 0; @@ -3668,8 +3693,8 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) boffset = 0; /* reset each field at offset 0 */ /* update the total alignment requirement, but skip it if the - field is an anonymous bitfield */ - falign = get_alignment(ftype); + field is an anonymous bitfield or if SF_PACKED */ + falign = (sflags & SF_PACKED) ? 1 : get_alignment(ftype); if (falign < 0) goto error; @@ -3781,6 +3806,7 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) PyErr_Format(PyExc_TypeError, "field '%s.%s' is declared with :0", ct->ct_name, PyText_AS_UTF8(fname)); + goto error; } if (!(sflags & SF_MSVC_BITFIELDS)) { /* GCC's notion of "ftype :0;" */ @@ -3812,6 +3838,14 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) if (bits_already_occupied + fbitsize > 8 * ftype->ct_size) { /* it would not fit, we need to start at the next allowed position */ + if ((sflags & SF_PACKED) && + (bits_already_occupied & 7)) { + PyErr_Format(PyExc_NotImplementedError, + "with 'packed', gcc would compile field " + "'%s.%s' to reuse some bits in the previous " + "field", ct->ct_name, PyText_AS_UTF8(fname)); + goto error; + } field_offset_bytes += falign; assert(boffset < field_offset_bytes * 8); boffset = field_offset_bytes * 8; @@ -5448,7 +5482,7 @@ init_cffi_backend(void) if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0) INITERROR; - v = PyText_FromString("0.8"); + v = PyText_FromString("0.8.2"); if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0) INITERROR; diff --git a/c/minibuffer.h b/c/minibuffer.h index 3129032..289dc4a 100644 --- a/c/minibuffer.h +++ b/c/minibuffer.h @@ -93,6 +93,14 @@ static Py_ssize_t mb_getsegcount(MiniBufferObj *self, Py_ssize_t *lenp) *lenp = self->mb_size; return 1; } + +static PyObject *mb_str(MiniBufferObj *self) +{ + /* Python 2: we want str(buffer) to behave like buffer[:], because + that's what bytes(buffer) does on Python 3 and there is no way + we can prevent this. */ + return PyString_FromStringAndSize(self->mb_data, self->mb_size); +} #endif static int mb_getbuf(MiniBufferObj *self, Py_buffer *view, int flags) @@ -249,7 +257,11 @@ static PyTypeObject MiniBuffer_Type = { #endif 0, /* tp_hash */ 0, /* tp_call */ +#if PY_MAJOR_VERSION < 3 + (reprfunc)mb_str, /* tp_str */ +#else 0, /* tp_str */ +#endif PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ &mb_as_buffer, /* tp_as_buffer */ diff --git a/c/test_c.py b/c/test_c.py index ab0b72b..1864391 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -370,6 +370,9 @@ def test_load_standard_library(): assert x.load_function(BVoidP, 'strcpy') py.test.raises(KeyError, x.load_function, BVoidP, 'xxx_this_function_does_not_exist') + # the next one is from 'libm', not 'libc', but we assume + # that it is already loaded too, so it should work + assert x.load_function(BVoidP, 'sqrt') def test_hash_differences(): BChar = new_primitive_type("char") @@ -1429,8 +1432,10 @@ def test_enum_in_struct(): p = newp(BStructPtr, [12]) assert p.a1 == 12 e = py.test.raises(TypeError, newp, BStructPtr, [None]) - assert ("an integer is required" in str(e.value) or - "unsupported operand type for int(): 'NoneType'" in str(e.value)) #PyPy + msg = str(e.value) + assert ("an integer is required" in msg or # CPython + "unsupported operand type for int(): 'NoneType'" in msg or # old PyPys + "expected integer, got NoneType object" in msg) # newer PyPys py.test.raises(TypeError, 'p.a1 = "def"') if sys.version_info < (3,): BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,), BInt) @@ -2154,7 +2159,13 @@ def test_buffer(): c = newp(BCharArray, b"hi there") # buf = buffer(c) - assert str(buf).startswith('<_cffi_backend.buffer object at 0x') + assert repr(buf).startswith('<_cffi_backend.buffer object at 0x') + assert bytes(buf) == b"hi there\x00" + if sys.version_info < (3,): + assert str(buf) == "hi there\x00" + assert unicode(buf) == u+"hi there\x00" + else: + assert str(buf) == repr(buf) # --mb_length-- assert len(buf) == len(b"hi there\x00") # --mb_item-- @@ -2886,7 +2897,7 @@ def _test_bitfield_details(flag): ('b1', BInt, 9), ('b2', BUInt, 7), ('c', BChar, -1)], -1, -1, -1, flag) - if flag % 2 == 0: # gcc, any variant + if not (flag & SF_MSVC_BITFIELDS): # gcc, any variant assert typeoffsetof(BStruct, 'c') == (BChar, 3) assert sizeof(BStruct) == 4 else: # msvc @@ -2901,20 +2912,20 @@ def _test_bitfield_details(flag): p.c = b'\x9D' raw = buffer(p)[:] if sys.byteorder == 'little': - if flag == 0 or flag == 2: # gcc, little endian - assert raw == b'A7\xC7\x9D' - elif flag == 1: # msvc + if flag & SF_MSVC_BITFIELDS: assert raw == b'A\x00\x00\x007\xC7\x00\x00\x9D\x00\x00\x00' - elif flag == 4: # gcc, big endian + elif flag & SF_GCC_LITTLE_ENDIAN: + assert raw == b'A7\xC7\x9D' + elif flag & SF_GCC_BIG_ENDIAN: assert raw == b'A\xE3\x9B\x9D' else: raise AssertionError("bad flag") else: - if flag == 0 or flag == 2: # gcc - assert raw == b'A\xC77\x9D' - elif flag == 1: # msvc + if flag & SF_MSVC_BITFIELDS: assert raw == b'A\x00\x00\x00\x00\x00\xC77\x9D\x00\x00\x00' - elif flag == 4: # gcc, big endian + elif flag & SF_GCC_LITTLE_ENDIAN: + assert raw == b'A\xC77\x9D' + elif flag & SF_GCC_BIG_ENDIAN: assert raw == b'A\x9B\xE3\x9D' else: raise AssertionError("bad flag") @@ -2924,18 +2935,15 @@ def _test_bitfield_details(flag): ('', BShort, 9), ('c', BChar, -1)], -1, -1, -1, flag) assert typeoffsetof(BStruct, 'c') == (BChar, 4) - if flag == 0: # gcc - assert sizeof(BStruct) == 5 - assert alignof(BStruct) == 1 - elif flag == 1: # msvc - assert sizeof(BStruct) == 6 - assert alignof(BStruct) == 2 - elif flag == 2: # gcc ARM + if flag & SF_MSVC_BITFIELDS: assert sizeof(BStruct) == 6 assert alignof(BStruct) == 2 - elif flag == 4: # gcc, big endian + elif flag & SF_GCC_X86_BITFIELDS: assert sizeof(BStruct) == 5 assert alignof(BStruct) == 1 + elif flag & SF_GCC_ARM_BITFIELDS: + assert sizeof(BStruct) == 6 + assert alignof(BStruct) == 2 else: raise AssertionError("bad flag") # @@ -2944,37 +2952,43 @@ def _test_bitfield_details(flag): ('', BInt, 0), ('', BInt, 0), ('c', BChar, -1)], -1, -1, -1, flag) - if flag == 0: # gcc - assert typeoffsetof(BStruct, 'c') == (BChar, 4) - assert sizeof(BStruct) == 5 - assert alignof(BStruct) == 1 - elif flag == 1: # msvc + if flag & SF_MSVC_BITFIELDS: assert typeoffsetof(BStruct, 'c') == (BChar, 1) assert sizeof(BStruct) == 2 assert alignof(BStruct) == 1 - elif flag == 2: # gcc ARM - assert typeoffsetof(BStruct, 'c') == (BChar, 4) - assert sizeof(BStruct) == 8 - assert alignof(BStruct) == 4 - elif flag == 4: # gcc, big endian + elif flag & SF_GCC_X86_BITFIELDS: assert typeoffsetof(BStruct, 'c') == (BChar, 4) assert sizeof(BStruct) == 5 assert alignof(BStruct) == 1 + elif flag & SF_GCC_ARM_BITFIELDS: + assert typeoffsetof(BStruct, 'c') == (BChar, 4) + assert sizeof(BStruct) == 8 + assert alignof(BStruct) == 4 else: raise AssertionError("bad flag") -def test_bitfield_as_gcc(): - _test_bitfield_details(flag=0) +SF_MSVC_BITFIELDS = 0x01 +SF_GCC_ARM_BITFIELDS = 0x02 +SF_GCC_X86_BITFIELDS = 0x10 + +SF_GCC_BIG_ENDIAN = 0x04 +SF_GCC_LITTLE_ENDIAN = 0x40 + +SF_PACKED = 0x08 + +def test_bitfield_as_x86_gcc(): + _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_LITTLE_ENDIAN) def test_bitfield_as_msvc(): - _test_bitfield_details(flag=1) + _test_bitfield_details(flag=SF_MSVC_BITFIELDS|SF_GCC_LITTLE_ENDIAN) def test_bitfield_as_arm_gcc(): - _test_bitfield_details(flag=2) + _test_bitfield_details(flag=SF_GCC_ARM_BITFIELDS|SF_GCC_LITTLE_ENDIAN) -def test_bitfield_as_big_endian(): - _test_bitfield_details(flag=4) +def test_bitfield_as_ppc_gcc(): + # PowerPC uses the same format as X86, but is big-endian + _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_BIG_ENDIAN) def test_struct_array_no_length(): @@ -3136,7 +3150,53 @@ def test_void_p_arithmetic(): py.test.raises(TypeError, "p + cast(new_primitive_type('int'), 42)") py.test.raises(TypeError, "p - cast(new_primitive_type('int'), 42)") +def test_sizeof_sliced_array(): + BInt = new_primitive_type("int") + BArray = new_array_type(new_pointer_type(BInt), 10) + p = newp(BArray, None) + assert sizeof(p[2:9]) == 7 * sizeof(BInt) + +def test_packed(): + BLong = new_primitive_type("long") + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a1', BLong, -1), + ('a2', BChar, -1), + ('a3', BShort, -1)], + None, -1, -1, SF_PACKED) + d = BStruct.fields + assert len(d) == 3 + assert d[0][0] == 'a1' + assert d[0][1].type is BLong + assert d[0][1].offset == 0 + assert d[0][1].bitshift == -1 + assert d[0][1].bitsize == -1 + assert d[1][0] == 'a2' + assert d[1][1].type is BChar + assert d[1][1].offset == sizeof(BLong) + assert d[1][1].bitshift == -1 + assert d[1][1].bitsize == -1 + assert d[2][0] == 'a3' + assert d[2][1].type is BShort + assert d[2][1].offset == sizeof(BLong) + sizeof(BChar) + assert d[2][1].bitshift == -1 + assert d[2][1].bitsize == -1 + assert sizeof(BStruct) == sizeof(BLong) + sizeof(BChar) + sizeof(BShort) + assert alignof(BStruct) == 1 + +def test_packed_with_bitfields(): + if sys.platform == "win32": + py.test.skip("testing gcc behavior") + BLong = new_primitive_type("long") + BChar = new_primitive_type("char") + BStruct = new_struct_type("struct foo") + py.test.raises(NotImplementedError, + complete_struct_or_union, + BStruct, [('a1', BLong, 30), + ('a2', BChar, 5)], + None, -1, -1, SF_PACKED) def test_version(): # this test is here mostly for PyPy - assert __version__ == "0.8" + assert __version__ == "0.8.2" diff --git a/cffi/__init__.py b/cffi/__init__.py index 575d1a1..fa9e86f 100644 --- a/cffi/__init__.py +++ b/cffi/__init__.py @@ -4,5 +4,5 @@ __all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', from .api import FFI, CDefError, FFIError from .ffiplatform import VerificationError, VerificationMissing -__version__ = "0.8.1" -__version_info__ = (0, 8, 1) +__version__ = "0.8.2" +__version_info__ = (0, 8, 2) diff --git a/cffi/api.py b/cffi/api.py index b899e5b..f44f086 100644 --- a/cffi/api.py +++ b/cffi/api.py @@ -1,4 +1,4 @@ -import types +import sys, types from .lock import allocate_lock try: @@ -88,18 +88,20 @@ class FFI(object): self.NULL = self.cast(self.BVoidP, 0) self.CData, self.CType = backend._get_types() - def cdef(self, csource, override=False): + def cdef(self, csource, override=False, packed=False): """Parse the given C source. This registers all declared functions, types, and global variables. The functions and global variables can then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'. The types can be used in 'ffi.new()' and other functions. + If 'packed' is specified as True, all structs declared inside this + cdef are packed, i.e. laid out without any field alignment at all. """ if not isinstance(csource, str): # unicode, on Python 2 if not isinstance(csource, basestring): raise TypeError("cdef() argument must be a string") csource = csource.encode('ascii') with self._lock: - self._parser.parse(csource, override=override) + self._parser.parse(csource, override=override, packed=packed) self._cdefsources.append(csource) if override: for cache in self._function_caches: @@ -387,22 +389,27 @@ class FFI(object): return self._backend.from_handle(x) -def _make_ffi_library(ffi, libname, flags): - import os - name = libname +def _load_backend_lib(backend, name, flags): if name is None: - name = 'c' # on Posix only - backend = ffi._backend + if sys.platform != "win32": + return backend.load_library(None, flags) + name = "c" # Windows: load_library(None) fails, but this works + # (backward compatibility hack only) try: if '.' not in name and '/' not in name: raise OSError("library not found: %r" % (name,)) - backendlib = backend.load_library(name, flags) + return backend.load_library(name, flags) except OSError: import ctypes.util path = ctypes.util.find_library(name) if path is None: raise # propagate the original OSError - backendlib = backend.load_library(path, flags) + return backend.load_library(path, flags) + +def _make_ffi_library(ffi, libname, flags): + import os + backend = ffi._backend + backendlib = _load_backend_lib(backend, libname, flags) copied_enums = [] # def make_accessor_locked(name): diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py index e7cd8a2..2b2b481 100644 --- a/cffi/backend_ctypes.py +++ b/cffi/backend_ctypes.py @@ -720,7 +720,7 @@ class CTypesBackend(object): return self._new_struct_or_union('union', name, ctypes.Union) def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, - totalsize=-1, totalalignment=-1): + 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 " @@ -739,6 +739,8 @@ class CTypesBackend(object): 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 # diff --git a/cffi/cparser.py b/cffi/cparser.py index 73ab903..99998ac 100644 --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -98,6 +98,7 @@ class Parser(object): self._anonymous_counter = 0 self._structnode2type = weakref.WeakKeyDictionary() self._override = False + self._packed = False def _parse(self, csource): csource, macros = _preprocess(csource) @@ -147,13 +148,16 @@ class Parser(object): msg = 'parse error\n%s' % (msg,) raise api.CDefError(msg) - def parse(self, csource, override=False): + def parse(self, csource, override=False, packed=False): prev_override = self._override + prev_packed = self._packed try: self._override = override + self._packed = packed self._internal_parse(csource) finally: self._override = prev_override + self._packed = prev_packed def _internal_parse(self, csource): ast, macros = self._parse(csource) @@ -476,6 +480,7 @@ class Parser(object): if isinstance(tp, model.StructType) and tp.partial: raise NotImplementedError("%s: using both bitfields and '...;'" % (tp,)) + tp.packed = self._packed return tp def _make_partial(self, tp, nested): diff --git a/cffi/model.py b/cffi/model.py index 45609c2..371153f 100644 --- a/cffi/model.py +++ b/cffi/model.py @@ -1,4 +1,6 @@ +import types import weakref + from .lock import allocate_lock @@ -81,29 +83,29 @@ class PrimitiveType(BaseType): 'long': 'i', 'long long': 'i', 'signed char': 'i', - 'unsigned char': 'u', - 'unsigned short': 'u', - 'unsigned int': 'u', - 'unsigned long': 'u', - 'unsigned long long': 'u', + 'unsigned char': 'i', + 'unsigned short': 'i', + 'unsigned int': 'i', + 'unsigned long': 'i', + 'unsigned long long': 'i', 'float': 'f', 'double': 'f', 'long double': 'f', - '_Bool': 'u', + '_Bool': 'i', # the following types are not primitive in the C sense 'wchar_t': 'c', 'int8_t': 'i', - 'uint8_t': 'u', + 'uint8_t': 'i', 'int16_t': 'i', - 'uint16_t': 'u', + 'uint16_t': 'i', 'int32_t': 'i', - 'uint32_t': 'u', + 'uint32_t': 'i', 'int64_t': 'i', - 'uint64_t': 'u', + 'uint64_t': 'i', 'intptr_t': 'i', - 'uintptr_t': 'u', + 'uintptr_t': 'i', 'ptrdiff_t': 'i', - 'size_t': 'u', + 'size_t': 'i', 'ssize_t': 'i', } @@ -114,12 +116,8 @@ class PrimitiveType(BaseType): def is_char_type(self): return self.ALL_PRIMITIVE_TYPES[self.name] == 'c' - def is_signed_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' - def is_unsigned_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'u' def is_integer_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] in 'iu' + return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' def is_float_type(self): return self.ALL_PRIMITIVE_TYPES[self.name] == 'f' @@ -259,6 +257,7 @@ class StructOrUnion(StructOrUnionOrEnum): fixedlayout = None completed = False partial = False + packed = False def __init__(self, name, fldnames, fldtypes, fldbitsize): self.name = name @@ -315,7 +314,11 @@ class StructOrUnion(StructOrUnionOrEnum): fldtypes = [tp.get_cached_btype(ffi, finishlist) for tp in self.fldtypes] lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) - ffi._backend.complete_struct_or_union(BType, lst, self) + sflags = 0 + if self.packed: + sflags = 8 # SF_PACKED + ffi._backend.complete_struct_or_union(BType, lst, self, + -1, -1, sflags) # else: fldtypes = [] @@ -468,8 +471,7 @@ def global_cache(srctype, ffi, funcname, *args, **kwds): # initialize the __typecache attribute, either at the module level # if ffi._backend is a module, or at the class level if ffi._backend # is some instance. - ModuleType = type(weakref) - if isinstance(ffi._backend, ModuleType): + if isinstance(ffi._backend, types.ModuleType): ffi._backend.__typecache = weakref.WeakValueDictionary() else: type(ffi._backend).__typecache = weakref.WeakValueDictionary() diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py index 83e3a55..d9af334 100644 --- a/cffi/vengine_cpy.py +++ b/cffi/vengine_cpy.py @@ -214,10 +214,7 @@ class VCPythonEngine(object): extraarg = '' if isinstance(tp, model.PrimitiveType): if tp.is_integer_type() and tp.name != '_Bool': - if tp.is_signed_type(): - converter = '_cffi_to_c_SIGNED' - else: - converter = '_cffi_to_c_UNSIGNED' + converter = '_cffi_to_c_int' extraarg = ', %s' % tp.name else: converter = '_cffi_to_c_%s' % (tp.name.replace(' ', '_'),) @@ -270,10 +267,7 @@ class VCPythonEngine(object): def _convert_expr_from_c(self, tp, var, context): if isinstance(tp, model.PrimitiveType): if tp.is_integer_type(): - if tp.is_signed_type(): - return '_cffi_from_c_SIGNED(%s, %s)' % (var, tp.name) - else: - return '_cffi_from_c_UNSIGNED(%s, %s)' % (var, tp.name) + return '_cffi_from_c_int(%s, %s)' % (var, tp.name) elif tp.name != 'long double': return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var) else: @@ -801,25 +795,23 @@ typedef unsigned char _Bool; #define _cffi_to_c_double PyFloat_AsDouble #define _cffi_to_c_float PyFloat_AsDouble -#define _cffi_from_c_SIGNED(x, type) \ - (sizeof(type) <= sizeof(long) ? PyInt_FromLong(x) : \ - PyLong_FromLongLong(x)) -#define _cffi_from_c_UNSIGNED(x, type) \ - (sizeof(type) < sizeof(long) ? PyInt_FromLong(x) : \ - sizeof(type) == sizeof(long) ? PyLong_FromUnsignedLong(x) : \ - PyLong_FromUnsignedLongLong(x)) - -#define _cffi_to_c_SIGNED(o, type) \ - (sizeof(type) == 1 ? _cffi_to_c_i8(o) : \ - sizeof(type) == 2 ? _cffi_to_c_i16(o) : \ - sizeof(type) == 4 ? _cffi_to_c_i32(o) : \ - sizeof(type) == 8 ? _cffi_to_c_i64(o) : \ - (Py_FatalError("unsupported size for type " #type), 0)) -#define _cffi_to_c_UNSIGNED(o, type) \ - (sizeof(type) == 1 ? _cffi_to_c_u8(o) : \ - sizeof(type) == 2 ? _cffi_to_c_u16(o) : \ - sizeof(type) == 4 ? _cffi_to_c_u32(o) : \ - sizeof(type) == 8 ? _cffi_to_c_u64(o) : \ +#define _cffi_from_c_int(x, type) \ + (((type)-1) > 0 ? /* unsigned */ \ + (sizeof(type) < sizeof(long) ? PyInt_FromLong(x) : \ + sizeof(type) == sizeof(long) ? PyLong_FromUnsignedLong(x) : \ + PyLong_FromUnsignedLongLong(x)) \ + : (sizeof(type) <= sizeof(long) ? PyInt_FromLong(x) : \ + PyLong_FromLongLong(x))) + +#define _cffi_to_c_int(o, type) \ + (sizeof(type) == 1 ? (((type)-1) > 0 ? _cffi_to_c_u8(o) \ + : _cffi_to_c_i8(o)) : \ + sizeof(type) == 2 ? (((type)-1) > 0 ? _cffi_to_c_u16(o) \ + : _cffi_to_c_i16(o)) : \ + sizeof(type) == 4 ? (((type)-1) > 0 ? _cffi_to_c_u32(o) \ + : _cffi_to_c_i32(o)) : \ + sizeof(type) == 8 ? (((type)-1) > 0 ? _cffi_to_c_u64(o) \ + : _cffi_to_c_i64(o)) : \ (Py_FatalError("unsupported size for type " #type), 0)) #define _cffi_to_c_i8 \ @@ -905,11 +897,13 @@ static void _cffi_init(void) if (c_api_object == NULL) return; if (!PyCapsule_CheckExact(c_api_object)) { + Py_DECREF(c_api_object); PyErr_SetNone(PyExc_ImportError); return; } memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"), _CFFI_NUM_EXPORTS * sizeof(void *)); + Py_DECREF(c_api_object); } #define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num)) diff --git a/doc/source/conf.py b/doc/source/conf.py index 3b091ef..17bcbd3 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -47,7 +47,7 @@ copyright = u'2012, Armin Rigo, Maciej Fijalkowski' # The short X.Y version. version = '0.8' # The full version, including alpha/beta/rc tags. -release = '0.8.1' +release = '0.8.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/index.rst b/doc/source/index.rst index 0dbdbc2..a9d7336 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -85,13 +85,13 @@ Requirements: Download and Installation: -* http://pypi.python.org/packages/source/c/cffi/cffi-0.8.1.tar.gz +* http://pypi.python.org/packages/source/c/cffi/cffi-0.8.2.tar.gz - Or grab the most current version by following the instructions below. - - MD5: 1a877bf113bfe90fdefedbf9e39310d2 + - MD5: ... - - SHA: d46b7cf92956fa01d9f8e0a8d3c7e2005ae40893 + - SHA: ... * Or get it from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` @@ -851,6 +851,14 @@ like ``ffi.new("int[%d]" % x)``. Indeed, this is not recommended: ``ffi`` normally caches the string ``"int[]"`` to not need to re-parse it all the time. +.. versionadded:: 0.9 + The ``ffi.cdef()`` call takes an optional argument ``packed``: if + True, then all structs declared within this cdef are "packed". This + has a meaning similar to ``__attribute__((packed))`` in GCC. It + specifies that all structure fields should have an alignment of one + byte. (Note that the packed attribute has no effect on bit fields so + far, which mean that they may be packed differently than on GCC.) + Python 3 support ---------------- @@ -1172,7 +1180,7 @@ an array.) because these objects' API changes too much across Python versions. Instead it has the following Python API (a subset of ``buffer``): -- ``buf[:]``: fetch a copy as a regular byte string (or +- ``buf[:]`` or ``bytes(buf)``: fetch a copy as a regular byte string (or ``buf[start:end]`` for a part) - ``buf[:] = newstr``: change the original content (or ``buf[start:end] @@ -1187,6 +1195,14 @@ an array.) owned memory will not be freed as long as the buffer is alive. Moreover buffer objects now support weakrefs to them. +.. versionchanged:: 0.9 + Before version 0.9, ``bytes(buf)`` was supported in Python 3 to get + the content of the buffer, but on Python 2 it would return the repr + ``<_cffi_backend.buffer object>``. This has been fixed. But you + should avoid using ``str(buf)``: it now gives inconsistent results + between Python 2 and Python 3 (this is similar to how ``str()`` + gives inconsistent results on regular byte strings). + ``ffi.typeof("C type" or cdata object)``: return an object of type ``<ctype>`` corresponding to the parsed string, or to the C type of the @@ -1257,13 +1273,19 @@ points in time, and using it in a ``with`` statement. ``void *`` that contains an opaque reference to ``python_object``. You can pass it around to C functions or store it into C structures. Later, you can use ``ffi.from_handle(p)`` to retrive the original -``python_object`` from a value with the same ``void *`` pointer. The -cdata object returned by ``new_handle()`` has *ownership*, in the same -sense as ``ffi.new()`` or ``ffi.gc()``: the association ``void * -> -python_object`` is only valid as long as *this* exact cdata returned by -``new_handle()`` is alive. *Calling ffi.from_handle(p) is invalid and -will likely crash if the cdata object returned by new_handle() is not -kept alive!* *New in version 0.7.* +``python_object`` from a value with the same ``void *`` pointer. +*Calling ffi.from_handle(p) is invalid and will likely crash if +the cdata object returned by new_handle() is not kept alive!* +*New in version 0.7.* + +Note that ``from_handle()`` conceptually works like this: it searches in +the list of cdata objects made by ``new_handle()`` the one which has got +the same ``void *`` value; and then it fetches in that cdata object the +corresponding Python object. The cdata object keeps the Python object +alive, similar to how ``ffi.new()`` returns a cdata object that keeps a +piece of memory alive. If the cdata object *itself* is not alive any +more, then the association ``void * -> python_object`` is dead and +``from_handle()`` will crash. .. "versionadded:: 0.7" --- inlined in the previous paragraph @@ -42,6 +42,11 @@ def _ask_pkg_config(resultlist, option, result_prefix='', sysroot=False): resultlist[:] = res def ask_supports_thread(): + if sys.platform == "darwin": + sys.stderr.write("OS/X: confusion between 'cc' versus 'gcc'") + sys.stderr.write(" (see issue 123)\n") + sys.stderr.write("will not use '__thread' in the C code\n") + return import distutils.errors from distutils.ccompiler import new_compiler compiler = new_compiler(force=1) @@ -91,11 +96,23 @@ else: if __name__ == '__main__': - from setuptools import setup, Feature, Extension - setup( - name='cffi', - description='Foreign Function Interface for Python calling C code.', - long_description=""" + from setuptools import setup, Extension + ext_modules = [] + if '__pypy__' not in sys.modules: + ext_modules.append(Extension( + name='_cffi_backend', + include_dirs=include_dirs, + sources=sources, + libraries=libraries, + define_macros=define_macros, + library_dirs=library_dirs, + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args, + )) + setup( + name='cffi', + description='Foreign Function Interface for Python calling C code.', + long_description=""" CFFI ==== @@ -106,36 +123,29 @@ Contact ------- `Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_ - """, - version='0.8.1', - packages=['cffi'], - zip_safe=False, - - url='http://cffi.readthedocs.org', - author='Armin Rigo, Maciej Fijalkowski', - author_email='python-cffi@googlegroups.com', - - license='MIT', - - features={ - 'cextension': Feature( - "fast c backend for cpython", - standard='__pypy__' not in sys.modules, - ext_modules=[ - Extension(name='_cffi_backend', - include_dirs=include_dirs, - sources=sources, - libraries=libraries, - define_macros=define_macros, - library_dirs=library_dirs, - extra_compile_args=extra_compile_args, - extra_link_args=extra_link_args, - ), - ], - ), - }, - - install_requires=[ - 'pycparser', - ] - ) +""", + version='0.8.2', + packages=['cffi'], + zip_safe=False, + + url='http://cffi.readthedocs.org', + author='Armin Rigo, Maciej Fijalkowski', + author_email='python-cffi@googlegroups.com', + + license='MIT', + + ext_modules=ext_modules, + + install_requires=[ + 'pycparser', + ], + classifiers=[ + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + ], + ) diff --git a/testing/backend_tests.py b/testing/backend_tests.py index ab25869..615e366 100644 --- a/testing/backend_tests.py +++ b/testing/backend_tests.py @@ -1,4 +1,5 @@ import py +import platform import sys, ctypes from cffi import FFI, CDefError from testing.support import * @@ -755,6 +756,8 @@ class BackendTests: p = ffi.cast("long long", ffi.cast("wchar_t", -1)) if SIZE_OF_WCHAR == 2: # 2 bytes, unsigned assert int(p) == 0xffff + elif platform.machine() == 'aarch64': # 4 bytes, unsigned + assert int(p) == 0xffffffff else: # 4 bytes, signed assert int(p) == -1 p = ffi.cast("int", u+'\u1234') @@ -1549,3 +1552,21 @@ class BackendTests: ffi2.include(ffi1) p = ffi2.new("foo_p", [142]) assert p.x == 142 + + def test_struct_packed(self): + 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 + assert ffi.sizeof("struct is_packed") == 5 + assert ffi.alignof("struct nonpacked") == 4 + assert ffi.alignof("struct is_packed") == 1 + s = ffi.new("struct is_packed[2]") + s[0].b = 42623381 + s[0].a = b'X' + s[1].b = -4892220 + s[1].a = b'Y' + assert s[0].b == 42623381 + assert s[0].a == b'X' + assert s[1].b == -4892220 + assert s[1].a == b'Y' diff --git a/testing/test_function.py b/testing/test_function.py index 322aea9..aab35de 100644 --- a/testing/test_function.py +++ b/testing/test_function.py @@ -34,6 +34,12 @@ class FdWriteCapture(object): def getvalue(self): return self._value +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' class TestFunction(object): Backend = CTypesBackend @@ -43,18 +49,18 @@ class TestFunction(object): ffi.cdef(""" double sin(double x); """) - m = ffi.dlopen("m") + m = ffi.dlopen(lib_m) x = m.sin(1.23) assert x == math.sin(1.23) def test_sinf(self): if sys.platform == 'win32': - py.test.skip("no 'sinf'") + py.test.skip("no sinf found in the Windows stdlib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" float sinf(float x); """) - m = ffi.dlopen("m") + m = ffi.dlopen(lib_m) x = m.sinf(1.23) assert type(x) is float assert x != math.sin(1.23) # rounding effects @@ -66,14 +72,14 @@ class TestFunction(object): ffi.cdef(""" void sin(double x); """) - m = ffi.dlopen("m") + m = ffi.dlopen(lib_m) x = m.sin(1.23) assert x is None def test_dlopen_filename(self): - path = ctypes.util.find_library("m") + path = ctypes.util.find_library(lib_m) if not path: - py.test.skip("libm not found") + py.test.skip("%s not found" % lib_m) ffi = FFI(backend=self.Backend()) ffi.cdef(""" double cos(double x); @@ -91,7 +97,7 @@ class TestFunction(object): ffi.cdef(""" double cos(double x); """) - m = ffi.dlopen("m", ffi.RTLD_LAZY | ffi.RTLD_LOCAL) + m = ffi.dlopen(lib_m, ffi.RTLD_LAZY | ffi.RTLD_LOCAL) x = m.cos(1.23) assert x == math.cos(1.23) @@ -250,22 +256,14 @@ class TestFunction(object): py.test.skip("probably no symbol 'stdout' in the lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" - int puts(const char *); - void *stdout, *stderr; + void *stdout; """) - ffi.C = ffi.dlopen(None) - pout = ffi.C.stdout - perr = ffi.C.stderr - assert repr(pout).startswith("<cdata 'void *' 0x") - assert repr(perr).startswith("<cdata 'void *' 0x") - with FdWriteCapture(2) as fd: # capturing stderr - ffi.C.stdout = perr - try: - ffi.C.puts(b"hello!") # goes to stdout, which is equal to stderr now - finally: - ffi.C.stdout = pout - res = fd.getvalue() - assert res == b"hello!\n" + C = ffi.dlopen(None) + pout = C.stdout + C.stdout = ffi.NULL + assert C.stdout == ffi.NULL + C.stdout = pout + assert C.stdout == pout def test_strchr(self): ffi = FFI(backend=self.Backend()) @@ -300,7 +298,7 @@ class TestFunction(object): typedef double func_t(double); func_t sin; """) - m = ffi.dlopen("m") + m = ffi.dlopen(lib_m) x = m.sin(1.23) assert x == math.sin(1.23) @@ -363,7 +361,7 @@ class TestFunction(object): ffi.cdef(""" int nonexistent(); """) - m = ffi.dlopen("m") + m = ffi.dlopen(lib_m) assert not hasattr(m, 'nonexistent') def test_wraps_from_stdlib(self): @@ -377,7 +375,7 @@ class TestFunction(object): def wrapper(*args): return f(*args) + 100 return wrapper - m = ffi.dlopen("m") + m = ffi.dlopen(lib_m) sin100 = my_decorator(m.sin) x = sin100(1.23) assert x == math.sin(1.23) + 100 diff --git a/testing/test_parsing.py b/testing/test_parsing.py index 73c2277..3b59a69 100644 --- a/testing/test_parsing.py +++ b/testing/test_parsing.py @@ -11,9 +11,9 @@ class FakeBackend(object): def load_library(self, name, flags): if sys.platform == 'win32': - assert "msvcr" in name + assert name is None or "msvcr" in name else: - assert "libc" in name or "libm" in name + assert name is None or "libc" in name or "libm" in name return FakeLibrary() def new_function_type(self, args, result, has_varargs): @@ -32,10 +32,11 @@ class FakeBackend(object): def new_struct_type(self, name): return FakeStruct(name) - def complete_struct_or_union(self, s, fields, tp=None): + def complete_struct_or_union(self, s, fields, tp=None, + totalsize=-1, totalalignment=-1, sflags=0): assert isinstance(s, FakeStruct) s.fields = fields - + def new_array_type(self, ptrtype, length): return FakeType('<array %s x %s>' % (ptrtype, length)) @@ -59,7 +60,7 @@ class FakeStruct(object): return ', '.join([str(y) + str(x) for x, y, z in self.fields]) class FakeLibrary(object): - + def load_function(self, BType, name): return FakeFunction(BType, name) @@ -69,11 +70,17 @@ class FakeFunction(object): self.BType = str(BType) self.name = name +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' def test_simple(): ffi = FFI(backend=FakeBackend()) ffi.cdef("double sin(double x);") - m = ffi.dlopen("m") + m = ffi.dlopen(lib_m) func = m.sin # should be a callable on real backends assert func.name == 'sin' assert func.BType == '<func (<double>), <double>, False>' @@ -147,7 +154,7 @@ def test_remove_comments(): x, double/*several*//*comment*/y) /*on the same line*/ ; """) - m = ffi.dlopen("m") + m = ffi.dlopen(lib_m) func = m.sin assert func.name == 'sin' assert func.BType == '<func (<double>, <double>), <double>, False>' diff --git a/testing/test_unicode_literals.py b/testing/test_unicode_literals.py index 90902a4..7b0a5cc 100644 --- a/testing/test_unicode_literals.py +++ b/testing/test_unicode_literals.py @@ -10,6 +10,13 @@ from __future__ import unicode_literals import sys, math from cffi import FFI +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' + def test_cast(): ffi = FFI() @@ -55,7 +62,7 @@ def test_enum(): def test_dlopen(): ffi = FFI() ffi.cdef("double sin(double x);") - m = ffi.dlopen("m") # unicode literal + m = ffi.dlopen(lib_m) # unicode literal x = m.sin(1.23) assert x == math.sin(1.23) diff --git a/testing/test_verify.py b/testing/test_verify.py index 442c2cb..ac0aaa4 100644 --- a/testing/test_verify.py +++ b/testing/test_verify.py @@ -4,7 +4,12 @@ from cffi import FFI, VerificationError, VerificationMissing, model from testing.support import * +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'] pass # no obvious -Werror equivalent on MSVC else: if (sys.platform == 'darwin' and @@ -63,13 +68,13 @@ def test_missing_function_import_error(): def test_simple_case(): ffi = FFI() ffi.cdef("double sin(double x);") - lib = ffi.verify('#include <math.h>') + lib = ffi.verify('#include <math.h>', libraries=lib_m) assert lib.sin(1.23) == math.sin(1.23) def test_rounding_1(): ffi = FFI() ffi.cdef("float sin(double x);") - lib = ffi.verify('#include <math.h>') + lib = ffi.verify('#include <math.h>', libraries=lib_m) res = lib.sin(1.23) assert res != math.sin(1.23) # not exact, because of double->float assert abs(res - math.sin(1.23)) < 1E-5 @@ -77,7 +82,7 @@ def test_rounding_1(): def test_rounding_2(): ffi = FFI() ffi.cdef("double sin(float x);") - lib = ffi.verify('#include <math.h>') + lib = ffi.verify('#include <math.h>', libraries=lib_m) res = lib.sin(1.23) assert res != math.sin(1.23) # not exact, because of double->float assert abs(res - math.sin(1.23)) < 1E-5 @@ -103,7 +108,7 @@ def test_strlen_array_of_char(): def test_longdouble(): ffi = FFI() ffi.cdef("long double sinl(long double x);") - lib = ffi.verify('#include <math.h>') + lib = ffi.verify('#include <math.h>', libraries=lib_m) for input in [1.23, ffi.cast("double", 1.23), ffi.cast("long double", 1.23)]: @@ -148,28 +153,27 @@ def test_longdouble_precision(): all_primitive_types = model.PrimitiveType.ALL_PRIMITIVE_TYPES -all_signed_integer_types = sorted(tp for tp in all_primitive_types - if all_primitive_types[tp] == 'i') -all_unsigned_integer_types = sorted(tp for tp in all_primitive_types - if all_primitive_types[tp] == 'u') +all_integer_types = sorted(tp for tp in all_primitive_types + if all_primitive_types[tp] == 'i') all_float_types = sorted(tp for tp in all_primitive_types if all_primitive_types[tp] == 'f') +def all_signed_integer_types(ffi): + return [x for x in all_integer_types if int(ffi.cast(x, -1)) < 0] + +def all_unsigned_integer_types(ffi): + return [x for x in all_integer_types if int(ffi.cast(x, -1)) > 0] + + def test_primitive_category(): for typename in all_primitive_types: tp = model.PrimitiveType(typename) C = tp.is_char_type() - U = tp.is_unsigned_type() - S = tp.is_signed_type() F = tp.is_float_type() I = tp.is_integer_type() assert C == (typename in ('char', 'wchar_t')) - assert U == (typename.startswith('unsigned ') or - typename == '_Bool' or typename == 'size_t' or - typename == 'uintptr_t' or typename.startswith('uint')) assert F == (typename in ('float', 'double', 'long double')) - assert S + U + F + C == 1 # one and only one of them is true - assert I == (S or U) + assert I + F + C == 1 # one and only one of them is true def test_all_integer_and_float_types(): typenames = [] @@ -207,7 +211,7 @@ def test_all_integer_and_float_types(): def test_var_signed_integer_types(): ffi = FFI() - lst = all_signed_integer_types + lst = all_signed_integer_types(ffi) csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) for tp in lst]) ffi.cdef(csource) @@ -226,7 +230,7 @@ def test_var_signed_integer_types(): def test_var_unsigned_integer_types(): ffi = FFI() - lst = all_unsigned_integer_types + lst = all_unsigned_integer_types(ffi) csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) for tp in lst]) ffi.cdef(csource) @@ -247,7 +251,7 @@ def test_var_unsigned_integer_types(): def test_fn_signed_integer_types(): ffi = FFI() - lst = all_signed_integer_types + lst = all_signed_integer_types(ffi) cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) for tp in lst]) ffi.cdef(cdefsrc) @@ -267,7 +271,7 @@ def test_fn_signed_integer_types(): def test_fn_unsigned_integer_types(): ffi = FFI() - lst = all_unsigned_integer_types + lst = all_unsigned_integer_types(ffi) cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) for tp in lst]) ffi.cdef(cdefsrc) @@ -464,11 +468,12 @@ def test_struct_signedness_ignored(): def test_struct_float_vs_int(): if sys.platform == 'win32': py.test.skip("XXX fixme: only gives warnings") - for typename in all_signed_integer_types: + ffi = FFI() + for typename in all_signed_integer_types(ffi): for real in all_float_types: _check_field_match(typename, real, expect_mismatch=True) for typename in all_float_types: - for real in all_signed_integer_types: + for real in all_signed_integer_types(ffi): _check_field_match(typename, real, expect_mismatch=True) def test_struct_array_field(): @@ -1133,6 +1138,9 @@ def test_callback_calling_convention(): xxx def test_opaque_integer_as_function_result(): + import platform + if platform.machine().startswith('sparc'): + py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') # XXX bad abuse of "struct { ...; }". It only works a bit by chance # anyway. XXX think about something better :-( ffi = FFI() @@ -1855,3 +1863,24 @@ def test_various_calls_direct(): def test_various_calls_libffi(): _test_various_calls(force_libffi=True) + +def test_ptr_to_opaque(): + ffi = FFI() + ffi.cdef("typedef ... foo_t; int f1(foo_t*); foo_t *f2(int);") + lib = ffi.verify(""" + #include <stdlib.h> + typedef struct { int x; } foo_t; + int f1(foo_t* p) { + int x = p->x; + free(p); + return x; + } + foo_t *f2(int x) { + foo_t *p = malloc(sizeof(foo_t)); + p->x = x; + return p; + } + """) + p = lib.f2(42) + x = lib.f1(p) + assert x == 42 diff --git a/testing/test_version.py b/testing/test_version.py index ce03b91..6fae16f 100644 --- a/testing/test_version.py +++ b/testing/test_version.py @@ -9,7 +9,7 @@ BACKEND_VERSIONS = { '0.4.2': '0.4', # did not change '0.7.1': '0.7', # did not change '0.7.2': '0.7', # did not change - '0.8.1': '0.8', # did not change + '0.8.1': '0.8', # did not change (essentially) } def test_version(): @@ -24,7 +24,7 @@ def test_doc_version(): content = open(p).read() # v = cffi.__version__ - assert ("version = '%s'\n" % BACKEND_VERSIONS.get(v, v)) in content + assert ("version = '%s'\n" % v[:3]) in content assert ("release = '%s'\n" % v) in content def test_doc_version_file(): diff --git a/testing/test_zdistutils.py b/testing/test_zdistutils.py index 692eebd..f97d015 100644 --- a/testing/test_zdistutils.py +++ b/testing/test_zdistutils.py @@ -7,6 +7,13 @@ from testing.udir import udir class DistUtilsTest(object): + def setup_class(self): + self.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': + self.lib_m = 'msvcrt' def test_locate_engine_class(self): cls = _locate_engine_class(FFI(), self.generic) @@ -25,7 +32,8 @@ class DistUtilsTest(object): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic) + v = Verifier(ffi, csrc, force_generic_engine=self.generic, + libraries=[self.lib_m]) v.write_source() with open(v.sourcefilename, 'r') as f: data = f.read() @@ -35,7 +43,8 @@ class DistUtilsTest(object): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic) + v = Verifier(ffi, csrc, force_generic_engine=self.generic, + libraries=[self.lib_m]) v.sourcefilename = filename = str(udir.join('write_source.c')) v.write_source() assert filename == v.sourcefilename @@ -47,7 +56,8 @@ class DistUtilsTest(object): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic) + v = Verifier(ffi, csrc, force_generic_engine=self.generic, + libraries=[self.lib_m]) try: from StringIO import StringIO except ImportError: @@ -60,7 +70,8 @@ class DistUtilsTest(object): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic) + v = Verifier(ffi, csrc, force_generic_engine=self.generic, + libraries=[self.lib_m]) v.compile_module() assert v.get_module_name().startswith('_cffi_') if v.generates_python_module(): @@ -71,7 +82,8 @@ class DistUtilsTest(object): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!2*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic) + v = Verifier(ffi, csrc, force_generic_engine=self.generic, + libraries=[self.lib_m]) basename = self.__class__.__name__ + 'test_compile_module' v.modulefilename = filename = str(udir.join(basename + '.so')) v.compile_module() @@ -87,7 +99,8 @@ class DistUtilsTest(object): ffi = FFI() ffi.cdef("%s sin(double x);" % csrc) v = Verifier(ffi, "#include <math.h>", - force_generic_engine=self.generic) + force_generic_engine=self.generic, + libraries=[self.lib_m]) names.append(v.get_module_name()) assert names[0] == names[1] != names[2] @@ -104,7 +117,8 @@ class DistUtilsTest(object): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!3*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic) + v = Verifier(ffi, csrc, force_generic_engine=self.generic, + libraries=[self.lib_m]) library = v.load_library() assert library.sin(12.3) == math.sin(12.3) @@ -114,7 +128,8 @@ class DistUtilsTest(object): csrc = '/*hi there %s!4*/#include "test_verifier_args.h"\n' % self udir.join('test_verifier_args.h').write('#include <math.h>\n') v = Verifier(ffi, csrc, include_dirs=[str(udir)], - force_generic_engine=self.generic) + force_generic_engine=self.generic, + libraries=[self.lib_m]) library = v.load_library() assert library.sin(12.3) == math.sin(12.3) @@ -122,7 +137,8 @@ class DistUtilsTest(object): ffi = FFI() ffi.cdef("double sin(double x);") csrc = "/*6%s*/\n#include <math.h>" % self - lib = ffi.verify(csrc, force_generic_engine=self.generic) + lib = ffi.verify(csrc, force_generic_engine=self.generic, + libraries=[self.lib_m]) assert lib.sin(12.3) == math.sin(12.3) assert isinstance(ffi.verifier, Verifier) with open(ffi.verifier.sourcefilename, 'r') as f: @@ -139,7 +155,8 @@ class DistUtilsTest(object): #endif ''' lib = ffi.verify(csrc, define_macros=[('TEST_EXTENSION_OBJECT', '1')], - force_generic_engine=self.generic) + force_generic_engine=self.generic, + libraries=[self.lib_m]) assert lib.sin(12.3) == math.sin(12.3) v = ffi.verifier ext = v.get_extension() @@ -152,7 +169,8 @@ class DistUtilsTest(object): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there9!%s*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic) + v = Verifier(ffi, csrc, force_generic_engine=self.generic, + libraries=[self.lib_m]) assert not os.path.exists(v.sourcefilename) v.get_extension() assert os.path.exists(v.sourcefilename) |