拡張モジュールのPython3への移植
- 著者
- ベンジャミン・ピーターソン
概要
C-APIの変更はPython3の目的の1つではありませんでしたが、Pythonレベルの多くの変更により、Python2のAPIをそのままにすることは不可能でした。 実際、 int()やlong()
の統合などのいくつかの変更は、経営幹部レベルでより明白です。 このドキュメントは、非互換性とそれらを回避する方法を文書化するように努めています。
条件付きコンパイル
Python 3の一部のコードのみをコンパイルする最も簡単な方法は、PY_MAJOR_VERSION
が3以上であるかどうかを確認することです。
#if PY_MAJOR_VERSION >= 3
#define IS_PY3K
#endif
存在しないAPI関数は、条件付きブロック内の同等の関数にエイリアスすることができます。
オブジェクトAPIの変更
Python 3は、いくつかのタイプを同様の機能でマージし、他のタイプを明確に分離しました。
str / unicode統一
Python3の str()タイプは、Python2のunicode()
と同等です。 C関数は両方ともPyUnicode_*
と呼ばれます。 古い8ビット文字列型は bytes()になり、C関数はPyBytes_*
と呼ばれます。 Python 2.6以降では、互換性ヘッダーbytesobject.h
が提供され、PyBytes
の名前がPyString
の名前にマッピングされます。 Python 3との互換性を最大限に高めるには、テキストデータにはPyUnicode
を使用し、バイナリデータにはPyBytes
を使用する必要があります。 Python3のPyBytes
とPyUnicode
は、Python2のPyString
とPyUnicode
のように互換性がないことを覚えておくことも重要です。 次の例は、PyUnicode
、PyString
、およびPyBytes
に関するベストプラクティスを示しています。
#include "stdlib.h"
#include "Python.h"
#include "bytesobject.h"
/* text example */
static PyObject *
say_hello(PyObject *self, PyObject *args) {
PyObject *name, *result;
if (!PyArg_ParseTuple(args, "U:say_hello", &name))
return NULL;
result = PyUnicode_FromFormat("Hello, %S!", name);
return result;
}
/* just a forward */
static char * do_encode(PyObject *);
/* bytes example */
static PyObject *
encode_object(PyObject *self, PyObject *args) {
char *encoded;
PyObject *result, *myobj;
if (!PyArg_ParseTuple(args, "O:encode_object", &myobj))
return NULL;
encoded = do_encode(myobj);
if (encoded == NULL)
return NULL;
result = PyBytes_FromString(encoded);
free(encoded);
return result;
}
モジュールの初期化と状態
Python 3には、改良された拡張モジュール初期化システムがあります。 ( PEP 3121 を参照してください。)モジュールの状態をグローバルに格納する代わりに、インタープリター固有の構造に格納する必要があります。 Python2とPython3の両方で正しく機能するモジュールを作成するのは難しいです。 次の簡単な例は、その方法を示しています。
#include "Python.h"
struct module_state {
PyObject *error;
};
#if PY_MAJOR_VERSION >= 3
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
#else
#define GETSTATE(m) (&_state)
static struct module_state _state;
#endif
static PyObject *
error_out(PyObject *m) {
struct module_state *st = GETSTATE(m);
PyErr_SetString(st->error, "something bad happened");
return NULL;
}
static PyMethodDef myextension_methods[] = {
{"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},
{NULL, NULL}
};
#if PY_MAJOR_VERSION >= 3
static int myextension_traverse(PyObject *m, visitproc visit, void *arg) {
Py_VISIT(GETSTATE(m)->error);
return 0;
}
static int myextension_clear(PyObject *m) {
Py_CLEAR(GETSTATE(m)->error);
return 0;
}
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"myextension",
NULL,
sizeof(struct module_state),
myextension_methods,
NULL,
myextension_traverse,
myextension_clear,
NULL
};
#define INITERROR return NULL
PyMODINIT_FUNC
PyInit_myextension(void)
#else
#define INITERROR return
void
initmyextension(void)
#endif
{
#if PY_MAJOR_VERSION >= 3
PyObject *module = PyModule_Create(&moduledef);
#else
PyObject *module = Py_InitModule("myextension", myextension_methods);
#endif
if (module == NULL)
INITERROR;
struct module_state *st = GETSTATE(module);
st->error = PyErr_NewException("myextension.Error", NULL, NULL);
if (st->error == NULL) {
Py_DECREF(module);
INITERROR;
}
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}
CObjectがCapsuleに置き換えられました
Capsule
オブジェクトは、CObject
を置き換えるためにPython3.1および2.7で導入されました。 CObjectは便利でしたが、CObject
APIには問題がありました。有効なCObjectを区別できなかったため、不一致のCObjectがインタープリターをクラッシュさせ、一部のAPIはCの未定義の動作に依存していました。 (カプセルの背後にある理論的根拠の詳細については、:issue: `5630` を参照してください。)
現在CObjectsを使用していて、3.1以降に移行する場合は、Capsulesに切り替える必要があります。 CObject
は3.1および2.7で非推奨になり、Python3.2で完全に削除されました。 2.7または3.1以降のみをサポートしている場合は、Capsule
に切り替えるだけです。 Python 3.0、または2.7より前のバージョンのPythonをサポートする必要がある場合は、CObjectsとCapsulesの両方をサポートする必要があります。 (Python 3.0はサポートされなくなったため、本番環境での使用はお勧めしません。)
次のサンプルヘッダーファイルcapsulethunk.h
は、問題を解決する可能性があります。 Capsule
APIに対してコードを記述し、Python.h
の後にこのヘッダーファイルをインクルードするだけです。 コードは、Capsulesを備えたPythonのバージョンでCapsulesを自動的に使用し、Capsulesが使用できない場合はCObjectsに切り替えます。
capsulethunk.h
は、CObjectを使用してカプセルをシミュレートします。 ただし、CObject
にはカプセルの「名前」を保存する場所がありません。 その結果、capsulethunk.h
によって作成されたシミュレートされたCapsule
オブジェクトは、実際のカプセルとは少し異なる動作をします。 具体的には:
- PyCapsule_New()に渡されたnameパラメーターは無視されます。
- PyCapsule_IsValid()および PyCapsule_GetPointer()に渡された名前パラメーターは無視され、名前のエラーチェックは実行されません。
- PyCapsule_GetName()は常にNULLを返します。
- PyCapsule_SetName()は常に例外を発生させ、失敗を返します。 (CObjectに名前を格納する方法がないため、ここでは PyCapsule_SetName()のノイズの多い失敗がサイレント失敗よりも好ましいと見なされました。 これが不便な場合は、必要に応じてローカルコピーを自由に変更してください。)
capsulethunk.h
は、Pythonソースディストリビューションで:source: `Doc / contains / 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 */
別のオプション
新しい拡張モジュールを作成している場合は、 Cython を検討してください。 Pythonのような言語をCに変換します。 それが作成する拡張モジュールは、Python3およびPython2と互換性があります。