Как исправить "системную ошибку":возвратил NULL без установки ошибки "в расширении Python C - PullRequest
0 голосов
/ 29 декабря 2018

Инструменты: Python3.7 (64 бит), Visual C ++ 10.0 Я пытаюсь создать расширение C для Python.Для начала я тестирую простой код C, который печатает строку и вызывает функцию Sleep () внутри цикла for .Однако, когда я делаю простой вызов этой функции C с именем gen_nums из Python, я получаю следующую ошибку:

"SystemError: встроенная функция gen_nums возвратила NULL без установки ошибки"

Мне кажется, проблема в функции Sleep ();удаление части «Sleep (1000)» или размещение ее перед «printf (« Printed from C thread ... \ n »)» устраняет эту ошибку.Я просмотрел документацию по Sleep (), но не смог найти ничего полезного.

C Код:

#include <Python.h>

static void gen_nums() {
    int i;
    for(i = 0; i < 10; i++) {
        printf("Printed from C thread...\n");
        Sleep(1000);
    }
}
static PyMethodDef gen_numsmethods[] = {
    {"gen_nums", gen_nums, METH_VARARGS, "This is a threading test"},
    {NULL, NULL, 0, NULL}
};
static struct PyModuleDef threadmod = {
    PyModuleDef_HEAD_INIT,
    "threadrun",
    "This is a thread test module",
    -1,
    gen_numsmethods
};

PyMODINIT_FUNC PyInit_threadrun(void) {
    return PyModule_Create(&threadmod);
}

Вызов Python:

threadrun.gen_nums() \\ the C module is called threadrun

Результат долженbe: «Отпечатано из потока C ...» 10 раз с интервалом в 1 секунду между каждым оператором.

Однако программа печатает инструкцию 10 раз, а затем отображает вышеупомянутую ошибку.

1 Ответ

0 голосов
/ 29 декабря 2018

Причина ошибки заключается в следующем: функции расширения Python должны иметь определенный прототип C:

PyObject *func(PyObject *self, PyObject *args)

Слоты методов содержат указатели на функции типа

PyObject *(*)(Pyobject *, PyObject *)

Старый способ был принудительно приводить функцию к этому типу указателя для сохранения в слоте метода.Явное приведение отключит ошибку преобразования void (*)() в PyObject *(*)(Pyobject *, PyObject *).Преобразование допустимо, но нуждается в явном приведении. Если явного преобразования нет, то компилятор C должен выдать диагностическое сообщение .

Ваш код не имеет явного преобразования, поэтому вы должны получить предупреждение для

{"gen_nums", gen_nums, METH_VARARGS, "This is a threading test"},

В любом случае, , если было явное приведение , программабудет по-прежнему правильной программой до тех пор, пока Python не попытается вызвать вашу функцию gen_nums(), потому что Python сделает так , как если бы ее прототип был

PyObject *gen_nums(PyObject *, PyObject *);

Теперь стандарт C говорит, что хотя до этого момента все было в порядке , с этого момента поведение программы равно undefined , потому что C11 6.3.2.3 :

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

Ваша функция возвращает void, то есть вообще ничего, но вы спрашиваете "почему он возвращает NULL только тогда, когда там Sleep(). Причина -" неопределенное поведение ".


Что касается того, как исправьте это , пожалуйста, прочитайте и поймите Глава 1 из 1. Расширение Python с помощью C или C ++ - там много подробностей, но все, что нужно для исправления этой простой функции, подробнотам. Если вы застряли, пожалуйста, do задайте дополнительные вопросы, но обратитесь к документации в вопросах.

Исправление для в том, что функция должна была бы записать ее как

static PyObject *gen_nums(PyObject *self, PyObject *args) {
    int i;
    for(i = 0; i < 10; i++) {
        printf("Printed from C thread...\n");
        Sleep(1000);
    }
    Py_RETURN_NONE;
}
...