Как перенаправить stderr в Python?Через Python C API?
-
21-09-2019 - |
Вопрос
Это комбинация двух моих недавних вопросов:
[1] Метод экземпляра Python в C
[2] Как перенаправить stderr в Python?
Я хотел бы записать выходные данные как stdout, так и stderr из скрипта python.
Вещь, которую я хочу спросить, заключается в том, что создать новый тип в соответствии с [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", <my writer object / class / type / ?>);
http://docs.python.org/c-api/sys.html ?выделить=pysys_setobject#PySys_SetObject
Таким образом, не было бы необходимости предоставлять новый "тип записи" остальной части скрипта Python, поэтому я подумал, что было бы немного проще написать код ...?
Решение
Просто создайте объект module (вы все равно это делаете, если используете C API!-) и придайте ему подходящий write
функция - этот объект модуля будет пригоден в качестве второго аргумента для PySys_SetObject
.
В моем ответе на ваш другой вопрос я указал вам на xxmodule.c
, файл примера в исходных текстах Python на C, который представляет собой модуль с множеством примеров, включая типы и функции различного рода - вы можете работать оттуда, даже если (загадочно для меня) вы считаете часть "создать новый тип" слишком сложной;-).
Редактировать:вот тривиальный рабочий пример (aview.py
):
#include "Python.h"
#include <stdio.h>
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'
, а затем снова с новой строкой).
Другие советы
Основываясь на ответе Алекса, вот полностью рабочий код на C, без Python "import aview", на Python 3 (поэтому без Py_InitModule) и с перенаправлением stderr :
#include <functional>
#include <iostream>
#include <string>
#include <Python.h>
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
не сможете узнать, в какой буфер добавлять.Вам понадобится что-то вроде это.
Здесь кстати, это потрясающая ссылка на то, как добавлять новые модули и типы.