Frage

Ich habe ein Programm, das während des Laufs manchmal Python anrufen muss, um einige Aufgaben vorzuhalten. Ich brauche eine Funktion, die Python und nennt fängt Pythons Stdout und stellt es in eine Datei. Dies ist eine Erklärung der Funktion

  pythonCallBackFunc(const char* pythonInput)

Mein Problem ist es, zu fangen Die gesamte Pythonausgabe für einen bestimmten Befehl (Pythoninput). Ich habe keine Erfahrung mit Python API und weiß nicht, was die richtige Technik dafür ist. Als erstes habe ich es versucht, Pythons SDTOUT und STDERR mit py_run_simpestring umzuleiten. Dies ist ein Beispiel für den Code, den ich geschrieben habe.

#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;
}

Gibt es einen besseren Weg, dies zu tun? Außerdem tut Pyrun_simpestring aus irgendeinem Grund nichts, wenn es einen mathematischen Ausdruck erhält, zum Beispiel Pyrun_Simpestring ("5 ** 3") nichts (Python Conlsul druckt das Ergebnis: 125).

Vielleicht ist es wichtig, ich verwende Visual Studio 2008. Danke, Alex


Änderungen, die ich gemäß Marks Vorschlag vorgenommen habe:

  #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;
  }

Die Ausgabe, die ich nach dem Ausführen von Main bekomme:

 Here's the output: 123

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

Es ist gut für mich, aber nur ein Problem sollte es sein

 Here's the output: 123

 Here's the output: 6

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

Ich weiß nicht warum, aber nach dem Ausführen dieses Befehls: Pythonprinting ("1+5"), pystring_assstring (output) gibt einen leeren String (char*) anstelle von 6 zurück ... :( Gibt es etwas, um das nicht zu verlieren, um dies nicht zu verlieren Ausgang?

Thaks, Alex

War es hilfreich?

Lösung

Wenn ich Ihre Frage richtig lese, möchten Sie Stdout/Stderr in eine Variable in Ihrem C ++ erfassen? Sie können dies tun, indem Sie Stdout/Stderr in eine Python -Variable umgeben und diese Variable dann in Ihr C ++ abfragen. Bitte nicht, dass ich nicht die richtige Schiedsrichterin erledigt habe:

#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;

}

Andere Tipps

Hier ist eine C ++ - freundliche Lösung, die ich in letzter Zeit entwickelt habe.

Ich erkläre ein paar Details davon in meinem Blog: Python sys.stdout -Umleitung in C ++ Wo ich auch auf ein Repository in meinem GitHub verweise, wo die neueste Version gefunden werden kann. Hier ist ein vollständiges Beispiel, das auf dem aktuellen Code zum Zeitpunkt der Veröffentlichung dieser Antwort basiert:

#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;
}

Dies ermöglicht das Abfangen sys.stdout.write Ausgabe mit jeglicher Art von C ++ - Entität: Freie Funktion, Klassenmitgliedfunktion, benannte Funktionsobjekte oder sogar anonyme Funktionen wie im obigen Beispiel, in dem ich verwende C ++ 11 Lambda.

Beachten Sie, dass dies ein minimales Beispiel ist, um das wesentliche Konzept zu präsentieren. Im produktionsbereiten Code benötigt es sicherlich mehr Aufmerksamkeit in Bezug auf die Referenzzählung von PyObject, den globalen Staat loswerden und so weiter.

Ich weiß, dass diese Frage alt ist, aber ein Teil der Frage wurde noch nicht beantwortet:

"Wie kann man die Ausgabe von Befehlen fangen, die nicht direkt in die Stdout von Python schreiben, wie: 1+1?"

Hier sind die Schritte (für Python 3.4):

  1. Umleiten Sie Stdout/Stderr in eine Python -Variable unter Verwendung von Marks Lösung: https://stackoverflow.com/a/4307737/1046299

  2. Kopiefunktion PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags) Aus Python -Quellcode. Es befindet sich in der Datei pythonrun.c

  3. Modifiziere den PyRun_InteractiveOneObject Funktionsname und Signatur, damit die neue Funktion a const char* (Ihr Befehl) als erster Parameter statt a FILE*. Dann müssen Sie verwenden PyParser_ASTFromStringObject Anstatt von PyParser_ASTFromFileObject In der Funktionsinimpuzierung. Beachten Sie, dass Sie die Funktion kopieren müssen run_mod wie es ist aus Python, da es innerhalb der Funktion aufgerufen wird.

  4. Rufen Sie beispielsweise die neue Funktion mit Ihrem Befehl an 1+1. Stdout sollte jetzt die Ausgabe empfangen 2.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top