Author: Armin Rigo <[email protected]>
Branch:
Changeset: r932:de07142a3fde
Date: 2012-09-13 17:22 +0200
http://bitbucket.org/cffi/cffi/changeset/de07142a3fde/
Log: Fix multiple small issues about when floats are accepted (or cdata
containing doubles -- or long doubles :-/ )
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -392,6 +392,14 @@
/************************************************************/
+static int
+CDataObject_Or_PyFloat_Check(PyObject *ob)
+{
+ return (PyFloat_Check(ob) ||
+ (CData_Check(ob) &&
+ (((CDataObject *)ob)->c_type->ct_flags & CT_PRIMITIVE_FLOAT)));
+}
+
static PY_LONG_LONG
_my_PyLong_AsLongLong(PyObject *ob)
{
@@ -413,7 +421,7 @@
PY_LONG_LONG res;
PyNumberMethods *nb = ob->ob_type->tp_as_number;
- if (PyFloat_Check(ob) ||
+ if (CDataObject_Or_PyFloat_Check(ob) ||
nb == NULL || nb->nb_int == NULL) {
PyErr_SetString(PyExc_TypeError, "an integer is required");
return -1;
@@ -470,7 +478,7 @@
unsigned PY_LONG_LONG res;
PyNumberMethods *nb = ob->ob_type->tp_as_number;
- if ((strict && PyFloat_Check(ob)) ||
+ if ((strict && CDataObject_Or_PyFloat_Check(ob)) ||
nb == NULL || nb->nb_int == NULL) {
PyErr_SetString(PyExc_TypeError, "an integer is required");
return (unsigned PY_LONG_LONG)-1;
@@ -2263,6 +2271,45 @@
return (PyObject *)cd;
}
+static int
+_my_PyObject_AsBool(PyObject *ob)
+{
+ /* convert and cast a Python object to a boolean. Accept an integer
+ or a float object, up to a CData 'long double'. */
+ double x;
+
+#if PY_MAJOR_VERSION < 3
+ if (PyInt_Check(ob)) {
+ return PyInt_AS_LONG(ob) != 0;
+ }
+ else
+#endif
+ if (PyLong_Check(ob)) {
+ return _PyLong_Sign(ob) != 0;
+ }
+ else if (CData_Check(ob)) {
+ CDataObject *cd = (CDataObject *)ob;
+ if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
+ if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) {
+ /* 'long double' objects: return the answer directly */
+ return read_raw_longdouble_data(cd->c_data) != 0.0;
+ }
+ /* else fall through */
+ }
+ else {
+ unsigned PY_LONG_LONG value;
+ value = _my_PyLong_AsUnsignedLongLong(ob, 0);
+ if (value == -1 && PyErr_Occurred())
+ return -1;
+ return value != 0;
+ }
+ }
+ x = PyFloat_AsDouble(ob);
+ if (x == -1.0 && PyErr_Occurred())
+ return -1;
+ return x != 0.0;
+}
+
static CDataObject *_new_casted_primitive(CTypeDescrObject *ct)
{
int dataoffset = offsetof(CDataObject_casted_primitive, alignment);
@@ -2334,15 +2381,15 @@
return NULL;
value = (unsigned char)res;
}
+ else if (ct->ct_flags & CT_IS_BOOL) {
+ value = _my_PyObject_AsBool(ob);
+ if (value < 0)
+ return NULL;
+ }
else {
value = _my_PyLong_AsUnsignedLongLong(ob, 0);
if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())
return NULL;
- if (ct->ct_flags & CT_IS_BOOL) {
- value = PyObject_IsTrue(ob);
- if (value < 0)
- return NULL;
- }
}
if (ct->ct_flags & CT_IS_BOOL)
value = !!value;
@@ -4368,16 +4415,10 @@
return NULL;
}
-#if PY_MAJOR_VERSION < 3
-# define PyCffiInt_AsLong PyInt_AsLong
-#else
-# define PyCffiInt_AsLong PyLong_AsLong
-#endif
-
#define _cffi_to_c_PRIMITIVE(TARGETNAME, TARGET) \
static TARGET _cffi_to_c_##TARGETNAME(PyObject *obj) { \
- long tmp = PyCffiInt_AsLong(obj); \
- if (tmp != (TARGET)tmp) \
+ PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj); \
+ if (tmp != (TARGET)tmp && !PyErr_Occurred()) \
return (TARGET)_convert_overflow(obj, #TARGET); \
return (TARGET)tmp; \
}
@@ -4386,10 +4427,14 @@
_cffi_to_c_PRIMITIVE(unsigned_char, unsigned char)
_cffi_to_c_PRIMITIVE(short, short)
_cffi_to_c_PRIMITIVE(unsigned_short, unsigned short)
-#if SIZEOF_INT < SIZEOF_LONG
+#if SIZEOF_INT == SIZEOF_LONG
+#define _cffi_to_c_int _cffi_to_c_long
+#define _cffi_to_c_unsigned_int _cffi_to_c_unsigned_long
+#else
_cffi_to_c_PRIMITIVE(int, int)
_cffi_to_c_PRIMITIVE(unsigned_int, unsigned int)
#endif
+_cffi_to_c_PRIMITIVE(long, long)
#if SIZEOF_LONG < SIZEOF_LONG_LONG
static unsigned long _cffi_to_c_unsigned_long(PyObject *obj)
@@ -4403,6 +4448,8 @@
# define _cffi_to_c_unsigned_long _cffi_to_c_unsigned_long_long
#endif
+#define _cffi_to_c_long_long _my_PyLong_AsLongLong
+
static unsigned PY_LONG_LONG _cffi_to_c_unsigned_long_long(PyObject *obj)
{
return _my_PyLong_AsUnsignedLongLong(obj, 1);
@@ -4437,12 +4484,15 @@
static _Bool _cffi_to_c__Bool(PyObject *obj)
{
- long tmp = PyCffiInt_AsLong(obj);
- switch (tmp) {
- case 0: return 0;
- case 1: return 1;
- default: return (_Bool)_convert_overflow(obj, "_Bool");
- }
+ PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj);
+ if (tmp == 0)
+ return 0;
+ else if (tmp == 1)
+ return 1;
+ else if (PyErr_Occurred())
+ return (_Bool)-1;
+ else
+ return (_Bool)_convert_overflow(obj, "_Bool");
}
static PyObject *_cffi_get_struct_layout(Py_ssize_t nums[])
@@ -4483,15 +4533,10 @@
_cffi_to_c_unsigned_char,
_cffi_to_c_short,
_cffi_to_c_unsigned_short,
-#if SIZEOF_INT < SIZEOF_LONG
_cffi_to_c_int,
_cffi_to_c_unsigned_int,
-#else
- 0,
- 0,
-#endif
+ _cffi_to_c_long,
_cffi_to_c_unsigned_long,
- _cffi_to_c_unsigned_long_long,
_cffi_to_c_char,
_cffi_from_c_pointer,
_cffi_to_c_pointer,
@@ -4511,6 +4556,8 @@
#endif
_cffi_to_c_long_double,
_cffi_to_c__Bool,
+ _cffi_to_c_long_long,
+ _cffi_to_c_unsigned_long_long,
};
/************************************************************/
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -2088,6 +2088,8 @@
assert int(cast(BBool, True)) == 1
assert bool(cast(BBool, False)) is True # warning!
assert int(cast(BBool, 3)) == 1
+ assert int(cast(BBool, long(3))) == 1
+ assert int(cast(BBool, 10**40000)) == 1
assert int(cast(BBool, -0.1)) == 1
assert int(cast(BBool, -0.0)) == 0
assert int(cast(BBool, '\x00')) == 0
@@ -2105,3 +2107,6 @@
q = cast(BBoolP, p)
assert q[0] == ord('X')
py.test.raises(TypeError, string, cast(BBool, False))
+ BDouble = new_primitive_type("double")
+ assert int(cast(BBool, cast(BDouble, 0.1))) == 1
+ assert int(cast(BBool, cast(BDouble, 0.0))) == 0
diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
--- a/cffi/vengine_cpy.py
+++ b/cffi/vengine_cpy.py
@@ -671,7 +671,6 @@
#if PY_MAJOR_VERSION >= 3
# define PyInt_FromLong PyLong_FromLong
-# define PyInt_AsLong PyLong_AsLong
#endif
#define _cffi_from_c_double PyFloat_FromDouble
@@ -698,7 +697,6 @@
# define _cffi_from_c_long_long PyInt_FromLong
#endif
-#define _cffi_to_c_long PyInt_AsLong
#define _cffi_to_c_double PyFloat_AsDouble
#define _cffi_to_c_float PyFloat_AsDouble
@@ -712,21 +710,14 @@
((short(*)(PyObject *))_cffi_exports[3])
#define _cffi_to_c_unsigned_short \
((unsigned short(*)(PyObject *))_cffi_exports[4])
-
-#if SIZEOF_INT < SIZEOF_LONG
-# define _cffi_to_c_int \
- ((int(*)(PyObject *))_cffi_exports[5])
-# define _cffi_to_c_unsigned_int \
- ((unsigned int(*)(PyObject *))_cffi_exports[6])
-#else
-# define _cffi_to_c_int _cffi_to_c_long
-# define _cffi_to_c_unsigned_int _cffi_to_c_unsigned_long
-#endif
-
+#define _cffi_to_c_int \
+ ((int(*)(PyObject *))_cffi_exports[5])
+#define _cffi_to_c_unsigned_int \
+ ((unsigned int(*)(PyObject *))_cffi_exports[6])
+#define _cffi_to_c_long \
+ ((long(*)(PyObject *))_cffi_exports[7])
#define _cffi_to_c_unsigned_long \
- ((unsigned long(*)(PyObject *))_cffi_exports[7])
-#define _cffi_to_c_unsigned_long_long \
- ((unsigned long long(*)(PyObject *))_cffi_exports[8])
+ ((unsigned long(*)(PyObject *))_cffi_exports[8])
#define _cffi_to_c_char \
((char(*)(PyObject *))_cffi_exports[9])
#define _cffi_from_c_pointer \
@@ -755,13 +746,11 @@
((long double(*)(PyObject *))_cffi_exports[21])
#define _cffi_to_c__Bool \
((_Bool(*)(PyObject *))_cffi_exports[22])
-#define _CFFI_NUM_EXPORTS 23
-
-#if SIZEOF_LONG < SIZEOF_LONG_LONG
-# define _cffi_to_c_long_long PyLong_AsLongLong
-#else
-# define _cffi_to_c_long_long _cffi_to_c_long
-#endif
+#define _cffi_to_c_long_long \
+ ((long long(*)(PyObject *))_cffi_exports[23])
+#define _cffi_to_c_unsigned_long_long \
+ ((unsigned long long(*)(PyObject *))_cffi_exports[24])
+#define _CFFI_NUM_EXPORTS 25
typedef struct _ctypedescr CTypeDescrObject;
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -1067,12 +1067,86 @@
def test_bool():
ffi = FFI()
- ffi.cdef("_Bool foo(_Bool);")
+ ffi.cdef("struct foo_s { _Bool x; };"
+ "_Bool foo(_Bool);")
lib = ffi.verify("""
+ struct foo_s { _Bool x; };
int foo(int arg) {
return !arg;
}
""")
+ p = ffi.new("struct foo_s *")
+ p.x = 1
+ assert p.x == 1
+ py.test.raises(OverflowError, "p.x = -1")
+ py.test.raises(TypeError, "p.x = 0.0")
assert lib.foo(1) == 0
assert lib.foo(0) == 1
py.test.raises(OverflowError, lib.foo, 42)
+ py.test.raises(TypeError, lib.foo, 0.0)
+ assert int(ffi.cast("_Bool", long(1))) == 1
+ assert int(ffi.cast("_Bool", long(0))) == 0
+ assert int(ffi.cast("_Bool", long(-1))) == 1
+ assert int(ffi.cast("_Bool", 10**200)) == 1
+ assert int(ffi.cast("_Bool", 10**40000)) == 1
+ #
+ class Foo(object):
+ def __int__(self):
+ self.seen = 1
+ return result
+ f = Foo()
+ f.seen = 0
+ result = 42
+ assert int(ffi.cast("_Bool", f)) == 1
+ assert f.seen
+ f.seen = 0
+ result = 0
+ assert int(ffi.cast("_Bool", f)) == 0
+ assert f.seen
+ #
+ py.test.raises(TypeError, ffi.cast, "_Bool", [])
+
+def test_bool_on_long_double():
+ f = 1E-250
+ if f == 0.0 or f*f != 0.0:
+ py.test.skip("unexpected precision")
+ ffi = FFI()
+ ffi.cdef("long double square(long double f); _Bool opposite(_Bool);")
+ lib = ffi.verify("long double square(long double f) { return f*f; }\n"
+ "_Bool opposite(_Bool x) { return !x; }")
+ f0 = lib.square(0.0)
+ f2 = lib.square(f)
+ f3 = lib.square(f * 2.0)
+ assert repr(f2) != repr(f3) # two non-null 'long doubles'
+ assert float(f0) == float(f2) == float(f3) == 0.0 # too tiny for 'double'
+ assert int(ffi.cast("_Bool", f2)) == 1
+ assert int(ffi.cast("_Bool", f3)) == 1
+ assert int(ffi.cast("_Bool", f0)) == 0
+ py.test.raises(TypeError, lib.opposite, f2)
+
+def test_cannot_pass_float():
+ for basetype in ['char', 'short', 'int', 'long', 'long long']:
+ for sign in ['signed', 'unsigned']:
+ type = '%s %s' % (sign, basetype)
+ ffi = FFI()
+ ffi.cdef("struct foo_s { %s x; };\n"
+ "int foo(%s);" % (type, type))
+ lib = ffi.verify("""
+ struct foo_s { %s x; };
+ int foo(%s arg) {
+ return !arg;
+ }
+ """ % (type, type))
+ p = ffi.new("struct foo_s *")
+ py.test.raises(TypeError, "p.x = 0.0")
+ assert lib.foo(42) == 0
+ assert lib.foo(0) == 1
+ py.test.raises(TypeError, lib.foo, 0.0)
+
+def test_cast_from_int_type_to_bool():
+ ffi = FFI()
+ for basetype in ['char', 'short', 'int', 'long', 'long long']:
+ for sign in ['signed', 'unsigned']:
+ type = '%s %s' % (sign, basetype)
+ assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1
+ assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit