versiones guia español descargar actualizar c++ python redirect python-c-api python-embedding

guia - Cómo atrapar Python stdout en código de C++



qgis manual (3)

Tengo un programa que durante su ejecución a veces necesita llamar a Python para realizar algunas tareas. Necesito una función que llame a python y atrape pythons stdout y lo ponga en algún archivo. Esta es una declaración de la función

pythonCallBackFunc(const char* pythonInput)

Mi problema es capturar toda la salida de python para un comando dado (pythonInput). No tengo experiencia con la API de Python y no sé cuál es la técnica correcta para hacerlo. Lo primero que he intentado es redirigir sdtout y stderr de Python utilizando Py_run_SimpleString, este es un ejemplo del código que he escrito.

#include "boost/python.hpp" #include <iostream> void pythonCallBackFunc(const char* inputStr){ PyRun_SimpleString(inputStr); } int main () { ... //S0me outside functions does this Py_Initialize(); PyRun_SimpleString("import sys"); PyRun_SimpleString("old_stdout = sys.stdout"); PyRun_SimpleString("fsock = open(''python_out.log'',''a'')"); PyRun_SimpleString("sys.stdout = fsock"); ... //my func pythonCallBackFunc("print ''HAHAHAHAHA''"); pythonCallBackFunc("result = 5"); pythonCallBackFunc("print result"); pythonCallBackFunc("result = ''Hello ''+''World!''"); pythonCallBackFunc("print result"); pythonCallBackFunc("''KUKU ''+''KAKA''"); pythonCallBackFunc("5**3"); pythonCallBackFunc("prinhghult"); pythonCallBackFunc("execfile(''stdout_close.py'')"); ... //Again anothers function code PyRun_SimpleString("sys.stdout = old_stdout"); PyRun_SimpleString("fsock.close()"); Py_Finalize(); return 0; }

¿Hay una mejor manera de hacer esto? Además, por alguna razón PyRun_SimpleString no hace nada cuando obtiene una expresión matemática, por ejemplo, PyRun_SimpleString ("5 ** 3") no imprime nada (Python conlsul imprime el resultado: 125)

tal vez es importante, estoy usando Visual Studio 2008. Gracias, Alex

Cambios que hice de acuerdo con la sugerencia de Mark:

#include <python.h> #include <string> using namespace std; void PythonPrinting(string inputStr){ string stdOutErr = "import sys/n/ class CatchOut:/n/ def __init__(self):/n/ self.value = ''''/n/ def write(self, txt):/n/ self.value += txt/n/ catchOut = CatchOut()/n/ sys.stdout = catchOut/n/ sys.stderr = catchOut/n/ "; //this is python code to redirect stdouts/stderr PyObject *pModule = PyImport_AddModule("__main__"); //create main module PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect PyRun_SimpleString(inputStr.c_str()); PyObject *catcher = PyObject_GetAttrString(pModule,"catchOut"); PyObject *output = PyObject_GetAttrString(catcher,"value"); printf("Here''s the output: %s/n", PyString_AsString(output)); } int main(int argc, char** argv){ Py_Initialize(); PythonPrinting("print 123"); PythonPrinting("1+5"); PythonPrinting("result = 2"); PythonPrinting("print result"); Py_Finalize(); return 0; }

La salida que obtengo después de ejecutar main:

Here''s the output: 123 Here''s the output: Here''s the output: Here''s the output: 2

Es bueno para mí, pero solo un problema, debería ser

Here''s the output: 123 Here''s the output: 6 Here''s the output: Here''s the output: 2

