Capsule, Cobject and python versions

59 views
Skip to first unread message

Fabrice Silva

unread,
Sep 20, 2012, 9:35:06 AM9/20/12
to cython...@googlegroups.com
Hi,
I have some C structure (holding, amongst other fields, an array) and
its relative destructor, and I want to manipulate them from python.
I thus defined a subclass of numpy's ndarray, using PyArray_NewFromDescr
but I want to keep a reference to the original C structure so that it
can be cleanly deallocated with its destructor when the python-side
array is garbage-collected.

I previously used the Pycapsule to encapsulate the C structure in the
ndarray base attribute, but I was reported to it is incompatible with
python versions earlier than 2.7, PyCobject being the now deprecated
parent (will be removed in the next few release).

How can I modify my code to let cython choose between pyCObject and
PyCapsule ?

Bradley Froehle

unread,
Sep 20, 2012, 12:01:10 PM9/20/12
to cython...@googlegroups.com
Hi Fabricio:

I've accomplished something similar by putting the logic in C++ layer, although you'll have to then deal with getting the right Python include directory when you compile.  Here's a small snippet which is part of a larger class and wrapping framework.  The portions you care about will be between the #if statements.  Your case might be easier if you don't have templated array types.

-Brad

#include <Python.h>

#if PY_VERSION_HEX >= 0x02070000 && !(PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 0)
  template <typename T>
  void array_destructor(PyObject* obj) {
    void* ptr = PyCapsule_GetPointer(obj, traits<T>::name);
    if (ptr != NULL)
      delete (array<T>*) ptr;
  }
#else
  template <typename T>
  void array_destructor(void* ptr) {
    if (ptr != NULL)
      delete (array<T>*) ptr;
  }
#endif

  template <typename T>
  PyObject* steal(array<T>& x, PyObject* subtype) {
    if (!x.owndata())
      throw runtime_error("array does not own data; cannot steal ownership");
    PyObject *arr = view(x, subtype);

    array<T>* ptr = new array<T>();
#if PY_VERSION_HEX >= 0x02070000 && !(PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 0)
    PyObject* cap = PyCapsule_New((void*) ptr, traits<T>::name,
 &array_destructor<T>);
#else
    PyObject* cap = PyCObject_FromVoidPtr((void*) ptr, &array_destructor<T>);
#endif
    PyArray_BASE(arr) = cap;

    // Transfer array ownership into the capsule.
    ptr->swap(x);
    return arr;
  }

Nathaniel Smith

unread,
Sep 20, 2012, 2:43:53 PM9/20/12
to cython...@googlegroups.com
If you're already using Cython then the easiest solution is to just
ignore the builtin classes and create your own "capsule-like" class
from scratch:

cdef class MyStructHolder:
mystruct * ref

def __cinit__(self, mystruct * ref):
self.ref = ref

def __dealloc__(self):
mystruct_free(self.ref)

cdef array_from_mystruct(mystruct * ref):
arr = PyArray_New(ref->data, ...)
arr.base = MyStructHolder(ref)
return arr

(modulo syntax errors)

-n

Fabrice Silva

unread,
Sep 21, 2012, 4:40:17 AM9/21/12
to cython...@googlegroups.com
Le jeudi 20 septembre 2012 à 19:43 +0100, Nathaniel Smith a écrit :
>
> If you're already using Cython then the easiest solution is to just
> ignore the builtin classes and create your own "capsule-like" class
> from scratch:
>
> cdef class MyStructHolder:
> mystruct * ref
>
> def __cinit__(self, mystruct * ref):
> self.ref = ref
>
> def __dealloc__(self):
> mystruct_free(self.ref)
>
> cdef array_from_mystruct(mystruct * ref):
> arr = PyArray_New(ref->data, ...)
> arr.base = MyStructHolder(ref)
> return arr

It is exactly what I ended up with.,although it seems overkill to me to
create a new extension just for that...
Thanks to you and Bradley.

Regards,

--
Fabrice Silva


Reply all
Reply to author
Forward
0 new messages