如何重新初始化嵌入式Python解释器?

11 投票
3 回答
3868 浏览
提问于 2025-04-17 04:14

我正在把Python嵌入到我们的测试套件应用程序中。这样做的目的是用Python来运行几个测试脚本,以收集数据并生成测试报告。在一次测试运行中,多个测试脚本可以创建全局变量和函数,这些变量和函数可以在下一个脚本中使用。

这个应用程序还提供了一些扩展模块,这些模块会在嵌入的解释器中被导入,用来和应用程序交换一些数据。

不过,用户也可以进行多次测试运行。我不想在多次测试运行之间共享那些全局变量、导入的模块和交换的数据。我必须确保每次都从一个干净的状态开始,以控制测试环境并获得相同的结果。

我应该如何重新初始化解释器呢?

我使用了Py_Initialize()和Py_Finalize(),但是在第二次运行时,当我再次初始化我提供给解释器的扩展模块时,出现了异常。而且文档警告不要多次使用它

使用子解释器似乎在初始化扩展模块时也有类似的问题。

我怀疑我在初始化扩展模块时做错了什么,但我担心第三方扩展模块也会出现同样的问题。

也许可以通过在自己的进程中启动解释器来解决这个问题,这样可以确保所有内存都被释放。

顺便说一下,我在使用boost-python,它也警告不要使用Py_Finalize!

有什么建议吗?

谢谢

3 个回答

0

我会写一个新的脚本,每次用新的Python实例来执行一系列的测试脚本。或者直接用Python来写,像这样:

# run your tests in the process first
# now run the user scripts, each in new process to have virgin env
for script in userScript:
    subprocess.call(['python',script])
1

你可以试试用 code.IteractiveInterpreter 这个东西。

像下面这样应该就可以了:

#include <boost/python.hpp>
#include <string>
#include <stdexcept>

using namespace boost::python;

std::string GetPythonError()
{
    PyObject *ptype = NULL, *pvalue = NULL, *ptraceback = NULL;
    PyErr_Fetch(&ptype, &pvalue, &ptraceback);
    std::string message("");
    if(pvalue && PyString_Check(pvalue)) {
        message = PyString_AsString(pvalue);
    }
    return message;
}

// Must be called after Py_Initialize()
void RunInterpreter(std::string codeToRun)
{
    object pymodule = object(handle<>(borrowed(PyImport_AddModule("__main__"))));
    object pynamespace = pymodule.attr("__dict__");

    try {
        // Initialize the embedded interpreter
        object result = exec(   "import code\n"
                                "__myInterpreter = code.InteractiveConsole() \n", 
                                pynamespace);
        // Run the code
        str pyCode(codeToRun.c_str());
        pynamespace["__myCommand"] = pyCode;
        result = eval("__myInterpreter.push(__myCommand)", pynamespace);
    } catch(error_already_set) {
        throw std::runtime_error(GetPythonError().c_str());
    }
}
6

这里有另一种方法可以实现我想要的效果,首先在解释器中从头开始。

我可以控制用来执行代码的全局和局部命名空间:

// get the dictionary from the main module
// Get pointer to main module of python script
object main_module = import("__main__");
// Get dictionary of main module (contains all variables and stuff)
object main_namespace = main_module.attr("__dict__");

// define the dictionaries to use in the interpreter
dict global_namespace;
dict local_namespace;

// add the builtins
global_namespace["__builtins__"] = main_namespace["__builtins__"];

然后我可以使用这些命名空间来执行包含在 pyCode 中的代码:

exec( pyCode, global_namespace, lobaca_namespace );

当我想要运行新的测试实例时,我可以通过清理字典来清空命名空间:

// empty the interpreters namespaces
global_namespace.clear();
local_namespace.clear();        

// Copy builtins to new global namespace
global_namespace["__builtins__"] = main_namespace["__builtins__"];

根据我想要执行的级别,我可以使用 global = local。

撰写回答