No sé por qué, pero después de ejecutar este comando: PythonPrinting ("1 + 5"), el comando PyString_AsString (salida) devuelve una cadena vacía (char *) en lugar de 6 ... :( ¿Hay algo que pueda hacer para no perder esto? ¿salida?

Thaks, Alex


Aquí hay una solución amigable con C ++ que he desarrollado últimamente.

Explico algunos detalles de esto en mi blog: Python sys.stdout redirection en C ++ donde también apunto a repositorio en mi GitHub donde se puede encontrar la versión más reciente. Aquí hay un ejemplo completo basado en el código actual en el momento de publicar esta respuesta:

#include <functional> #include <iostream> #include <string> #include <Python.h> namespace emb { typedef std::function<void(std::string)> stdout_write_type; struct Stdout { PyObject_HEAD stdout_write_type write; }; PyObject* Stdout_write(PyObject* self, PyObject* args) { std::size_t written(0); Stdout* selfimpl = reinterpret_cast<Stdout*>(self); if (selfimpl->write) { char* data; if (!PyArg_ParseTuple(args, "s", &data)) return 0; std::string str(data); selfimpl->write(str); written = str.size(); } return PyLong_FromSize_t(written); } PyObject* Stdout_flush(PyObject* self, PyObject* args) { // no-op return Py_BuildValue(""); } PyMethodDef Stdout_methods[] = { {"write", Stdout_write, METH_VARARGS, "sys.stdout.write"}, {"flush", Stdout_flush, METH_VARARGS, "sys.stdout.flush"}, {0, 0, 0, 0} // sentinel }; PyTypeObject StdoutType = { PyVarObject_HEAD_INIT(0, 0) "emb.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 */ "emb.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 */ }; PyModuleDef embmodule = { PyModuleDef_HEAD_INIT, "emb", 0, -1, 0, }; // Internal state PyObject* g_stdout; PyObject* g_stdout_saved; PyMODINIT_FUNC PyInit_emb(void) { g_stdout = 0; g_stdout_saved = 0; StdoutType.tp_new = PyType_GenericNew; if (PyType_Ready(&StdoutType) < 0) return 0; PyObject* m = PyModule_Create(&embmodule); if (m) { Py_INCREF(&StdoutType); PyModule_AddObject(m, "Stdout", reinterpret_cast<PyObject*>(&StdoutType)); } return m; } void set_stdout(stdout_write_type write) { if (!g_stdout) { g_stdout_saved = PySys_GetObject("stdout"); // borrowed g_stdout = StdoutType.tp_new(&StdoutType, 0, 0); } Stdout* impl = reinterpret_cast<Stdout*>(g_stdout); impl->write = write; PySys_SetObject("stdout", g_stdout); } void reset_stdout() { if (g_stdout_saved) PySys_SetObject("stdout", g_stdout_saved); Py_XDECREF(g_stdout); g_stdout = 0; } } // namespace emb int main() { PyImport_AppendInittab("emb", emb::PyInit_emb); Py_Initialize(); PyImport_ImportModule("emb"); PyRun_SimpleString("print(/'hello to console/')"); // here comes the ***magic*** std::string buffer; { // switch sys.stdout to custom handler emb::stdout_write_type write = [&buffer] (std::string s) { buffer += s; }; emb::set_stdout(write); PyRun_SimpleString("print(/'hello to buffer/')"); PyRun_SimpleString("print(3.14)"); PyRun_SimpleString("print(/'still talking to buffer/')"); emb::reset_stdout(); } PyRun_SimpleString("print(/'hello to console again/')"); Py_Finalize(); // output what was written to buffer object std::clog << buffer << std::endl; }

Esto permite interceptar la salida sys.stdout.write con cualquier tipo de entidad invocable de C ++: función libre, función miembro de clase, objetos de función nombrados o incluso funciones anónimas como en el ejemplo anterior donde utilizo C ++ 11 lambda .

Tenga en cuenta que este es un ejemplo mínimo para presentar el concepto esencial. En el código de producción lista, sin duda necesita más atención en cuanto al recuento de referencias de PyObject , deshacerse del estado global, y demás.


Sé que esta pregunta es antigua, pero una parte de la pregunta aún no ha sido respondida:

"Cómo atrapar la salida de comandos que no escriben directamente a la salida estándar de Python, como: 1 + 1?"

Estos son los pasos (para Python 3.4):

  1. Redirija stdout / stderr a una variable de Python con la solución de Mark: https://.com/a/4307737/1046299

  2. Copie la función PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags) del código fuente de Python. Está ubicado en el archivo pythonrun.c

  3. Modifique el nombre y la firma de la función PyRun_InteractiveOneObject para que la nueva función tome un const char* (su comando) como primer parámetro en lugar de un FILE* . Luego necesitará usar PyParser_ASTFromStringObject lugar de PyParser_ASTFromFileObject en la implementación de la función. Tenga en cuenta que necesitará copiar la función run_mod como está de Python, ya que se llama dentro de la función.

  4. Llame a la nueva función con su comando, por ejemplo 1+1 . Stdout ahora debería recibir la salida 2 .


Si estoy leyendo su pregunta correctamente, ¿desea capturar stdout / stderr en una variable dentro de su C ++? Puede hacerlo redirigiendo stdout / stderr a una variable de python y luego consultando esta variable en su C ++. Por favor, no es que no he hecho el recuento de ref apropiado a continuación:

#include <Python.h> #include <string> int main(int argc, char** argv) { std::string stdOutErr = "import sys/n/ class CatchOutErr:/n/ def __init__(self):/n/ self.value = ''''/n/ def write(self, txt):/n/ self.value += txt/n/ catchOutErr = CatchOutErr()/n/ sys.stdout = catchOutErr/n/ sys.stderr = catchOutErr/n/ "; //this is python code to redirect stdouts/stderr Py_Initialize(); PyObject *pModule = PyImport_AddModule("__main__"); //create main module PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect PyRun_SimpleString("print(1+1)"); //this is ok stdout PyRun_SimpleString("1+a"); //this creates an error PyObject *catcher = PyObject_GetAttrString(pModule,"catchOutErr"); //get our catchOutErr created above PyErr_Print(); //make python print any errors PyObject *output = PyObject_GetAttrString(catcher,"value"); //get the stdout and stderr from our catchOutErr object printf("Here''s the output:/n %s", PyString_AsString(output)); //it''s not in our C++ portion Py_Finalize(); return 0; }