Как получить текущее имя функции в Python C-API?

Я реализовал кучу функций, и они отправляются из одной и той же функции C, вызываемой интерпретатором Python:

PyObject *
CmdDispatch(PyObject *self, PyObject *args, PyObject *kwargs)

Неожиданно self равно NULL, и мне нужно получить имя вызываемой в данный момент функции. Есть ли способ получить эту информацию?

У меня есть десятки функций, которые проходят через эту процедуру. Эта команда преобразует все параметры в карту C++ и передает ее реализации каждой команды.

Обновление: http://docs.python.org/extending/extending.html#a-simple-example специально говорит: «Аргумент self указывает на объект модуля для функций уровня модуля; для метода он будет указывать на экземпляр объекта.», но я получаю значение null при связывании с python 2.6.


person Juan    schedule 03.07.2011    source источник
comment
Почему неожиданно?   -  person Ignacio Vazquez-Abrams    schedule 04.07.2011
comment
Потому что в документации API не сказано, что первый аргумент ничего не значит.   -  person Juan    schedule 04.07.2011
comment
Есть много документации по API; вам нужно быть более конкретным.   -  person Ignacio Vazquez-Abrams    schedule 04.07.2011
comment
Основываясь на моем исследовании, я не согласен. Они неспецифичны и отсутствуют   -  person Juan    schedule 04.07.2011
comment
Если вы используете версию 2.6, вам следует ознакомиться с документацией по версии 2.6 (документы .python.org/release/2.6.7/c-api/structures.html), где говорится: «Первый — это объект self для методов; для модульных функций оно имеет значение, присвоенное Py_InitModule4() (или NULL, если использовалось Py_InitModule()).   -  person Samuel    schedule 04.07.2011


Ответы (4)


API Python не предназначен для того, чтобы сообщать вам, какую функцию он вызывает. Вы создали функцию, и она вызывает ее, API предполагает, что вы знаете, какую функцию вы написали. Вам нужно будет создать небольшую функцию-оболочку для каждой из ваших функций Python. Лучший способ сделать это — зарегистрировать одну функцию C как одну функцию Python, которая принимает строку в качестве первого аргумента. Затем на уровне Python создайте столько функций Python, сколько вам нужно, каждая из которых вызывает вашу функцию C с соответствующим строковым аргументом, определяющим, какую функцию вы действительно хотите вызвать.

Другая альтернатива состоит в том, чтобы переосмыслить структуру вашего кода C и иметь столько функций, сколько вам нужно, каждая из которых вызывает ваш общий вспомогательный код для обработки параметров и т. д.

person Ned Batchelder    schedule 04.07.2011
comment
в Интернете есть документация по использованию самоанализа на уровне сценариев для получения имени функции, но не на уровне c-api. - person Juan; 04.07.2011
comment
В итоге я перебрал все функции в модуле и создал оболочку для каждой, передающей имя команды. - person Juan; 04.07.2011

Я пытался решить очень похожую проблему.

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

Вот краткий пример на Python, иллюстрирующий то, чего я хотел достичь (я предполагаю, что Хуан требует аналогичного поведения):

import sys
def foo():
    print('foo:', sys._getframe(0).f_code.co_name)
def bar():
    print('bar:', sys._getframe(0).f_code.co_name)
    foo()
bar()

Вот близкий эквивалент этого примера (на основе Документация по Python 3), но реализованная с помощью Python C API:

// Based on example from Python 3.2.1 documentation, 5.4. Extending Embedded Python
// http://docs.python.org/release/3.2.1/extending/embedding.html#extending-embedded-python
//
#include <Python.h>
#include <frameobject.h>

static void foo()
{
    PyThreadState * ts = PyThreadState_Get();
    PyFrameObject* frame = ts->frame;
    while (frame != 0)
    {
        char const* filename = _PyUnicode_AsString(frame->f_code->co_filename);
        char const* name = _PyUnicode_AsString(frame->f_code->co_name);
        printf("foo: filename=%s, name=%s\n", filename, name);
        frame = frame->f_back;
    }
}

static void bar()
{
    PyRun_SimpleString(
    "import sys\n"
    "print(\"bar: filename=%s, name=%s\" % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_code.co_name))"
    );
}

static PyObject* emb_numargs(PyObject *self, PyObject *args)
{
    foo();
    bar();

    PyRun_SimpleString(
    "import sys\n"
    "print(\"emb_numargs: filename=%s, name=%s\" % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_code.co_name))"
    );

    return PyLong_FromLong(0);
}

static PyMethodDef EmbMethods[] = {
    {"numargs", emb_numargs, METH_VARARGS, "Return number 0"},
    {NULL, NULL, 0, NULL}
};

static PyModuleDef EmbModule = {
    PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,
    NULL, NULL, NULL, NULL
};

static PyObject* PyInit_emb(void)
{
    return PyModule_Create(&EmbModule);
}

int main(int argc, char* argv[])
{
    PyImport_AppendInittab("emb", &PyInit_emb);
    Py_Initialize();
    PyRun_SimpleString(
    "import emb\n"
    "print('This is Zero: ', emb.numargs())\n"
    );
    Py_Finalize();
    return 0;
}

Я надеюсь, что это тоже завершает ответ Неда.

person mloskot    schedule 25.08.2011

УВЕДОМЛЕНИЕ проверка ошибок для API не предусмотрена для ясности;

В этом примере новая функция вставляется непосредственно в модуль Python __builtin__, что позволяет вызывать метод без схемы class.method. Просто измените mymodule на любой другой модуль по вашему желанию.

PyObject* py_cb(PyObject *self, PyObject *args)
{
    const char *name = (const char *) PyCObject_AsVoidPtr(self);

    printf("%s\n", name);
    Py_RETURN_NONE;
}

int main(int argc, char *argv)
{
    PyObject        *mod, *modname, *dict, *fnc, *usrptr;
    const char      *mymodule = "__builtin__";
    PyMethodDef     *m;
    const char      *method = "hello_python";

    Py_Initialize();
    mod = PyImport_ImportModule(mymodule);
    modname = PyString_FromString(mymodule);
    dict = PyModule_GetDict(mod);
    m = (PyMethodDef *) calloc(1, sizeof(PyMethodDef));
    m->ml_name = strdup(method);
    m->ml_meth = py_cb;
    usrptr = PyCObject_FromVoidPtr("I'm am the hello_python!", NULL);
    fnc = PyCFunction_NewEx(m, usrptr, modname);
    PyDict_SetItemString(dict, method, fnc);
    ...

Когда скрипт python выполняет hello_python, функция расширения py_cb покажет:

Я привет_питон!

self используется для отправки реального указателя, такого как контекст библиотеки, вместо этого const char * в этом примере, теперь это вопрос изменения его на что-то интересное. .

person Carlos Barcellos    schedule 31.05.2017

Я не знаю, можно ли это сделать напрямую из C-API. В худшем случае вы можете вызвать модуль traceback из C, чтобы получить имя вызывающего.

person Samuel    schedule 04.07.2011