Как вернуть Python Enum из C расширения? - PullRequest
2 голосов
/ 27 апреля 2020

Я пишу расширение C для Python, которое оборачивает библиотеку C. Библиотека C имеет несколько типов enum, и я написал соответствующие IntEnum s для них, например:

from enum import IntEnum
# _enum_consts is a C extension creating Python
# constants for the library's enum constants
from . import _enum_consts

class Align(IntEnum):
    DEFAULT = _enum_consts.MD_ALIGN_DEFAULT
    LEFT = _enum_consts.MD_ALIGN_LEFT
    CENTER = _enum_consts.MD_ALIGN_CENTER
    RIGHT = _enum_consts.MD_ALIGN_RIGHT

Теперь в расширении C, где я упаковываю библиотеку Основная функциональность, некоторые из библиотечных функций возвращают типы перечислений, и я хочу преобразовать их в значения Python IntEnum. (Я понимаю, что мог бы просто вернуть int с напрямую, и они успешно сравнились бы с IntEnum с, но это побеждает цель.)

Вопрос в том, как мне создать IntEnum значения для возврата? Все, что я могу найти, это примеры, где константы перечисления C преобразуются в Python константы с PyModule_AddIntConstant() (это то, что делает модуль расширения _enum_consts).

1 Ответ

1 голос
/ 27 апреля 2020

Прежде всего, модуль с IntEnum необходимо импортировать в модуль расширения (если вы этого еще не сделали). В функции инициализации модуля:

PyMODINIT_FUNC PyInit_someextension(void)
{
    // ...

    PyObject *m;
    m = PyModule_Create(&someextension_module);
    if (m == NULL) {
        return NULL;
    }

    // ...

    // Import the enums module, where "somepackage.enums"
    // is the full name of the enums module
    PyObject *enums = PyImport_ImportModule("somepackage.enums");
    if (enums == NULL) {
        Py_DECREF(m);
        return NULL;
    }
    Py_DECREF(enums);

    return m;
}

Затем, где вы хотите создать экземпляр значения IntEnum, сначала извлеките модуль, в котором он находится снова:

PyObject *enums = PyImport_AddModule("somepackage.enums");
if (enums == NULL) {
    // Return NULL, -1, etc. to signal an exception
}

Сразу после извлечения вашего IntEnum type (Align в данном случае) из модуля:

PyObject *align_enum = PyObject_GetAttrString(enums, "Align");
if (align_enum == NULL) {
    // Return NULL, -1, etc. to signal an exception
}

Затем вы можете создать нужный вам экземпляр. align здесь будет значение для извлечения из IntEnum. Мы делаем это, вызывая объект типа, как в обычном Python (обратите внимание, что PyObject_CallFunction на самом деле не ограничивается вызовом функций - он может вызывать любой вызываемый объект):

PyObject *align_instance = PyObject_CallFunction(align_enum, "(i)", align);
if (align_instance == NULL) {
    Py_DECREF(align_enum);
    // Return NULL, -1, etc. to signal an exception
}

Теперь align_instance содержит экземпляр IntEnum для возврата. Обязательно Py_DECREF(align_enum) перед возвращением.

...