如何在Python中重定向stderr? 通过Python C API?

这是我最近两个问题的组合:
[1] C中的Python实例方法
[2] 如何在Python中重定向stderr?

我想从python脚本中记录stdout和stderr的输出。

我想问的是,根据[1]创建一个新类型似乎相当复杂。 如果不需要将新类型暴露给Python,它是否简化了事情,即它只存在于C中?

我的意思是,当Python打印出来的东西时,它会转到“Objects / fileobject.c”,然后在“PyFile_WriteObject”中检查它是否可以写入其参数:

writer = PyObject_GetAttrString(f, "write"); if (writer == NULL) ... 

此外,有可能像这样得到stdout和stderr:

 PyObject* out = PySys_GetObject("stdout"); PyObject* err = PySys_GetObject("stderr"); 

我的问题是,是否有可能构造必要的PyObject,它满足上面的’PyObject_GetAttrString(f,“write”)’并且可以调用,所以我可以写:

 PySys_SetObject("stdout", ); 

http://docs.python.org/c-api/sys.html?highlight=pysys_setobject#PySys_SetObject

这样,就没有必要将新的“writer类型”暴露给Python脚本的其余部分,所以我认为编写代码可能会有点简单……?

只需创建一个模块对象(无论如何,如果你正在使用C API,你正在这样做) – 并使它具有合适的write函数 – 该模块对象将适合作为PySys_SetObject的第二个参数。

在我对你的另一个问题的回答中,我向你指出了xxmodule.c ,它是Python C源代码中的一个示例文件,它是一个包含很多例子的模块,包括各种类型和函数 – 你可以在那里工作,即使(神秘地)对我来说,你认为“做一个新类型”的部分太难了;-)。

编辑 :这是一个简单的工作示例( aview.py ):

 #include "Python.h" #include  static PyObject * aview_write(PyObject *self, PyObject *args) { const char *what; if (!PyArg_ParseTuple(args, "s", &what)) return NULL; printf("==%s==", what); return Py_BuildValue(""); } static PyMethodDef a_methods[] = { {"write", aview_write, METH_VARARGS, "Write something."}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC initaview(void) { PyObject *m = Py_InitModule("aview", a_methods); if (m == NULL) return; PySys_SetObject("stdout", m); } 

一旦正确安装了此aview模块:

 $ python Python 2.5.4 (r254:67917, Dec 23 2008, 14:57:27) [GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import aview >>> print 'ciao' ==ciao==== ==>>> 

…发送到标准输出的任何字符串都是用==符号写的(这个print调用.write两次:使用'ciao' ,然后再使用换行符)。

根据Alex的回答,这里是完全正常工作的C代码,没有Python“import aview”,在Python 3中(所以没有Py_InitModule),以及stderr重定向:

 #include  #include  #include  #include  PyObject* aview_write(PyObject* self, PyObject* args) { const char *what; if (!PyArg_ParseTuple(args, "s", &what)) return NULL; printf("==%s==", what); return Py_BuildValue(""); } PyObject* aview_flush(PyObject* self, PyObject* args) { return Py_BuildValue(""); } PyMethodDef aview_methods[] = { {"write", aview_write, METH_VARARGS, "doc for write"}, {"flush", aview_flush, METH_VARARGS, "doc for flush"}, {0, 0, 0, 0} // sentinel }; PyModuleDef aview_module = { PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base; "aview", // const char* m_name; "doc for aview", // const char* m_doc; -1, // Py_ssize_t m_size; aview_methods, // PyMethodDef *m_methods // inquiry m_reload; traverseproc m_traverse; inquiry m_clear; freefunc m_free; }; PyMODINIT_FUNC PyInit_aview(void) { PyObject* m = PyModule_Create(&aview_module); PySys_SetObject("stdout", m); PySys_SetObject("stderr", m); return m; } int main() { PyImport_AppendInittab("aview", PyInit_aview); Py_Initialize(); PyImport_ImportModule("aview"); PyRun_SimpleString("print(\'hello to buffer\')"); PyRun_SimpleString("make a SyntaxException in stderr"); Py_Finalize(); return 0; } 

但请注意,如果您计划使用多个不同的解释器,这将是不够的,因为aview_write将无法知道要追加到哪个缓冲区。 你需要这样的东西。

这是一个关于如何添加新模块和类型的真棒参考,顺便说一下。