Wie man Python Stdout in C ++ - Code fängt
-
29-09-2019 - |
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
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):
Umleiten Sie Stdout/Stderr in eine Python -Variable unter Verwendung von Marks Lösung: https://stackoverflow.com/a/4307737/1046299
Kopiefunktion
PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags)
Aus Python -Quellcode. Es befindet sich in der Dateipythonrun.c
Modifiziere den
PyRun_InteractiveOneObject
Funktionsname und Signatur, damit die neue Funktion aconst char*
(Ihr Befehl) als erster Parameter statt aFILE*
. Dann müssen Sie verwendenPyParser_ASTFromStringObject
Anstatt vonPyParser_ASTFromFileObject
In der Funktionsinimpuzierung. Beachten Sie, dass Sie die Funktion kopieren müssenrun_mod
wie es ist aus Python, da es innerhalb der Funktion aufgerufen wird.Rufen Sie beispielsweise die neue Funktion mit Ihrem Befehl an
1+1
. Stdout sollte jetzt die Ausgabe empfangen2
.