diff --git a/Doc/howto/cporting.rst b/Doc/howto/cporting.rst --- a/Doc/howto/cporting.rst +++ b/Doc/howto/cporting.rst @@ -209,6 +209,185 @@ } +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 ``"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. + +``"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 ``"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. + +Here is ``"capsulethunk.h"``:: + + #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 */ + + Other options =============