Comment attraper stdout python dans c ++ Code
-
29-09-2019 - |
Question
J'ai un programme qui, au cours de ce terme a parfois besoin d'appeler python pour préformer certaines tâches. J'ai besoin d'une fonction qui appelle python et prises pythons stdout et met dans un certain dossier. Ceci est une déclaration de la fonction
pythonCallBackFunc(const char* pythonInput)
Mon problème est d'attraper toutes les sorties python pour une commande donnée (pythonInput). Je n'ai aucune expérience avec l'API python et je ne sais pas quelle est la bonne technique pour le faire. La première chose que j'ai essayé est au sdtout de python et redirect stderr en utilisant Py_run_SimpleString Ceci est un exemple de code que je l'ai écrit.
#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;
}
Y at-il une meilleure façon de le faire? Par ailleurs, pour une raison quelconque PyRun_SimpleString ne fait rien quand il obtient une expression mathématique, par exemple PyRun_SimpleString ( "5 ** 3") imprime rien (python conlsul imprime le résultat: 125)
peut-être, il est important, je suis en utilisant Visual Studio 2008. Merci, Alex
Les changements que je l'ai fait selon la suggestion 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 sortie je reçois après l'exécution principale:
Here's the output: 123
Here's the output:
Here's the output:
Here's the output: 2
Il est bon pour moi, mais un seul problème, il doit être
Here's the output: 123
Here's the output: 6
Here's the output:
Here's the output: 2
Je ne sais pas pourquoi, mais après l'exécution de cette commande: PythonPrinting ( « 1 + 5 »), commande retourne PyString_AsString (sortie) une chaîne vide (char *) au lieu de 6 ... :( Y at-il somthing je peux ne pas de perdre cette sortie?
Thaks, Alex
La solution
Si je lis bien votre question, vous voulez stdout / stderr capture dans une variable au sein de votre C ++? Vous pouvez le faire en redirigeant stdout / stderr dans une variable python, puis en interrogeant cette variable dans votre C ++. S'il vous plaît pas que je ne l'ai pas fait le décompte correct ref ci-dessous:
#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;
}
Autres conseils
Voici une C ++ solution amicale que j'ai développé ces derniers temps.
J'explique quelques détails de celui-ci sur mon blog: redirection Python sys.stdout en C ++ où je aussi le point de dépôt à ma GitHub où la version la plus récente se trouve. Voici exemple complet basé sur le code en vigueur au moment de l'affichage de cette réponse:
#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;
}
Cela permet d'intercepter la sortie sys.stdout.write
avec tout type de appelable C ++ entité: Fonction libre, fonction de membre de classe, des objets de fonction nommée ou même des fonctions anonymes comme dans l'exemple ci-dessus où j'utilise C ++ 11 lambda .
Notez que ceci est un exemple minimal de présenter le concept essentiel. Dans le code de prêt à la production, il a certainement besoin de plus d'attention autour de comptage de référence de PyObject
, se débarrasser de l'état global, et ainsi de suite.
Je sais que cette question est ancienne, mais une partie de la question n'a pas été encore répondu:
« Comment la sortie de prise de commandes ne pas écrire directement à la sortie standard de Python, comme: 1 + 1 »
Voici les étapes (pour Python 3.4):
-
Rediriger stdout / stderr dans une variable Python en utilisant la solution de Mark: https://stackoverflow.com/a/4307737/ 1046299
-
Fonction copie
PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags)
à partir du code source Python. Il est situé dans le fichierpythonrun.c
-
Modifier le nom de la fonction
PyRun_InteractiveOneObject
et signature de telle sorte que la nouvelle fonction prend uneconst char*
(votre commande) en tant que premier paramètre au lieu d'unFILE*
. Ensuite, vous devrez utiliserPyParser_ASTFromStringObject
au lieu dePyParser_ASTFromFileObject
dans la mise en œuvre de la fonction. Notez que vous devrez copier la fonctionrun_mod
est de Python, car il est appelé dans la fonction. -
Appelez la nouvelle fonction avec votre commande, par exemple
1+1
. Stdout doit maintenant recevoir le2
de sortie.