Использование нового типа в расширении Python C

Я написал код для определения нового типа в расширении Python C (MyStatus). Я написал код C для определения распределения, освобождения и т. д., как указано на этой странице.

Я смог скомпилировать модуль и использовать его из python.

Теперь я пытаюсь использовать этот новый тип в другом расширении Python C (TestStatus). Мое требование: для этого мне нужен только один .so. Я не хочу использовать MyStatus напрямую из кода Python. Я буду только импортировать TestStatus в свой код, и я хочу инициализировать MyStatus из моего расширения C, написанного для TestStatus.

Я написал такой код для TestStatus

static PyObject * TestStatus_checkPyObject *self, PyObject *args)
{
    PyObject * mystatus = NULL;
    const char *command;

    /* Call the class object. */
    mystatus = PyObject_CallObject((PyObject *) &MyStatusType, NULL);

    return mystatus;
}

    PyMODINIT_FUNC initTestStatus(void)
    {
        (void) Py_InitModule("TestStatus", TestMethods);

        initMyStatus();//This is available in the C code written for MyStatus
    }

Я смог создать так, как я упомянул в коде. Но я застрял в настройке переменных для MyStatus, который является целым числом, и char * (PyObject *). Может ли кто-нибудь пролить свет на это, например, правильный ли мой подход и как инициализировать и использовать MyStatus из TestStatus с аргументами.

Я пытаюсь это сделать с Python 2.6.6 на Rhel 6.3.

В MyStatus у меня есть 2 переменные

typedef struct {
    PyObject_HEAD
    int         mStatus;
    PyObject    *mErrorString;
} MyStatus;

Мне нужно инициализировать то же самое из TestStatus.


person Raghuram    schedule 22.11.2013    source источник
comment
Вы застряли на установке переменных, какие переменные? можно поконкретнее, это помогло бы. Почему требуется иметь только .so файл? Будет ли работать, если вы поместите код обоих модулей в один файл? - Извините, но я так и не понял проблему, поэтому сложно что-то в ней прояснить.   -  person dastrobu    schedule 25.11.2013
comment
Я отредактировал сообщение, чтобы добавить информацию о переменных.   -  person Raghuram    schedule 26.11.2013


Ответы (1)


Расширение Python C должно предоставлять C-API для использования из других модулей C. Так что в вашем случае у вас должно быть что-то вроде MyStatus.h

/* Header file for MyStatus module */

#ifndef MyStatus_MODULE_H
#define MyStatus_MODULE_H
#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    PyObject_HEAD
    int         mStatus;
    PyObject    *mErrorString;
} MyStatus;

#define MyStatus_Type_NUM 0

#define MyStatus_New_NUM 1
#define MyStatus_New_RETURN MyStatus *
#define MyStatus_New_PROTO (int mStatus, PyObject *mErrorString)

/* Total number of C API pointers */
#define MyStatus_API_pointers 2

#ifdef MyStatus_MODULE
/* do nothing for this minimal example */
#else

static void **MyStatus_API;

#define MyStatus_Type (*(PyTypeObject *)(\
    MyStatus_API[MyStatus_Type_NUM]))

#define MyStatus_New \
 (*(MyStatus_New_RETURN (*)MyStatus_New_PROTO) \
  MyStatus_API[MyStatus_New_NUM])

static int import_MyStatus(void)
{
    MyStatus_API = (void **)PyCapsule_Import("MyStatus._C_API", 0);
    return (MyStatus_API != NULL) ? 0 : -1;
}
#endif /* !defined(MyStatus_MODULE) */
#ifdef __cplusplus
}
#endif
#endif /* !defined(MyStatus_MODULE_H) */

и определить что-то вроде

static Py MyStatus *
PyMyStatus_New(int mStatus, PyObject *mErrorString){
    MyStatus *self;
    self = (MyStatus *)MyStatusType.tp_alloc(&MyStatusType, 0);
    self->mStatus = mStatus;
    Py_INCREF(mErrorString); // in case you don't want to steal a reference
    self->mErrorString = mErrorString;
    if (!self->mErrorString){
        Py_DECREF(self);
        return NULL;
    }
    return self;
}

