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

Reply via email to