I have a c++ program with a pybind11 embedded python interpreter, executing the following python file, it prints directly to std::cout
# test.py
print("text")
The c++ program executing the file:
#include <pybind11/embed.h>
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{};
py::eval_file("test.py");
}
Other solutions I found required modifying the python file - how can I redirect python sys.stdout to c++ as a std::string without modifying the python code using only the print() statement?
This github issue describes a way: https://github.com/pybind/pybind11/issues/1622
Copying the code from that issue verbatim. The following bit was recommended to make it work:
#include <pybind11/pybind11.h>
namespace py = pybind11;
From the issue:
class PyStdErrOutStreamRedirect {
py::object _stdout;
py::object _stderr;
py::object _stdout_buffer;
py::object _stderr_buffer;
public:
PyStdErrOutStreamRedirect() {
auto sysm = py::module::import("sys");
_stdout = sysm.attr("stdout");
_stderr = sysm.attr("stderr");
auto stringio = py::module::import("io").attr("StringIO");
_stdout_buffer = stringio(); // Other filelike object can be used here as well, such as objects created by pybind11
_stderr_buffer = stringio();
sysm.attr("stdout") = _stdout_buffer;
sysm.attr("stderr") = _stderr_buffer;
}
std::string stdoutString() {
_stdout_buffer.attr("seek")(0);
return py::str(_stdout_buffer.attr("read")());
}
std::string stderrString() {
_stderr_buffer.attr("seek")(0);
return py::str(_stderr_buffer.attr("read")());
}
~PyStdErrOutStreamRedirect() {
auto sysm = py::module::import("sys");
sysm.attr("stdout") = _stdout;
sysm.attr("stderr") = _stderr;
}
};
Usage:
{
PyStdErrOutStreamRedirect pyOutputRedirect{};
py::print("hello world");
// Other noisy python functions can be put here
assert(pyOutputRedirect.stdoutString() == "hello world\n")
}
// sys.stdout is back to its original state