# 확장 모듈을 파이썬 3에 이식하기

저자

  Benjamin Peterson

요약

C-API를 변경하는 것이 파이썬 3의 목표 중 하나는 아니었지만, 파이썬 수준의 많은 변경으로 인해 파이썬 2의 API를 그대로 남겨두는 것은 불가능했습니다. 사실, `int()`와 `long()` 통합과 같은 일부 변경 사항은 C 수준에서 더 분명합니다. 이 문서는 비 호환성을 기록으로 남기고 그 문제를 해결하는 방법을 설명합니다.

## 조건부 컴파일

어떤 코드를 파이썬 3에서만 컴파일하는 가장 쉬운 방법은 `PY_MAJOR_VERSION`이 3 이상인지 확인하는 것입니다.

    #if PY_MAJOR_VERSION >= 3
    #define IS_PY3K
    #endif

존재하지 않는 API 함수는 조건 블록 내에서 동등한 것으로 별칭을 만들 수 있습니다.

## 객체 API의 변경 사항

파이썬 3은 비슷한 기능을 가진 일부 형을 병합하면서 다른 것들은 깨끗하게 분리합니다.

### str/unicode 통합

파이썬 3의 `str()` 형은 파이썬 2의 `unicode()`와 동등합니다; C 함수는 모두 `PyUnicode_*`입니다. 이전의 8비트 문자열형은 `bytes()`가 되었고, C 함수는 `PyBytes_*`가 되었습니다. 파이썬 2.6 이상은 호환성 헤더 `bytesobject.h`를 제공하며, `PyBytes` 이름을 `PyString` 으로 매핑합니다. 파이썬 3과의 최상의 호환성을 위해, 텍스트 데이터에는 `PyUnicode`를, 바이너리 데이터에는 `PyBytes`를 사용해야 합니다. 파이썬 3의 `PyBytes`와 `PyUnicode`가 파이썬 2의 `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;
    }

### long/int 통합

파이썬 3은 오직 하나의 정수형 `int()`만을 가집니다. 그러나 이것은 실제로 파이썬 2의 `long()` 형에 해당합니다 - 파이썬 2에서 사용된 `int()` 형은 제거되었습니다. C-API에서, `PyInt_*` 함수는 동등한 `PyLong_*` 함수로 대체됩니다.

## 모듈 초기화와 상태

파이썬 3에는 개선된 확장 모듈 초기화 시스템이 있습니다. (PEP 3121를 참조하십시오.) 모듈 상태를 전역에 저장하는 대신, 인터프리터별 구조체에 저장해야 합니다. 파이썬 2와 파이썬 3 모두에서 올바르게 동작하는 모듈을 만드는 것은 까다 롭습니다. 다음의 간단한 예제는 방법을 보여줍니다.

    #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` 객체는 `CObject`를 대체하기 위해 파이썬 3.1과 2.7에 도입되었습니다. CObject는 유용했지만, `CObject` API는 문제가 있었습니다: 유효한 CObject를 구분할 수 없어서 불일치한 CObject가 인터프리터에 충돌을 일으킬 수 있도록 했고, 일부 API는 C의 정의되지 않은 동작에 의존했습니다. (캡슐을 뒷받침하는 이유에 대한 자세한 내용은 bpo-5630를 참조하십시오.)

현재 CObject를 사용하고 있고, 3.1 이상으로 이전하려고 한다면, 캡슐로 전환해야 합니다. `CObject`는 3.1과 2.7에서 폐지되었고, 파이썬 3.2에서 완전히 제거되었습니다. 2.7이나 3.1 이상만 지원한다면, 단순히 `Capsule`로 전환할 수 있습니다. 파이썬 3.0이나 2.7 이전의 파이썬 버전을 지원해야 한다면, CObject와 캡슐을 모두 지원해야 합니다. (파이썬 3.0은 더는 지원되지 않으며 프로덕션 용도로 권장되지 않음에 유의하십시오.)

다음 예제 헤더 파일 `capsulethunk.h`는 문제를 해결할 수 있습니다. `Capsule` API로 코드를 작성하고, `Python.h` 다음에 이 헤더 파일을 포함하십시오. 여러분의 코드는 자동으로 캡슐이 있는 파이썬 버전에서 캡슐을 사용하고, 캡슐을 사용할 수 없을 때 CObject로 전환합니다.

`capsulethunk.h`는 CObject를 사용하여 캡슐을 시뮬레이션합니다. 그러나, `CObject`는 캡슐의 “이름(name)”을 저장할 장소를 제공하지 않습니다. 결과적으로 `capsulethunk.h`가 만든 시뮬레이트 된 `Capsule` 객체는 실제 캡슐과 약간 다르게 동작합니다. 구체적으로:

> - `PyCapsule_New()`에 전달된 name 매개 변수는 무시됩니다.

> - `PyCapsule_IsValid()` 와 `PyCapsule_GetPointer()`에 전달된 name 매개 변수는 무시되며, name에 대한 에러 검사는 수행되지 않습니다.

> - `PyCapsule_GetName()`은 항상 NULL을 반환합니다.

> - `PyCapsule_SetName()`은 항상 예외를 발생시키고 실패를 반환합니다. (CObject에 이름을 저장할 수 있는 방법이 없으므로, 여기서 조용한 실패보다 요란한 `PyCapsule_SetName()`의 실패를 선호합니다. 이것이 불편하다면, 적절하다고 생각하는 대로 로컬 복사본을 자유롭게 수정하십시오.)

`capsulethunk.h`는 파이썬 소스 배포판의 Doc/includes/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을 고려하십시오. 파이썬과 비슷한 언어를 C로 변환합니다. 만들어진 확장 모듈은 파이썬 3과 파이썬 2 모두와 호환됩니다.