Commits

mattip committed 95c2319

add c test, fix PyNumber_Coerce{,Ex}
including PyDecRef example code for successful call since coerce returns new object

Comments (0)

Files changed (3)

pypy/module/cpyext/api.py

                             return api_function.error_value
                     if not we_are_translated():
                         got_integer = isinstance(res, (int, long, float))
-                        assert got_integer == expect_integer
+                        assert got_integer == expect_integer,'got %r not integer' % res
                     if res is None:
                         return None
                     elif isinstance(res, Reference):

pypy/module/cpyext/number.py

 from pypy.module.cpyext.pyobject import PyObject, PyObjectP, from_ref, make_ref, Py_DecRef
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.tool.sourcetools import func_with_new_name
+from pypy.module.cpyext.state import State
 
 @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
 def PyIndex_Check(space, w_obj):
     """
     return space.index(w_obj)
 
-@cpython_api([PyObjectP, PyObjectP], rffi.INT_real, error=-1)
+@cpython_api([PyObjectP, PyObjectP], rffi.INT_real, error=CANNOT_FAIL)
 def PyNumber_CoerceEx(space, pp1, pp2):
     """This function is similar to PyNumber_Coerce(), except that it returns
     1 when the conversion is not possible and when no error is raised.
     Reference counts are still not increased in this case."""
-    w_obj1 = from_ref(space, pp1[0])
-    w_obj2 = from_ref(space, pp2[0])
-    w_res = space.try_coerce(w_obj1, w_obj2)
-    if w_res is None:
+    retVal = PyNumber_Coerce(space, pp1, pp2)
+    if retVal != 0:
         return 1
-    else:
-        Py_DecRef(space, pp1[0])
-        Py_DecRef(space, pp2[0])
-        pp1[0] = make_ref(space, space.getitem(w_res, space.wrap(0)))
-        pp2[0] = make_ref(space, space.getitem(w_res, space.wrap(1)))
-        return 0
+    return 0
 
-@cpython_api([PyObjectP, PyObjectP], rffi.INT_real, error=-1)
+@cpython_api([PyObjectP, PyObjectP], rffi.INT_real, error=CANNOT_FAIL)
 def PyNumber_Coerce(space, pp1, pp2):
     """This function takes the addresses of two variables of type PyObject*.  If
     the objects pointed to by *p1 and *p2 have the same type, increment their
     Python statement o1, o2 = coerce(o1, o2)."""
     w_obj1 = from_ref(space, pp1[0])
     w_obj2 = from_ref(space, pp2[0])
-    w_res = space.coerce(w_obj1, w_obj2)
-    if w_res is None:
+    try:
+        w_res = space.coerce(w_obj1, w_obj2)
+    except (TypeError, OperationError):
+        state = space.fromcache(State)
+        state.clear_exception()
         return -1
-    else:
-        Py_DecRef(space, pp1[0])
-        Py_DecRef(space, pp2[0])
-        pp1[0] = make_ref(space, space.getitem(w_res, space.wrap(0)))
-        pp2[0] = make_ref(space, space.getitem(w_res, space.wrap(1)))
-        return 0
+    w_res1, w_res2 = space.unpackiterable(w_res, 2)
+    pp1[0] = make_ref(space, w_res1)
+    pp2[0] = make_ref(space, w_res2)
+    return 0
 
 def func_rename(newname):
     return lambda func: func_with_new_name(func, newname)

pypy/module/cpyext/test/test_number.py

 from rpython.rtyper.lltypesystem import lltype
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.module.cpyext.pyobject import PyObjectP, from_ref, make_ref, Py_DecRef
+from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 
 class TestIterator(BaseApiTest):
     def test_check(self, space, api):
         assert space.str_w(space.repr(from_ref(space, pp2[0]))) == '456.789'
         Py_DecRef(space, pp1[0])
         Py_DecRef(space, pp2[0])
-        # Yes, decrement twice.
+        lltype.free(pp1, flavor='raw')
+        # Yes, decrement twice since we decoupled between w_obj* and pp*[0].
         Py_DecRef(space, w_obj1)
         Py_DecRef(space, w_obj2)
-        lltype.free(pp1, flavor='raw')
         lltype.free(pp2, flavor='raw')
 
     def test_number_coerce_ex(self, space, api):
 
         assert api.PyFloat_Check(w_res)
         assert space.unwrap(w_res) == 123.
+        Py_DecRef(space, pl)
+        Py_DecRef(space, pf)
         Py_DecRef(space, ppl[0])
         Py_DecRef(space, ppf[0])
         lltype.free(ppl, flavor='raw')
             api.PyNumber_Power(space.wrap(3), space.wrap(2), space.wrap(5)))
         assert 9 == space.unwrap(
             api.PyNumber_InPlacePower(space.wrap(3), space.wrap(2), space.w_None))
+
+class AppTestCNumber(AppTestCpythonExtensionBase):
+    def test_app_coerce(self):
+        mod = self.import_extension('foo', [
+            ("test_fail", "METH_NOARGS",
+             '''
+                PyObject * hello = PyString_FromString("hello");
+                PyObject * float1 = PyFloat_FromDouble(1.0);
+                int retVal = PyNumber_Coerce(&hello, &float1);
+                Py_DECREF(hello);
+                Py_DECREF(float1);
+                return PyInt_FromLong(retVal);
+            '''),
+            ("test", "METH_NOARGS",
+             '''
+                PyObject * float1p = PyFloat_FromDouble(1.0);
+                PyObject * int3p   = PyInt_FromLong(3);
+                PyObject * tupl = PyTuple_New(2);
+                PyObject float1 = *float1p;
+                PyObject int3 = *int3p;
+                int retVal = PyNumber_CoerceEx(&int3p, &float1p);
+                if (retVal == 0)
+                {
+                    PyTuple_SET_ITEM(tupl, 0, int3p);
+                    PyTuple_SET_ITEM(tupl, 1, float1p);
+                }
+                Py_DECREF(&int3);
+                Py_DECREF(&float1);
+                Py_DECREF(int3p);
+                Py_DECREF(float1p);
+                return tupl;
+            ''')])
+        assert mod.test_fail() == -1
+        '''tupl = mod.test()
+        assert tupl[0] == 3.
+        assert tupl[1] == 1.
+        assert isinstance(tupl[0], float)'''