1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
..
Copyright (c) 2015, Red Hat, Inc. and/or its affiliates
Licensed under CC-BY-SA-3.0; see the license file
.. highlight:: c
.. index::
double: Porting; PyTypeObject
=======================
Porting extension types
=======================
The extension type structure, PyTypeObject, has seen some changes in Python 3.
You might wish to refresh your memory with the Python documentation on this
(:ref:`Python 2 <py2:type-structs>`, :ref:`Python 3 <py3:type-structs>`);
here we concentrate only on the differences.
.. _tpflags:
Type Flags
==========
The most common incompatibility in type definition involves feature flags
like :data:`Py_TPFLAGS_HAVE_WEAKREFS` and :data:`Py_TPFLAGS_HAVE_ITER`
(see :ref:`Type flags reference <tpflags_ref>` for a full list).
These flags indicate capabilities that are always present in Python 3,
so the macros are only available in Python 2.
Most projects can simply define these to 0 in Python 3.
However, another use of the macros is feature checking, as in
:c:func:`PyType_HasFeature(cls, Py_TPFLAGS_HAVE_ITER) <py2:PyType_HasFeature>`.
Defining the flags to ``0`` would cause that test to fail under Python 3,
where it should instead always succeed!
So, in these cases, the check should be done as
``(IS_PY3 || PyType_HasFeature(cls, Py_TPFLAGS_HAVE_ITER))``.
If your project does not use ``PyType_HasFeature``, or bypasses the check under
Python 3 as above, you can include ``<py3c/tpflags.h>`` to define missing type
flags as 0.
PyTypeObject
============
The differences in PyTypeObject itself are fairly minor.
The ``tp_compare`` field became ``void *tp_reserved``, and is ignored.
If you use ``tp_richcompare``, this field is ignored in Python 2.
It is best set to NULL.
The change can case trouble if you use explicity types during definition
for type safety, as in::
...
(destructor)Example_dealloc, /* tp_dealloc */
(printfunc)0, /* tp_print */
(getattrfunc)0, /* tp_getattr */
(setattrfunc)0, /* tp_setattr */
(cmpfunc)0, /* tp_compare */
...
In this case, make an exception for tp_compare, and use just ``NULL``.
Python 3 also adds new fields at the end of PyTypeObject – but that should
not affect initialization.
.. index::
double: Porting; PyNumberMethods
PyNumberMethods
===============
The ``PyNumberMethods`` structure, used to implement number-like behavior
and operators, was changed.
(Docs: :c:type:`py2 <py2:PyNumberMethods>`, :c:type:`py3 <py3:PyNumberMethods>`)
Specifically, several members were removed:
* ``nb_divide`` (Python3 calls ``nb_floor_divide`` or ``nb_true_divide``)
* ``nb_coerce``
* ``nb_oct``
* ``nb_hex``
* ``nb_inplace_divide`` (see nb_divide)
one was renamed:
* ``nb_nonzero`` became ``nb_bool``
and one was blanked:
* ``unaryfunc nb_long`` became ``void *nb_reserved`` and must be NULL.
The mix of removal strategies on the CPython side makes the port somewhat
annoying.
As of yet, the py3c library does not provide helpers for porting
PyNumberMethods. More investigation is needed to be sure all projects' needs
are addressed.
What you need to do depends on your initialization style:
C89/C++ style
.............
This style is compatible both with older C compilers and with C++::
static PyNumberMethods long_as_number = {
(binaryfunc)long_add, /*nb_add*/
(binaryfunc)long_sub, /*nb_subtract*/
(binaryfunc)long_mul, /*nb_multiply*/
(binaryfunc)long_div, /*nb_divide*/
long_mod, /*nb_remainder*/
long_divmod, /*nb_divmod*/
long_pow, /*nb_power*/
(unaryfunc)long_neg, /*nb_negative*/
...
When using this, wrap the removed elements in ``#if !IS IS_PY3``.
If you use nb_long in Python 2, conditionally set it to NULL in Python 3.
Make sure ``nb_int`` is set.
C99 style
.........
If you don't support both C89 and C++ (!) compilers, you may
use the named member initialization feature of C99::
static PyNumberMethods long_as_number = {
.tp_add = long_add,
.tp_div = long_div,
...
If this is the case, lump the non-NULL Python2-only members and ``nb_long``
together in a single ``#if !IS IS_PY3`` block.
You will need another ``#if``/``#else`` block to handle to handle both names
of ``nb_nonzero``, if using that.
.. index::
double: Porting; PyBufferProcs
PyBufferProcs
=============
The buffer protocol changed significantly in Python 3.
Kindly read the :ref:`documentation <py3:bufferobjects>`, and implement
the new buffer protocol for Python 3.
If you find an easier way to port buffer-aware objects,
which other projects could benefit from,
remember that py3c welcomes contributions.
|