如何重新初始化嵌入式Python解释器?
我正在把Python嵌入到我们的测试套件应用程序中。这样做的目的是用Python来运行几个测试脚本,以收集数据并生成测试报告。在一次测试运行中,多个测试脚本可以创建全局变量和函数,这些变量和函数可以在下一个脚本中使用。
这个应用程序还提供了一些扩展模块,这些模块会在嵌入的解释器中被导入,用来和应用程序交换一些数据。
不过,用户也可以进行多次测试运行。我不想在多次测试运行之间共享那些全局变量、导入的模块和交换的数据。我必须确保每次都从一个干净的状态开始,以控制测试环境并获得相同的结果。
我应该如何重新初始化解释器呢?
我使用了Py_Initialize()和Py_Finalize(),但是在第二次运行时,当我再次初始化我提供给解释器的扩展模块时,出现了异常。而且文档警告不要多次使用它。
使用子解释器似乎在初始化扩展模块时也有类似的问题。
我怀疑我在初始化扩展模块时做错了什么,但我担心第三方扩展模块也会出现同样的问题。
也许可以通过在自己的进程中启动解释器来解决这个问题,这样可以确保所有内存都被释放。
顺便说一下,我在使用boost-python,它也警告不要使用Py_Finalize!
有什么建议吗?
谢谢
3 个回答
我会写一个新的脚本,每次用新的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])
你可以试试用 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());
}
}
这里有另一种方法可以实现我想要的效果,首先在解释器中从头开始。
我可以控制用来执行代码的全局和局部命名空间:
// 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。