You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌入Python 3:如何将stdout、stdin和stderr重定向至C++?

Pure C/C++ Solution for Redirecting PyRun_SimpleString Output

Let’s break down your questions and build a clean, pure C/C++ approach that fits your custom Python GUI use case perfectly.

Question 1: Is PyFile_FromFd the Right Replacement for PyFile_FromFile in Python 3?

Great call noticing PyFile_FromFile is deprecated in Python 3. While PyFile_FromFd is marked as a "legacy" API, it’s still fully functional in Python 3 and acts as the direct replacement for PyFile_FromFile when wrapping file descriptors.

If you want to follow the most modern Python IO practices, you could use the io module’s C API to create buffered IO objects, but that adds unnecessary boilerplate for your use case. For capturing output from PyRun_SimpleString, PyFile_FromFd is more than sufficient and keeps your code concise.

Question 2: How to Only Redirect Output from Your Own PyRun_SimpleString Calls?

The solution here is temporary redirection: swap out sys.stdout and sys.stderr right before executing your Python code, then restore them immediately after. This way, only the output from your PyRun_SimpleString call gets captured—other parts of the GUI or app won’t be affected.

Since your library is loaded by the GUI, this approach is safe and isolated to your library’s Python execution context.

Step-by-Step Implementation

Here’s a complete, tested code example that uses pipes to capture output, temporary redirection, and pure C/C++ with Python’s C API:

#include <Python.h>
#include <unistd.h>
#include <fcntl.h>
#include <vector>
#include <string>

std::string capture_python_execution_output(const char* user_python_code) {
    // Ensure we hold the GIL (critical for thread-safe Python API calls)
    PyGILState_STATE gstate = PyGILState_Ensure();

    std::string output;
    int pipefd[2];

    // Create a pipe to capture output
    if (pipe(pipefd) == -1) {
        PyGILState_Release(gstate);
        return "Error: Failed to create output capture pipe";
    }

    // Save original stdout/stderr to restore later
    PyObject* original_stdout = PySys_GetObject("stdout");
    PyObject* original_stderr = PySys_GetObject("stderr");
    if (!original_stdout || !original_stderr) {
        close(pipefd[0]);
        close(pipefd[1]);
        PyGILState_Release(gstate);
        return "Error: Could not access original stdout/stderr";
    }
    Py_INCREF(original_stdout);
    Py_INCREF(original_stderr);

    // Create Python file objects from the pipe's write end
    PyObject* redirected_stdout = PyFile_FromFd(
        pipefd[1], "w", "w", -1, NULL, NULL, NULL, 1  // 1 = close fd when object is deleted
    );
    PyObject* redirected_stderr = PyFile_FromFd(
        pipefd[1], "w", "w", -1, NULL, NULL, NULL, 1
    );
    if (!redirected_stdout || !redirected_stderr) {
        Py_DECREF(original_stdout);
        Py_DECREF(original_stderr);
        close(pipefd[0]);
        close(pipefd[1]);
        PyGILState_Release(gstate);
        return "Error: Failed to create redirected file objects";
    }

    // Replace sys.stdout and sys.stderr with our pipe-backed objects
    if (PySys_SetObject("stdout", redirected_stdout) != 0 || 
        PySys_SetObject("stderr", redirected_stderr) != 0) {
        Py_DECREF(redirected_stdout);
        Py_DECREF(redirected_stderr);
        Py_DECREF(original_stdout);
        Py_DECREF(original_stderr);
        close(pipefd[0]);
        close(pipefd[1]);
        PyGILState_Release(gstate);
        return "Error: Failed to redirect stdout/stderr";
    }
    Py_DECREF(redirected_stdout);
    Py_DECREF(redirected_stderr);

    // Execute the user's Python code
    int exec_result = PyRun_SimpleString(user_python_code);
    (void)exec_result;  // Add error handling here if you need to report execution failures

    // Flush any buffered output to ensure it hits the pipe
    PyRun_SimpleString("import sys; sys.stdout.flush(); sys.stderr.flush()");

    // Restore original stdout/stderr
    PySys_SetObject("stdout", original_stdout);
    PySys_SetObject("stderr", original_stderr);
    Py_DECREF(original_stdout);
    Py_DECREF(original_stderr);

    // Close the pipe's write end to signal EOF to the read end
    close(pipefd[1]);

    // Read all captured output from the pipe
    char buffer[1024];
    ssize_t bytes_read;
    while ((bytes_read = read(pipefd[0], buffer, sizeof(buffer))) > 0) {
        output.append(buffer, bytes_read);
    }
    close(pipefd[0]);

    // Release the GIL
    PyGILState_Release(gstate);

    return output;
}

Key Notes

  1. GIL Handling: The PyGILState_Ensure() and PyGILState_Release() calls are critical if your library might be called from non-Python threads (common in GUI contexts). They ensure safe access to Python’s C API.
  2. Buffering: We explicitly flush sys.stdout and sys.stderr after executing the user’s code to make sure all output is written to the pipe before restoring the original streams.
  3. Isolation: By saving and restoring the original stdout/stderr, we guarantee that only our PyRun_SimpleString call’s output is captured—no impact on the GUI or other parts of the app.

内容的提问来源于stack exchange,提问作者dev15

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:18:08