Issue #13053: Added section on migrating from CObject to Capsule

to howto/cporting.rst.
This commit is contained in:
Larry Hastings 2011-10-09 13:03:44 +01:00
parent 017e535bde
commit fc45bbaccc
2 changed files with 186 additions and 0 deletions

View File

@ -209,6 +209,58 @@ both 2.x and 3.0 is tricky. The following simple example demonstrates how. ::
}
CObject replaced with Capsule
=============================
The :ctype:`Capsule` object was introduced in Python 3.1 and 2.7 to replace
:ctype:`CObject`. CObjects were useful,
but the :ctype:`CObject` API was problematic: it didn't permit distinguishing
between valid CObjects, which allowed mismatched CObjects to crash the
interpreter, and some of its APIs relied on undefined behavior in C.
(For further reading on the rationale behind Capsules, please see :issue:`5630`.)
If you're currently using CObjects, and you want to migrate to 3.1 or newer,
you'll need to switch to Capsules.
:ctype:`CObject` was deprecated in 3.1 and 2.7 and completely removed in
Python 3.2. If you only support 2.7, or 3.1 and above, you
can simply switch to :ctype:`Capsule`. If you need to support 3.0 or
versions of Python earlier than 2.7 you'll have to support both CObjects
and Capsules.
The following example header file :file:`capsulethunk.h` may
solve the problem for you;
simply write your code against the :ctype:`Capsule` API, include
this header file after ``"Python.h"``, and you'll automatically use CObjects
in Python 3.0 or versions earlier than 2.7.
:file:`capsulethunk.h` simulates Capsules using CObjects. However,
:ctype:`CObject` provides no place to store the capsule's "name". As a
result the simulated :ctype:`Capsule` objects created by :file:`capsulethunk.h`
behave slightly differently from real Capsules. Specifically:
* The name parameter passed in to :cfunc:`PyCapsule_New` is ignored.
* The name parameter passed in to :cfunc:`PyCapsule_IsValid` and
:cfunc:`PyCapsule_GetPointer` is ignored, and no error checking
of the name is performed.
* :cfunc:`PyCapsule_GetName` always returns NULL.
* :cfunc:`PyCapsule_SetName` always throws an exception and
returns failure. (Since there's no way to store a name
in a CObject, noisy failure of :cfunc:`PyCapsule_SetName`
was deemed preferable to silent failure here. If this is
inconveient, feel free to modify your local
copy as you see fit.)
You can find :file:`capsulethunk.h` in the Python source distribution
in the :file:`Doc/includes` directory. We also include it here for
your reference; here is :file:`capsulethunk.h`:
.. literalinclude:: ../includes/capsulethunk.h
Other options
=============

134
Doc/includes/capsulethunk.h Normal file
View File

@ -0,0 +1,134 @@
#ifndef __CAPSULETHUNK_H
#define __CAPSULETHUNK_H
#if ( (PY_VERSION_HEX < 0x02070000) \
|| ((PY_VERSION_HEX >= 0x03000000) \
&& (PY_VERSION_HEX < 0x03010000)) )
#define __PyCapsule_GetField(capsule, field, default_value) \
( PyCapsule_CheckExact(capsule) \
? (((PyCObject *)capsule)->field) \
: (default_value) \
) \
#define __PyCapsule_SetField(capsule, field, value) \
( PyCapsule_CheckExact(capsule) \
? (((PyCObject *)capsule)->field = value), 1 \
: 0 \
) \
#define PyCapsule_Type PyCObject_Type
#define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))
#define PyCapsule_IsValid(capsule, name) (PyCObject_Check(capsule))
#define PyCapsule_New(pointer, name, destructor) \
(PyCObject_FromVoidPtr(pointer, destructor))
#define PyCapsule_GetPointer(capsule, name) \
(PyCObject_AsVoidPtr(capsule))
/* Don't call PyCObject_SetPointer here, it fails if there's a destructor */
#define PyCapsule_SetPointer(capsule, pointer) \
__PyCapsule_SetField(capsule, cobject, pointer)
#define PyCapsule_GetDestructor(capsule) \
__PyCapsule_GetField(capsule, destructor)
#define PyCapsule_SetDestructor(capsule, dtor) \
__PyCapsule_SetField(capsule, destructor, dtor)
/*
* Sorry, there's simply no place
* to store a Capsule "name" in a CObject.
*/
#define PyCapsule_GetName(capsule) NULL
static int
PyCapsule_SetName(PyObject *capsule, const char *unused)
{
unused = unused;
PyErr_SetString(PyExc_NotImplementedError,
"can't use PyCapsule_SetName with CObjects");
return 1;
}
#define PyCapsule_GetContext(capsule) \
__PyCapsule_GetField(capsule, descr)
#define PyCapsule_SetContext(capsule, context) \
__PyCapsule_SetField(capsule, descr, context)
static void *
PyCapsule_Import(const char *name, int no_block)
{
PyObject *object = NULL;
void *return_value = NULL;
char *trace;
size_t name_length = (strlen(name) + 1) * sizeof(char);
char *name_dup = (char *)PyMem_MALLOC(name_length);
if (!name_dup) {
return NULL;
}
memcpy(name_dup, name, name_length);
trace = name_dup;
while (trace) {
char *dot = strchr(trace, '.');
if (dot) {
*dot++ = '\0';
}
if (object == NULL) {
if (no_block) {
object = PyImport_ImportModuleNoBlock(trace);
} else {
object = PyImport_ImportModule(trace);
if (!object) {
PyErr_Format(PyExc_ImportError,
"PyCapsule_Import could not "
"import module \"%s\"", trace);
}
}
} else {
PyObject *object2 = PyObject_GetAttrString(object, trace);
Py_DECREF(object);
object = object2;
}
if (!object) {
goto EXIT;
}
trace = dot;
}
if (PyCObject_Check(object)) {
PyCObject *cobject = (PyCObject *)object;
return_value = cobject->cobject;
} else {
PyErr_Format(PyExc_AttributeError,
"PyCapsule_Import \"%s\" is not valid",
name);
}
EXIT:
Py_XDECREF(object);
if (name_dup) {
PyMem_FREE(name_dup);
}
return return_value;
}
#endif /* #if PY_VERSION_HEX < 0x02070000 */
#endif /* __CAPSULETHUNK_H */