[Scipy-tickets] [SciPy] #1187: ode crashes if rhs returns a tuple instead of a list

SciPy Trac scipy-tickets@scipy....
Fri Nov 11 07:32:19 CST 2011


#1187: ode crashes if rhs returns a tuple instead of a list
--------------------------------------------------------+-------------------
 Reporter:  miha                                        |       Owner:  somebody   
     Type:  defect                                      |      Status:  new        
 Priority:  normal                                      |   Milestone:  Unscheduled
Component:  scipy.integrate                             |     Version:  0.7.0      
 Keywords:  integrade, ode, PyObject_Call, tuple, list  |  
--------------------------------------------------------+-------------------

Comment(by warren.weckesser):

 This appears to be an f2py bug.  f2py generates the file vodemodule.c,
 which contains this snippet:
 {{{
   capi_return =
 PyObject_CallObject(cb_f_in_dvode__user__routines_capi,(PyObject
 *)capi_arglist);
 #ifdef F2PY_REPORT_ATEXIT
 f2py_cb_stop_call_clock();
 #endif
   CFUNCSMESSPY("cb:capi_return=",capi_return);
   if (capi_return == NULL) {
     fprintf(stderr,"capi_return is NULL\n");
     goto capi_fail;
   }
   if (capi_return == Py_None) {
     Py_DECREF(capi_return);
     capi_return = Py_BuildValue("()");
   }
   else if (!PyTuple_Check(capi_return)) {
     capi_return = Py_BuildValue("(N)",capi_return);
   }
   capi_j = PyTuple_Size(capi_return);
   capi_i = 0;
 /*frompyobj*/
   if (capi_j>capi_i) {
     PyArrayObject *rv_cb_arr = NULL;
     if ((capi_tmp = PyTuple_GetItem(capi_return,capi_i++))==NULL) goto
 capi_fail;
     rv_cb_arr =
 array_from_pyobj(PyArray_DOUBLE,ydot_Dims,1,F2PY_INTENT_IN
 |F2PY_INTENT_C
 ,capi_tmp);
     if (rv_cb_arr == NULL) {
       fprintf(stderr,"rv_cb_arr is NULL\n");
       goto capi_fail;
     }
     MEMCOPY(ydot,rv_cb_arr->data,PyArray_NBYTES(rv_cb_arr));
     if (capi_tmp != (PyObject *)rv_cb_arr) {
       Py_DECREF(rv_cb_arr);
     }
   }
   CFUNCSMESS("cb:cb_f_in_dvode__user__routines:successful\n");
   Py_DECREF(capi_return);
 }}}
 In the first line of the snippet, capi_return is the python object
 returned
 by the call to the the callback function f().  Several checks are made:

 * if capi_return is NONE, something bad happened (not relevant here);

 * if capi_return is Py_None, the user's function returned None. This code
   replaces None with an empty tuple;

 * if capi_return is is not a tuple, it is wrapped in a tuple of length 1:
 {{{
         capi_return = Py_BuildValue("(N)",capi_return);
 }}}
 Note that if the return value is already a tuple, it is unchanged by these
 initial checks.  Then this line
 {{{
   capi_j = PyTuple_Size(capi_return);
 }}}
 results in capi_j being 2, so in the next 'if' statement, capi_j > capi_i,
 and
 the if-block is executed.  That code calls PyTuple_GetItem to get the
 first
 element of the tuple, which it then passes to array_from_pyobj.  This is
 the
 problem, because if the return value of f() was a tuple, it is only the
 first
 element of the tuple--a scalar--that is being passed to array_from_pyobj
 in
 the next line.  The function array_from_pyobj() is defined in
 fortranobject.c.
 It will call check_and_fix_dimensions() (also in fortranobject.c) like
 this:
 {{{
         if (check_and_fix_dimensions(arr,rank,dims)) {
             return NULL; /*XXX: set exception */
         }
 }}}
 It is check_and_fix_dimensions() that is printing the error message
 {{{
 0-th dimension must be 2 but got 0 (not defined).
 }}}
 because it got a scalar value, but it expected an array with shape (2,).
 Then the code in array_from_pyobj() detects that
 check_and_fix_dimensions()
 has failed, and returns NULL, but--as noted with 'XXX' in the comment--it
 does not properly set the error exception.  This results in the error
 message:
 {{{
 SystemError: NULL result without error in PyObject_Call
 }}}
 when the exception propagates up to the user.

 I don't know why the code in vodemodule.c wraps the non-tuple result in
 a tuple, only to pull out the value later.  But this appears to be the
 source
 of the bug.  This means, for example, that if f() returns a tuple of
 length 1
 containing the tuple of values, the solver works--but that's just a
 curiosity,
 not a solution or work-around.

-- 
Ticket URL: <http://projects.scipy.org/scipy/ticket/1187#comment:1>
SciPy <http://www.scipy.org>
SciPy is open-source software for mathematics, science, and engineering.


More information about the Scipy-tickets mailing list