【问题标题】:"overloading" doesn't work with stderr“重载”不适用于标准错误
【发布时间】:2012-05-04 01:00:17
【问题描述】:
/*---------------stdcallbk_module.h---------------------*/
#ifndef STDCALLBK_MODULE_H
#include <Python.h>
#define STDCALLBK_MODULE_H


// Type definition for the callback function.
typedef void __stdcall(CALLBK_FUNC_T)(const char* p);


#ifdef __cplusplus
extern "C" {
#endif
  void register_callback(CALLBK_FUNC_T* callback);
  void import_stdcallbk(void);
#ifdef __cplusplus
}
#endif


#endif    /* #define STDCALLBK_MODULE_H */


/*---------------stdcallbk_module.c---------------------*/
#include "stdcallbk_module.h"

static CALLBK_FUNC_T *callback_write_func = NULL;
static FILE *fp;    /*for debugging*/
static PyObject* g_stdout;

typedef struct
{
    PyObject_HEAD
    CALLBK_FUNC_T *write;
} Stdout;


static PyObject* write2(PyObject* self, PyObject* args)
{
    const char* p;

    if (!PyArg_ParseTuple(args, "s", &p))
        return NULL;

    fputs(p, fp);    fflush(fp);
    if(callback_write_func)
        callback_write_func(p);
    else
        printf("----%s----", p);

    return PyLong_FromLong(strlen(p));
}

static PyObject* flush2(PyObject* self, PyObject* args)
{
    // no-op
    return Py_BuildValue("");
}


static PyMethodDef Stdout_methods[] =
{
    {"write", write2, METH_VARARGS, "sys-stdout-write"},
    {"flush", flush2, METH_VARARGS, "sys-stdout-write"},
    {NULL, NULL, 0, NULL}
};



static PyTypeObject StdoutType =
{
    PyVarObject_HEAD_INIT(0, 0)
    "stdcallbk.StdoutType", /* tp_name */
    sizeof(Stdout), /* tp_basicsize */
    0, /* tp_itemsize */
    0, /* tp_dealloc */
    0, /* tp_print */
    0, /* tp_getattr */
    0, /* tp_setattr */
    0, /* tp_reserved */
    0, /* tp_repr */
    0, /* tp_as_number */
    0, /* tp_as_sequence */
    0, /* tp_as_mapping */
    0, /* tp_hash */
    0, /* tp_call */
    0, /* tp_str */
    0, /* tp_getattro */
    0, /* tp_setattro */
    0, /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT, /* tp_flags */
    "stdcallbk.Stdout objects", /* tp_doc */
    0, /* tp_traverse */
    0, /* tp_clear */
    0, /* tp_richcompare */
    0, /* tp_weaklistoffset */
    0, /* tp_iter */
    0, /* tp_iternext */
    Stdout_methods, /* tp_methods */
    0, /* tp_members */
    0, /* tp_getset */
    0, /* tp_base */
    0, /* tp_dict */
    0, /* tp_descr_get */
    0, /* tp_descr_set */
    0, /* tp_dictoffset */
    0, /* tp_init */
    0, /* tp_alloc */
    0, /* tp_new */
};


static struct PyModuleDef stdcallbkmodule = {
    PyModuleDef_HEAD_INIT,
    "stdcallbk",   /* name of module */
    NULL, /* module documentation, may be NULL */
    -1,       /* size of per-interpreter state of the module,
                or -1 if the module keeps state in global variables. */
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};


PyMODINIT_FUNC PyInit_stdcallbk(void)
{
    PyObject* m;

    fp = fopen("debuggered.txt", "wt");
    fputs("got to here Vers 12\n", fp);  fflush(fp);

    StdoutType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&StdoutType) < 0)
        return 0;

    m = PyModule_Create(&stdcallbkmodule);
    if (m)
    {
        Py_INCREF(&StdoutType);
        PyModule_AddObject(m, "Stdout", (PyObject*) &StdoutType);
        fputs("PyModule_AddObject() Called\n", fp); fflush(fp);
    }
    g_stdout = StdoutType.tp_new(&StdoutType, 0, 0);

    /*PySys_SetObject("stdout", g_stdout)*/
    fprintf(fp, "PySys_SetObject(stdout) returned %d\n", PySys_SetObject("stdout", g_stdout));
    fflush(fp);

    /*PySys_SetObject("stderr", g_stdout)*/
    fprintf(fp, "PySys_SetObject(stderr) returned %d\n", PySys_SetObject("stderr", g_stdout));
    fflush(fp);

    return m;
}




