summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2014-03-07 07:52:39 +0100
committerArmin Rigo <arigo@tunes.org>2014-03-07 07:52:39 +0100
commit9fc0398217b8738c01646c8182d171edb55d740b (patch)
treeb186bff5a02dd5f44e1a82c85c1354b0715df92d
parent11d5c2fa7cce0fbfdb874aa8b462d6102e664b02 (diff)
parent5e77026a2d4224ab94b7fc0ed37b9823d34a9a60 (diff)
downloadcffi-9fc0398217b8738c01646c8182d171edb55d740b.tar.gz
Merge for release 0.8.2
-rw-r--r--TODO2
-rw-r--r--c/_cffi_backend.c70
-rw-r--r--c/minibuffer.h12
-rw-r--r--c/test_c.py134
-rw-r--r--cffi/__init__.py4
-rw-r--r--cffi/api.py27
-rw-r--r--cffi/backend_ctypes.py4
-rw-r--r--cffi/cparser.py7
-rw-r--r--cffi/model.py42
-rw-r--r--cffi/vengine_cpy.py48
-rw-r--r--doc/source/conf.py2
-rw-r--r--doc/source/index.rst44
-rw-r--r--setup.py86
-rw-r--r--testing/backend_tests.py21
-rw-r--r--testing/test_function.py48
-rw-r--r--testing/test_parsing.py21
-rw-r--r--testing/test_unicode_literals.py9
-rw-r--r--testing/test_verify.py71
-rw-r--r--testing/test_version.py4
-rw-r--r--testing/test_zdistutils.py40
20 files changed, 462 insertions, 234 deletions
diff --git a/TODO b/TODO
index c23c132..499ae83 100644
--- a/TODO
+++ b/TODO
@@ -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
diff --git a/setup.py b/setup.py
index ba0d00a..c3a6402 100644
--- a/setup.py
+++ b/setup.py
@@ -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)