#ifndef PyMODINIT_FUNC  /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif PyMODINIT_FUNC initMyStatus(void){
    PyObject *m = Py_InitModule3("MyStatus", methods, "");
    static void *MyStatus_API[MyStatus_API_pointers];

    MyStatus_API[MyStatus_Type_NUM] = (void *)&MyStatusType;
    MyStatus_API[MyStatus_New_NUM] = (void *)MyStatus_New;

    PyObject *c_api_object = PyCapsule_New((void *)MyStatus_API, "MyStatus._C_API", NULL);
    if (c_api_object != NULL) PyModule_AddObject(m, "_C_API", c_api_object); }

в MyStauts.c. Кроме того, очень удобно определять макросы типа

PyMyStatus_SET_MSTATUS(self, mStatus)
PyMyStatus_GET_MSTATUS(self)
PyMyStatus_SET_mErrorString(self, mErrorString)
PyMyStatus_GET_mErrorString(self)

чтобы впоследствии можно было изменить базовую структуру MyStatus и управлять подсчетом ссылок.

Если вы не хотите этого делать, вы всегда можете напрямую инициализировать и изменять объекты MyStatus, как показано в верхнем примере для функции MyStatus_New.

В TestStatus.c, наконец, импортируйте C-API.

PyMODINIT_FUNC initTestStatus(void){
    import_MyStatus();
    Py_InitModule3("TestStatus", methods, "");

}

Теперь вы сможете использовать MyStatus_New и MyStatusType.

person dastrobu    schedule 26.11.2013
comment
У меня есть этот код как часть MyStatus. Но я сомневаюсь, как мне вызвать это из TestStatus. Как видите, я вызываю initMyStatus. Но в методах TestStatus я должен инициализировать объект MyStatus, инициализируя его строкой ошибки и статусом. Вот где я застрял - person Raghuram; 28.11.2013
comment
Я понимаю. Я думаю, проблема в том, что вы пытаетесь позвонить initMyStatus(); в initTestStatus. Вам нужно вызвать import_MyStatus();, который вы должны определить в модуле MyStatus, чтобы импортировать C-API. Затем вы можете вызывать функции из MyStatus в TestStatus и использовать тип, если вы экспортировали его через C-API. - person dastrobu; 28.11.2013
comment
Попробую это и дам вам знать ... Спасибо за ваш вклад - person Raghuram; 02.12.2013
comment
Была ли ваша попытка успешной? - person dastrobu; 06.12.2013
comment
Я был в отпуске... так что нужно попробовать на этой неделе... скоро сообщу - person Raghuram; 09.12.2013
comment
Для этого мне нужно использовать Python 2.6, и когда я пытался выполнить это, я получал ошибку компоновщика, что PyCapsule_New не определен. - person Raghuram; 09.12.2013
comment
да, для python 2.6 экспорт C-API немного длиннее (см. docs), но его можно напрямую перенести из примера в документации в ваш модуль. - person dastrobu; 09.12.2013
comment
У меня есть одна проблема. По ссылке я могу скомпилировать модуль. После этого я обнаружил, что MyStatus нужен как модуль Python, присутствующий в каталоге lib python. Я не хочу этого в таком виде. Мне нужен один модуль, и я не могу выставлять MyStaus за пределы моего модуля. - person Raghuram; 09.12.2013
comment
Вы должны иметь возможность поместить все в один файл, определение типа MyStatus и типа TestStatus. В этом случае вам даже не нужно явно импортировать C-API. Однако способ создания и взаимодействия с объектами MyStatus должен оставаться прежним, например. через PyMyStatus_New. Чего я не понимаю, так это почему MyStatus должен быть типом python, если вы не хотите подвергать его воздействию python. В этом случае я бы просто использовал обычные структуры и функции C вместо того, чтобы иметь дело со сложным C-API Python. - person dastrobu; 11.12.2013
comment
Позвольте мне потратить некоторое время на это .. и вернуться - person Raghuram; 12.12.2013
comment
Мне нужно также открыть MyStatus для python. Но я не хочу, чтобы пользователь также импортировал MyStatus как отдельный модуль или создавал для этого новый. Возврат из TestStatus — это MyStatus, и пользователь должен использовать MyStatus таким образом. - person Raghuram; 31.01.2014