如何在pytest中捕获pybind11封装的C++函数输出的stdout?
问题原因与解决方案
核心原因
capsys 是通过替换Python的sys.stdout/sys.stderr来捕获输出的,但C的std::cout直接操作操作系统的标准输出文件描述符(比如fd=1),完全绕过了Python的IO层,所以capsys无法捕获到这部分输出。而测试报告能看到内容,是因为报告直接收集了终端的所有输出(包括C直接写入的内容)。
解决方法
方法1:改用pybind11的py::print输出
直接在C++代码里用pybind11提供的打印函数,它会将内容输出到Python的sys.stdout,自然能被capsys捕获:
#include <pybind11/pybind11.h> namespace py = pybind11; class Ex { public: Ex() { py::print("ASSIGNING"); // 替换std::cout } }; PYBIND11_MODULE(your_module, m) { py::class_<Ex>(m, "Ex") .def(py::init<>()); }
方法2:重定向C++流到Python的IO
pybind11提供了现成的工具来重定向C++的std::cout/std::cerr到Python的输出流,步骤如下:
- C++端添加重定向支持:
#include <pybind11/pybind11.h> #include <pybind11/iostream.h> #include <iostream> namespace py = pybind11; class Ex { public: Ex() { std::cout << "ASSIGNING" << std::endl; } }; PYBIND11_MODULE(your_module, m) { // 添加流重定向工具 py::add_ostream_redirect(m, "ostream_redirect"); py::class_<Ex>(m, "Ex") .def(py::init<>()); }
- Python测试代码中使用重定向上下文:
def test_ex_capture(capsys): from your_module import Ex, ostream_redirect with ostream_redirect(): Ex() captured = capsys.readouterr() assert captured.out.strip() == "ASSIGNING"
方法3:手动重定向流缓冲区(进阶)
如果需要更灵活的控制,可以手动将std::cout的缓冲区替换为Python stdout的缓冲区:
#include <pybind11/pybind11.h> #include <iostream> #include <Python.h> namespace py = pybind11; class Ex { public: Ex() { // 临时重定向cout到Python stdout auto old_buf = std::cout.rdbuf(); std::cout.rdbuf(PySys_GetObject("stdout")->ob_type->tp_as_stream->write); std::cout << "ASSIGNING" << std::endl; // 恢复原缓冲区 std::cout.rdbuf(old_buf); } }; PYBIND11_MODULE(your_module, m) { py::class_<Ex>(m, "Ex") .def(py::init<>()); }
内容的提问来源于stack exchange,提问作者sodiumnitrate
相关产品推荐
相关产品推荐

