diff options
-rw-r--r-- | Doc/howto/cporting.rst | 111 | ||||
-rw-r--r-- | Doc/includes/capsulethunk.h | 134 |
2 files changed, 219 insertions, 26 deletions
diff --git a/Doc/howto/cporting.rst b/Doc/howto/cporting.rst index 7184496..98db9dd 100644 --- a/Doc/howto/cporting.rst +++ b/Doc/howto/cporting.rst @@ -2,27 +2,28 @@ .. _cporting-howto: -******************************** -Porting Extension Modules to 3.0 -******************************** +************************************* +Porting Extension Modules to Python 3 +************************************* :author: Benjamin Peterson .. topic:: Abstract - Although changing the C-API was not one of Python 3.0's objectives, the many - Python level changes made leaving 2.x's API intact impossible. In fact, some - changes such as :func:`int` and :func:`long` unification are more obvious on - the C level. This document endeavors to document incompatibilities and how - they can be worked around. + Although changing the C-API was not one of Python 3's objectives, + the many Python-level changes made leaving Python 2's API intact + impossible. In fact, some changes such as :func:`int` and + :func:`long` unification are more obvious on the C level. This + document endeavors to document incompatibilities and how they can + be worked around. Conditional compilation ======================= -The easiest way to compile only some code for 3.0 is to check if -:c:macro:`PY_MAJOR_VERSION` is greater than or equal to 3. :: +The easiest way to compile only some code for Python 3 is to check +if :c:macro:`PY_MAJOR_VERSION` is greater than or equal to 3. :: #if PY_MAJOR_VERSION >= 3 #define IS_PY3K @@ -35,7 +36,7 @@ conditional blocks. Changes to Object APIs ====================== -Python 3.0 merged together some types with similar functions while cleanly +Python 3 merged together some types with similar functions while cleanly separating others. @@ -43,14 +44,14 @@ str/unicode Unification ----------------------- -Python 3.0's :func:`str` (``PyString_*`` functions in C) type is equivalent to -2.x's :func:`unicode` (``PyUnicode_*``). The old 8-bit string type has become -:func:`bytes`. Python 2.6 and later provide a compatibility header, +Python 3's :func:`str` (``PyString_*`` functions in C) type is equivalent to +Python 2's :func:`unicode` (``PyUnicode_*``). The old 8-bit string type has +become :func:`bytes`. Python 2.6 and later provide a compatibility header, :file:`bytesobject.h`, mapping ``PyBytes`` names to ``PyString`` ones. For best -compatibility with 3.0, :c:type:`PyUnicode` should be used for textual data and +compatibility with Python 3, :c:type:`PyUnicode` should be used for textual data and :c:type:`PyBytes` for binary data. It's also important to remember that -:c:type:`PyBytes` and :c:type:`PyUnicode` in 3.0 are not interchangeable like -:c:type:`PyString` and :c:type:`PyUnicode` are in 2.x. The following example +:c:type:`PyBytes` and :c:type:`PyUnicode` in Python 3 are not interchangeable like +:c:type:`PyString` and :c:type:`PyUnicode` are in Python 2. The following example shows best practices with regards to :c:type:`PyUnicode`, :c:type:`PyString`, and :c:type:`PyBytes`. :: @@ -94,10 +95,12 @@ and :c:type:`PyBytes`. :: long/int Unification -------------------- -In Python 3.0, there is only one integer type. It is called :func:`int` on the -Python level, but actually corresponds to 2.x's :func:`long` type. In the -C-API, ``PyInt_*`` functions are replaced by their ``PyLong_*`` neighbors. The -best course of action here is using the ``PyInt_*`` functions aliased to +Python 3 has only one integer type, :func:`int`. But it actually +corresponds to Python 2's :func:`long` type--the :func:`int` type +used in Python 2 was removed. In the C-API, ``PyInt_*`` functions +are replaced by their ``PyLong_*`` equivalents. + +The best course of action here is using the ``PyInt_*`` functions aliased to ``PyLong_*`` found in :file:`intobject.h`. The abstract ``PyNumber_*`` APIs can also be used in some cases. :: @@ -120,10 +123,11 @@ can also be used in some cases. :: Module initialization and state =============================== -Python 3.0 has a revamped extension module initialization system. (See -:pep:`3121`.) Instead of storing module state in globals, they should be stored -in an interpreter specific structure. Creating modules that act correctly in -both 2.x and 3.0 is tricky. The following simple example demonstrates how. :: +Python 3 has a revamped extension module initialization system. (See +:pep:`3121`.) Instead of storing module state in globals, they should +be stored in an interpreter specific structure. Creating modules that +act correctly in both Python 2 and Python 3 is tricky. The following +simple example demonstrates how. :: #include "Python.h" @@ -209,10 +213,65 @@ both 2.x and 3.0 is tricky. The following simple example demonstrates how. :: } +CObject replaced with Capsule +============================= + +The :c:type:`Capsule` object was introduced in Python 3.1 and 2.7 to replace +:c:type:`CObject`. CObjects were useful, +but the :c:type:`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. +:c:type:`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 :c:type:`Capsule`. If you need to support Python 3.0, +or versions of Python earlier than 2.7, +you'll have to support both CObjects and Capsules. +(Note that Python 3.0 is no longer supported, and it is not recommended +for production use.) + +The following example header file :file:`capsulethunk.h` may +solve the problem for you. Simply write your code against the +:c:type:`Capsule` API and include this header file after +:file:`Python.h`. Your code will automatically use Capsules +in versions of Python with Capsules, and switch to CObjects +when Capsules are unavailable. + +:file:`capsulethunk.h` simulates Capsules using CObjects. However, +:c:type:`CObject` provides no place to store the capsule's "name". As a +result the simulated :c:type:`Capsule` objects created by :file:`capsulethunk.h` +behave slightly differently from real Capsules. Specifically: + + * The name parameter passed in to :c:func:`PyCapsule_New` is ignored. + + * The name parameter passed in to :c:func:`PyCapsule_IsValid` and + :c:func:`PyCapsule_GetPointer` is ignored, and no error checking + of the name is performed. + + * :c:func:`PyCapsule_GetName` always returns NULL. + + * :c:func:`PyCapsule_SetName` always throws an exception and + returns failure. (Since there's no way to store a name + in a CObject, noisy failure of :c:func:`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 ============= If you are writing a new extension module, you might consider `Cython <http://www.cython.org>`_. It translates a Python-like language to C. The -extension modules it creates are compatible with Python 3.x and 2.x. +extension modules it creates are compatible with Python 3 and Python 2. diff --git a/Doc/includes/capsulethunk.h b/Doc/includes/capsulethunk.h new file mode 100644 index 0000000..6b20564 --- /dev/null +++ b/Doc/includes/capsulethunk.h @@ -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 */ |