Pregunta

Tengo un programa que durante su ejecución a veces necesita llamar a Python para preformar algunas tareas. Necesito una función que llame a Python y atrapa a Pythons stdout y lo pone en algún archivo. Esta es una declaración de la función

  pythonCallBackFunc(const char* pythonInput)

Mi problema es atrapar 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 hacer esto. Lo primero que he probado es redirigir el SDTout y Stderr de Python usando 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 he realizado según 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 Principal:

 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 pytring_asstring (salida) devuelve una cadena vacía (char*) en lugar de 6 ... :( ¿Hay algo que pueda hacer para no perder esto ¿producción?

Thaks, Alex

¿Fue útil?

Solución

Si estoy leyendo su pregunta correctamente, ¿desea capturar stdout/stderr en una variable dentro de su c ++? Puede hacer esto redirigiendo stdout/stderr en una variable Python y luego consultando esta variable en su C ++. Por favor, no es que no haya realizado el conteo de referencia adecuado 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;

}

Otros consejos

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

Explico algunos detalles en mi blog: Python sys.stdout redirección en c ++ Donde también señalo el 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 al 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 sys.stdout.write Salida con cualquier tipo de entidad C ++ llamable: función gratuita, función de miembro de clase, objetos de función nombrados o incluso funciones anónimas como en el ejemplo anterior donde uso C ++ 11 Lambda.

Tenga en cuenta que este es un ejemplo mínimo para presentar el concepto esencial. En el código listo para la producción, ciertamente necesita más atención sobre el recuento de referencias de PyObject, deshacerse del estado global, y así sucesivamente.

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 en el stdout de Python, como: 1+1?"

Aquí están los pasos (para Python 3.4):

  1. Redirige STDOUT/STDERR en una variable Python utilizando la solución de Mark: https://stackoverflow.com/a/4307737/1046299

  2. Función de copia PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags) del código fuente de Python. Se encuentra en el archivo pythonrun.c

  3. Modificar el PyRun_InteractiveOneObject Nombre de la función y firma para que la nueva función tome un const char* (su comando) como primer parámetro en lugar de un FILE*. Entonces necesitarás usar PyParser_ASTFromStringObject en vez de PyParser_ASTFromFileObject En la implementación de funciones. Tenga en cuenta que deberá copiar la función run_mod como es 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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top