Re: sometype.__new__ and C subclasses

2010-05-02 Thread Carl Banks
On May 1, 11:03 pm, James Porter port...@alum.rit.edu wrote:
 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.

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.


 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.

It should work if you use mysubtype.__new__(mysubtype,...).

If it doesn't do what you want, then there's probably something wrong
with the way you subclassed ndarray.


Carl Banks
-- 
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 Robert Kern

On 2010-05-02 12:48 , James Porter wrote:

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.


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.


--
Robert Kern

I have come to believe that the whole world is an enigma, a harmless enigma
 that is made terrible by our own mad attempt to interpret it as though it had
 an underlying truth.
  -- Umberto Eco

--
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 Carl Banks
On May 2, 10:48 am, James Porter port...@alum.rit.edu wrote:
 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...,
 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.

I would say numpy is wrong here, so I suggest filing a bug report.

In fact I can't think of any benefit to EVER calling X.__new__(Y)
where X is not Y.  Maybe old-style classes?  Someone who wants to
ensure they're getting an instance of a certain type can check
issubclass(Y,X) then call Y.__new__(Y).

Unfortunately, you just can't get rid of the test in tp_new_wrapper.


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


Re: sometype.__new__ and C subclasses

2010-05-02 Thread Robert Kern

On 2010-05-02 15:28 , Carl Banks wrote:

On May 2, 10:48 am, James Porterport...@alum.rit.edu  wrote:

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...,
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.


I would say numpy is wrong here, so I suggest filing a bug report.

In fact I can't think of any benefit to EVER calling X.__new__(Y)
where X is not Y.  Maybe old-style classes?  Someone who wants to
ensure they're getting an instance of a certain type can check
issubclass(Y,X) then call Y.__new__(Y).


Well, the Y.__new__(Y) may call X.__new__(Y) (and we certainly do this 
successfully in other Python subclasses of ndarray; this also appears in the 
Python regression tests). I'm not sure why this would be permitted there and not 
in a regular function (numpy.zeros_like() seems to be the function that does 
this and fails for the OP). The reason we do it there instead of calling the 
subclass's constructor is because the subclass's constructor may have different 
arguments.


I'm happy to concede that this might be a bug in numpy, but I don't understand 
why this is allowed for Python subclasses but not C subtypes.


--
Robert Kern

I have come to believe that the whole world is an enigma, a harmless enigma
 that is made terrible by our own mad attempt to interpret it as though it had
 an underlying truth.
  -- Umberto Eco

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


Re: sometype.__new__ and C subclasses

2010-05-02 Thread Robert Kern

On 2010-05-02 15:03 , James Porter wrote:


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


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
...

--
Robert Kern

I have come to believe that the whole world is an enigma, a harmless enigma
 that is made terrible by our own mad attempt to interpret it as though it had
 an underlying truth.
  -- Umberto Eco

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


Re: sometype.__new__ and C subclasses

2010-05-02 Thread Carl Banks
On May 2, 1:51 pm, Robert Kern robert.k...@gmail.com wrote:
 On 2010-05-02 15:28 , Carl Banks wrote:



  On May 2, 10:48 am, James Porterport...@alum.rit.edu  wrote:
  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...,
  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.

  I would say numpy is wrong here, so I suggest filing a bug report.

  In fact I can't think of any benefit to EVER calling X.__new__(Y)
  where X is not Y.  Maybe old-style classes?  Someone who wants to
  ensure they're getting an instance of a certain type can check
  issubclass(Y,X) then call Y.__new__(Y).

 Well, the Y.__new__(Y) may call X.__new__(Y) (and we certainly do this
 successfully in other Python subclasses of ndarray; this also appears in the
 Python regression tests). I'm not sure why this would be permitted there and 
 not
 in a regular function (numpy.zeros_like() seems to be the function that does
 this and fails for the OP). The reason we do it there instead of calling the
 subclass's constructor is because the subclass's constructor may have 
 different
 arguments.

 I'm happy to concede that this might be a bug in numpy, but I don't understand
 why this is allowed for Python subclasses but not C subtypes.

Because Python subclasses (i.e., heap types) all invoke
tp_new_wrapper, which is guaranteed to call the tp_new of the most
derived base.

C subtypes can, and often have to, replace tp_new with their own
version.  Calling a base type's tp_new when you've defined your own
tp_new at the C level is dangerous.


As for the issue with a subclass's arguments being different, I'm
shocked that anyone at numpy could possibly think bypassing the
subtype's constructor is good idea.


Carl Banks
-- 
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


Re: sometype.__new__ and C subclasses

2010-05-02 Thread Gregory Ewing

James Porter wrote:
Functions like numpy.zeros_like use ndarray.__new__(subtype, 
...) to create new arrays based on the shape of other arrays.


Maybe the real answer to this question is NumPy is doing it wrong


Yes, I think NumPy is doing it wrong, even for subclasses
written in Python. If the subtype has overridden ndarray's
__new__ method, the way NumPy is doing it will skip the
subclass's version of the method.

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