/* called by cpp exe _after_ Py_Initialize(); */
void __declspec(dllexport) import_stdcallbk(void)
{
    PyImport_ImportModule("stdcallbk");
}



/* called by cpp exe _before_ Py_Initialize(); */
void __declspec(dllexport) register_callback(CALLBK_FUNC_T* callback)
{
    PyImport_AppendInittab("stdcallbk", &PyInit_stdcallbk);
    callback_write_func = callback;
}

/*------------- Embarcadero C++ Builder exe ---------------------*/
#include "/py_module/stdcallbk_module.h"



void __stdcall callback_write_func(const char* p)
{
    ShowMessage(p);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    PyObject *pName, *pModule;

    register_callback(callback_write_func);

    Py_Initialize();
    import_stdcallbk();
    //PyRun_SimpleString("import stdcallbk\n");

    pName = PyUnicode_FromString("hello_emb.py");
    pModule = PyImport_Import(pName);
    Py_Finalize();

}
//---------------------------------------------------------------------------



-------------- python script - hello_emb.py ----------------
#import stdcallbk

print("HELLO FRED !")


assert 1==2


------------------------------------------------------------

上面的代码是从这里借用的:How to redirect stderr in Python? Via Python C API?

从 cpp exe 运行时,我收到一个消息框“HELLO FRED!”接下来是一个空的消息框,我认为它是回车。到目前为止一切都很好,但是当引发异常时,我什么也得不到。也没有任何内容写入文本文件。

但是...如果我从 python 脚本中取消注释“import stdcallbk”并从命令行运行它,我会得到:

D:\projects\embed_python3\Win32\Debug>hello_emb.py
----HELLO FRED !--------
--------Traceback (most recent call last):
--------  File "D:\projects\embed_python3\Win32\Debug\hello_emb.py", line 7, in <module>
--------    --------assert 1==2--------
--------AssertionError----------------
----

还有这个:

D:\projects\embed_python3\Win32\Debug>type debuggered.txt
got to here Vers 12
PyModule_AddObject() Called
PySys_SetObject(stdout) returned 0
PySys_SetObject(stderr) returned 0
HELLO FRED !
Traceback (most recent call last):
  File "D:\projects\embed_python3\Win32\Debug\hello_emb.py", line 7, in <module>
    assert 1==2
AssertionError

所以它确实有效,只是从 cpp exe 运行时无效。

有人知道这里到底发生了什么吗?

干杯

【问题讨论】:

    标签: python c python-c-api python-embedding


    【解决方案1】:

    您的断言将导致hello_emb 导入失败。模块的主体是在导入模块时执行的。在您的情况下,这发生在 PyImport_Import() 调用中。它将打印“HELLO FRED!”很好,但是它会到达会引发异常的断言。

    PyImport_Import() 将返回 NULL 以表明这一点。您必须检查并采取适当的措施(这可能并不总是打印回溯,这就是它留给您的原因)。

    如果你想将错误打印到stderr,你必须调用PyErr_Print()

    pName = PyUnicode_FromString("hello_emb.py");
    pModule = PyImport_Import(pName);
    if (pModule == NULL)
        PyErr_Print();
    

    与代码无关的问题:

    这不是唯一缺少错误检查的地方(但只有上面一个是缺少回溯的原因)。调用 fopen 或几乎任何 Python C/API 调用都可能失败,您应该处理它。

    另外,PyInit_stdcallbk 中存在参考泄漏。在创建 stdout 对象后,它的引用计数为 1。然后将其设置为 sys.stdoutsys.stderr,使其变为 3。之后,您应该将 Py_DECREF 设置为 2,以便 if/when 它是从stdoutstderr 中删除,它将从内存中释放。现在它永远不会被释放。

    【讨论】:

    • 但是......导入没有失败 - 我得到“HELLO FRED!”信息。 fopen 仅用于调试。
    • 噢!一分钱掉了...谢谢
    • 我已经退出答案以便更好地解释。导入不会立即失败,它是由于 assert 引发的异常而失败的。
    猜你喜欢
    • 2011-09-23
    • 1970-01-01
    • 2015-06-21
    • 1970-01-01
    • 2014-02-24
    • 1970-01-01
    • 2017-03-24
    • 1970-01-01
    • 2014-12-14
    相关资源
    最近更新 更多