简体   繁体   English

如何在 Python C-API 中获取当前的 function 名称?

[英]How to get current function name in Python C-API?

I implemented a bunch of functions and they are dispatched from the same C function called by the Python interpreter:我实现了一堆函数,它们是从 Python 解释器调用的同一个 C function 分派的:

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

Unexpectedly, self is NULL, and I need to get the function name currently being called.没想到self是NULL,我需要得到当前正在调用的function这个名字。 Is there any way to get this information?有什么方法可以获取这些信息吗?

I have dozens of functions which are all going through this routine.我有几十个函数都在通过这个例程。 This command processes all of the options into a C++ map and passes it along to the implementation of each command.此命令将所有选项处理为 C++ map 并将其传递给每个命令的实现。

Update: http://docs.python.org/extending/extending.html#a-simple-example specifically says "The self argument points to the module object for module-level functions; for a method it would point to the object instance.", but I am getting passed null when linking against python 2.6. Update: http://docs.python.org/extending/extending.html#a-simple-example specifically says "The self argument points to the module object for module-level functions; for a method it would point to the object instance .",但是在链接到 python 2.6 时,我通过了 null。

I've been trying to solve very similar problem.我一直在尝试解决非常相似的问题。

The conclusion I've come to suggests there is no way to determine name of current function or caller(s) at the level of Python C API. The conclusion I've come to suggests there is no way to determine name of current function or caller(s) at the level of Python C API. The reason being, Python interpreter puts on call stack only pure Python functions (implemented in Python itself).原因是,Python 解释器仅在调用堆栈上放置纯 Python 函数(在 Python 本身中实现)。 Functions implemented in C, regardless if registered in module methods table, are not put on Python stack, thus it's not possible to find them inspecting the stack frames.在 C 中实现的功能,无论是否在模块方法表中注册,都不会放在 Python 堆栈中,因此无法找到它们检查堆栈帧。

Here is a quick example in Python illustrating what I wanted to achieve (I assume Juan asks for similar behaviour):这是 Python 中的一个简单示例,说明了我想要实现的目标(我假设 Juan 要求类似的行为):

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()

Here is close equivalent of this example (based on the Python 3 docs ) but implemented using Python C API: Here is close equivalent of this example (based on the Python 3 docs ) but implemented using 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;
}

I hope this completes Ned's answer too.我希望这也可以完成 Ned 的回答。

NOTICE error checking for API is not being provided for clarity;注意 为清楚起见,未提供 API 的错误检查;

This example insert a new function directly on python __builtin__ module, allowing to call the method without class.method schema.这个例子直接在 python __builtin__模块上插入一个新的 function ,允许在没有 class.method 模式的情况下调用方法。 Just change mymodule to any other module as you wish.只需根据需要将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);
    ...

When python script execute hello_python , the py_cb extension function will show:当 python 脚本执行hello_python时,py_cb 扩展 function 将显示:

I'm am the hello_python!我是hello_python!

The self is used to send a real pointer such as the library context instead of this const char * of this example, this is now a matter of changing it to something interesting though. self用于发送一个真实的指针,例如库上下文,而不是这个例子的这个const char * ,不过现在是把它改成有趣的东西了。

The Python api isn't built to tell you what function it is calling. Python api 并不是为了告诉你它正在调用什么 function。 You've created a function, and it is calling it, the API assumes you know what function you've written.你已经创建了一个 function,它正在调用它,API 假设你知道你写了什么 function。 You'll need to create a small wrapper function for each of your Python functions.您需要为每个 Python 函数创建一个小型包装器 function。 The best way to do this is to register your one C function as one Python function that takes a string as its first argument. The best way to do this is to register your one C function as one Python function that takes a string as its first argument. Then, in your Python layer, create as many Python functions as you need, each invoking your C function with the proper string argument identifying what function you really want to call. Then, in your Python layer, create as many Python functions as you need, each invoking your C function with the proper string argument identifying what function you really want to call.

Another alternative is to rethink the structure of your C code, and have as many functions as you need, each of which invokes your common helper code to process options, etc.另一种选择是重新考虑您的 C 代码的结构,并根据需要拥有尽可能多的功能,每个功能都会调用您的常用帮助代码来处理选项等。

I don't know if can be done directly from the C-API.我不知道是否可以直接从 C-API 完成。 At worst, you could call the traceback module from C, to get the name of the caller.在最坏的情况下,您可以从 C 调用traceback模块,以获取调用者的名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM