pybind11 将 python sys.stdout 从 print() 重定向到 C++
pybind11 Redirect python sys.stdout to c++ from print()
我有一个带有 pybind11 嵌入式 python 解释器的 C++ 程序,执行以下 python 文件,它直接打印到 std::cout
# test.py
print("text")
执行文件的c++程序:
#include <pybind11/embed.h>
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{};
py::eval_file("test.py");
}
我发现的其他解决方案需要修改 python 文件 - 如何将 python sys.stdout 重定向到 c++ 作为 std::string 而无需 仅使用 print() 语句修改 python 代码?
在这种情况下,'capture' 是什么意思?归根结底,不管是python还是C++,写都要经过OS。如果只是为了使输出静音,将其写入文件,将其发送到另一个进程等等,您可以拦截该级别的所有输出。
这是一个示例,它在 python 脚本期间压缩所有标准输出输出,然后恢复,之后标准输出的行为与以前一样(对于 python 和其他情况):
#include <pybind11/embed.h>
#include <unistd.h>
namespace py = pybind11;
int main() {
auto fdo = fileno(stdout);
auto savefd = dup(fdo);
auto f = fopen("/dev/null", "w");
dup2(fileno(f), fdo);
py::scoped_interpreter guard{};
py::eval_file("test.py");
fflush(stdout);
dup2(savefd, fdo);
}
这个github问题描述了一个方法:
https://github.com/pybind/pybind11/issues/1622
逐字复制该问题的代码。建议使用以下位使其工作:
#include <pybind11/pybind11.h>
namespace py = pybind11;
来自问题:
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;
}
};
用法:
{
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
我的实现如下
原理是实现一个模块来代替sys.stdout
#include <iostream>
#include <pybind11/embed.h>
PYBIND11_EMBEDDED_MODULE(my_sys, m) {
struct my_stdout {
my_stdout() = default;
my_stdout(const my_stdout &) = default;
my_stdout(my_stdout &&) = default;
};
py::class_<my_stdout> my_stdout(m, "my_stdout");
my_stdout.def_static("write", [](py::object buffer) {
std::cout << buffer.cast<std::string>();
});
my_stdout.def_static("flush", []() {
std::cout << std::flush;
});
m.def("hook_stdout", []() {
auto py_sys = py::module::import("sys");
auto my_sys = py::module::import("my_sys");
py_sys.attr("stdout") = my_sys.attr("my_stdout");
});
}
auto main(int argc, char **argv) -> int {
py::scoped_interpreter python;
#if 0
py_stdout.attr("write") = py::cpp_function([](py::object info) {
std::cout << "info" << std::endl;
});
#else
py::module::import("my_sys").attr("hook_stdout")();
#endif
py::print("Hello, World!\n")
return 0;
}
我有一个带有 pybind11 嵌入式 python 解释器的 C++ 程序,执行以下 python 文件,它直接打印到 std::cout
# test.py
print("text")
执行文件的c++程序:
#include <pybind11/embed.h>
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{};
py::eval_file("test.py");
}
我发现的其他解决方案需要修改 python 文件 - 如何将 python sys.stdout 重定向到 c++ 作为 std::string 而无需 仅使用 print() 语句修改 python 代码?
在这种情况下,'capture' 是什么意思?归根结底,不管是python还是C++,写都要经过OS。如果只是为了使输出静音,将其写入文件,将其发送到另一个进程等等,您可以拦截该级别的所有输出。
这是一个示例,它在 python 脚本期间压缩所有标准输出输出,然后恢复,之后标准输出的行为与以前一样(对于 python 和其他情况):
#include <pybind11/embed.h>
#include <unistd.h>
namespace py = pybind11;
int main() {
auto fdo = fileno(stdout);
auto savefd = dup(fdo);
auto f = fopen("/dev/null", "w");
dup2(fileno(f), fdo);
py::scoped_interpreter guard{};
py::eval_file("test.py");
fflush(stdout);
dup2(savefd, fdo);
}
这个github问题描述了一个方法: https://github.com/pybind/pybind11/issues/1622
逐字复制该问题的代码。建议使用以下位使其工作:
#include <pybind11/pybind11.h>
namespace py = pybind11;
来自问题:
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;
}
};
用法:
{
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
我的实现如下
原理是实现一个模块来代替sys.stdout
#include <iostream>
#include <pybind11/embed.h>
PYBIND11_EMBEDDED_MODULE(my_sys, m) {
struct my_stdout {
my_stdout() = default;
my_stdout(const my_stdout &) = default;
my_stdout(my_stdout &&) = default;
};
py::class_<my_stdout> my_stdout(m, "my_stdout");
my_stdout.def_static("write", [](py::object buffer) {
std::cout << buffer.cast<std::string>();
});
my_stdout.def_static("flush", []() {
std::cout << std::flush;
});
m.def("hook_stdout", []() {
auto py_sys = py::module::import("sys");
auto my_sys = py::module::import("my_sys");
py_sys.attr("stdout") = my_sys.attr("my_stdout");
});
}
auto main(int argc, char **argv) -> int {
py::scoped_interpreter python;
#if 0
py_stdout.attr("write") = py::cpp_function([](py::object info) {
std::cout << "info" << std::endl;
});
#else
py::module::import("my_sys").attr("hook_stdout")();
#endif
py::print("Hello, World!\n")
return 0;
}