sometype.__new__ and C subclasses

2010-05-02 Thread James Porter
I've been trying to write a Python C extension module that uses NumPy 
and has a subtype of numpy.ndarray written in C. However, I've run into 
a snag: calling numpy.ndarray.__new__(mysubtype, ...) triggers an 
exception in the bowels of Python (this is necessary for a handful of 
NumPy features). I'm posting to this list to try to figure out why this 
exception exists in the first place, and what (if anything) I can do to 
work around it.


The exception in question happens in Objects/typeobject.c in 
tp_new_wrapper. Here's the comment for the block:


/* Check that the use doesn't do something silly and unsafe like
   object.__new__(dict).  To do this, we check that the
   most derived base that's not a heap type is this type. */

The code has the end effect that basetype.__new__(subtype, ...) fails 
whenever subtype is a statically-defined type (i.e. a normal C extension 
type object). Why is this necessary in general? I can see why it might 
be bad for a limited number of core Python types, but it seems 
unnecessarily limiting for Python C extensions.


On a more practical note, is there a way (short of rewriting the subtype 
in Python) to work around this? It seems that I could call the type 
metaclass to create a heap type in C, but I'm not sure of all the 
implications of that.


Thanks in advance,
Jim

--
http://mail.python.org/mailman/listinfo/python-list


Re: sometype.__new__ and C subclasses

2010-05-02 Thread James Porter

On 5/2/2010 4:34 AM, Carl Banks wrote:

Why don't you use mysubtype.__new__(mysubtype,...)?

If you wrote mysubtype in C, and defined a different tp_new than
ndarray, then this exception will trigger.  And it ought to; you don't
want to use ndarray's tp_new to create an object of your subclass, if
you've defined a different tp_new.


Unfortunately, I can't do that, since that call is in NumPy itself and 
it's part of their standard way of making instances of subclasses of 
ndarray. Functions like numpy.zeros_like use ndarray.__new__(subtype, 
...) to create new arrays based on the shape of other arrays.


The Python version of the subclass is shown here: 
http://docs.scipy.org/doc/numpy/user/basics.subclassing.html#slightly-more-realistic-example-attribute-added-to-existing-array, 
and I'm trying to write something pretty similar in C. I'm trying to 
stay in C since everything else is in C, so it's easier to stay in C 
then to jump back and forth all the time.


Maybe the real answer to this question is NumPy is doing it wrong and 
I should be on their list; still, it seems strange that the behavior is 
different between Python and C.


- Jim

--
http://mail.python.org/mailman/listinfo/python-list


Re: sometype.__new__ and C subclasses

2010-05-02 Thread James Porter

On 5/2/2010 1:43 PM, Robert Kern wrote:

Perhaps things would be clearer if you could post the C code that you've
written that fails. So far, you've only alluded at what you are doing
using Python-syntax examples.


I'm not sure how much this will help, but here you go. The actual C code 
probably doesn't matter except for where I set tp_flags, tp_new, and 
register the type, but I included it for completeness. The full C source 
is available here if you need it, but be warned that other strangeness 
abounds in the code: 
http://trac.mcs.anl.gov/projects/ITAPS/browser/python/trunk/iMesh_array.inl?rev=3831.


Obviously, this is kind of a bizarre case, so I'm not entirely sure what 
the best route is here.


Thanks,
Jim

static PyObject*
iMeshArrObj_new(PyTypeObject *cls,PyObject *args,PyObject *kw)
{
static char *kwlist[] = {object,instance,0};

PyObject *obj;
iMesh_Object *instance = NULL;
PyObject *arr = NULL;
iMeshArr_Object *self;

if(!PyArg_ParseTupleAndKeywords(args,kw,O|O!,kwlist,obj,
iMesh_Type,instance))
return NULL;

arr = PyArray_FROM_O(obj);
if(arr == NULL)
return NULL;

self = (iMeshArr_Object*)PyObject_CallMethod(arr,view,O,cls);
Py_DECREF(arr);
if(self == NULL)
return NULL;

/* some boring stuff to set |instance| */

return self;
}

static void
iMeshArrObj_dealloc(iMeshArr_Object *self)
{
Py_XDECREF(self-instance);
self-array.ob_type-tp_free((PyObject*)self);
}

static PyObject*
iMeshArrObj_finalize(iMeshArr_Object *self,PyObject *args)
{
iMeshArr_Object *context;
if(PyArg_ParseTuple(args,O!,iMeshArr_Type,context))
{
self-instance = context-instance;
Py_XINCREF(self-instance);
}
PyErr_Clear();
Py_RETURN_NONE;
}

static PyMethodDef iMeshArrObj_methods[] = {
{ __array_finalize__, (PyCFunction)iMeshArrObj_finalize,
  METH_VARARGS, 
},
{0}
};

static PyMemberDef iMeshArrObj_members[] = {
{instance, T_OBJECT_EX, offsetof(iMeshArr_Object, instance),
 READONLY, base iMesh instance},
{0}
};

static PyTypeObject iMeshArr_Type = {
PyObject_HEAD_INIT(NULL)
/* ... */
(destructor)iMeshArrObj_dealloc,  /* tp_dealloc */
/* ... */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
iMesh array objects,/* tp_doc */
/* ... */
iMeshArrObj_methods,  /* tp_methods */
iMeshArrObj_members,  /* tp_members */
/* ... */
iMeshArrObj_new,  /* tp_new */
};

PyMODINITFUNC initiMesh(void)
{
PyObject *m;
m = Py_InitModule(iMesh,module_methods);
import_array();

iMeshArr_Type.tp_base = PyArray_Type;  
if(PyType_Ready(iMeshArr_Type)  0)
return;
Py_INCREF(iMeshArr_Type);
PyModule_AddObject(m,Array,(PyObject *)iMeshArr_Type);
}

/* End C code */

And then in Python:

A = iMesh.Array(numpy.array([1,2,3,4,5]), instance=mesh)
numpy.zeros_like(A) # fails here

Inside NumPy, zeros_like looks like this (there's a bit more than this, 
but it's irrelevant to this problem):


def zeros_like(a):
if isinstance(a, ndarray):
res = ndarray.__new__(type(a), a.shape, a.dtype,
  order=a.flags.fnc)
res.fill(0)
return res

--
http://mail.python.org/mailman/listinfo/python-list


Re: sometype.__new__ and C subclasses

2010-05-02 Thread James Porter

On 5/2/2010 3:58 PM, Robert Kern wrote:

Well, I think we can change zeros_like() and the rest to work around
this issue. Can you bring it up on the numpy mailing list?

def zeros_like(a):
if isinstance(a, ndarray):
res = numpy.empty(a.shape, a.dtype, order=a.flags.fnc)
res.fill(0)
res = res.view(type(a))
return res
...


I'm having difficulty posting to the NumPy list (both via gmane and 
email) so I'm just going to put this here so it doesn't get lost. 
zeros_like probably needs to call __array_finalize__ for this to work 
properly (it'll cause a segfault for me otherwise):


 def zeros_like(a):
 if isinstance(a, ndarray):
 res = numpy.zeros(a.shape, a.dtype, order=a.flags.fnc)
 res = res.view(type(a))
 res.__array_finalize__(a)
 return res
 ...

- Jim

--
http://mail.python.org/mailman/listinfo/python-list