I have a program which during it's run sometimes needs to call python in order to preform some tasks. I need a function that calls python and catches pythons stdout and puts it in some file. This is a declaration of the function
pythonCallBackFunc(const char* pythonInput)
My problem is to catch all the python output for a given command (pythonInput). I have no experience with python API and I don't know what is the right technique to do this. First thing I've tried is to redirect python's sdtout and stderr using Py_run_SimpleString this is some example of the code i've written.
#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; }
Is there a better way to do this? Besides, for some reason PyRun_SimpleString does nothing when it gets some mathematical expression, for example PyRun_SimpleString("5**3") prints nothing (python conlsul prints the result: 125)
maybe it is important, i am using visual studio 2008. Thanks, Alex
Changes I've made according Mark's suggestion:
#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; }
The output i get after running main:
Here's the output: 123 Here's the output: Here's the output: Here's the output: 2
It is good for me , but only one problem, it should be
Here's the output: 123 Here's the output: 6 Here's the output: Here's the output: 2
I dont know why but after running this command: PythonPrinting("1+5"), PyString_AsString(output) command returns an empty string (char*) instead of 6... :( Is there somthing i can do not to loose this output?
Thaks, Alex
Here is a C++ friendly solution I have developed lately.
I explain a few details of it on my blog: Python sys.stdout redirection in C++ where I also point to repository at my GitHub where most recent version can be found. Here is complete example based on the current code at the time of posting this answer:
#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; }
This allows to intercept sys.stdout.write
output with any kind of callable C++ entity: free function, class member function, named function objects or even anonymous functions as in the example above where I use C++11 lambda.
Note, this is a minimal example to present the essential concept. In production-ready code, it certainly needs more attention around reference counting of PyObject
, getting rid of global state, and so on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With