Как повторно инициализировать встроенный интерпретатор Python?

Я работаю над внедрением Python в наше приложение test suite. Цель состоит в том, чтобы использовать Python для запуска нескольких сценариев тестов для сбора данных и создания отчета о тестах. Несколько тестовых сценариев для одного тестового запуска могут создавать глобальные переменные и функции, которые могут использоваться в следующем сценарии.

приложение также предоставляет модули расширения, которые импортируются во встроенный интерпретатор и используются для обмена некоторыми данными с приложением.

но пользователь может также сделайте несколько тестовых запусков. Я не хочу делиться этими глобалами, импортом и обменом данными между несколькими тестовыми запусками. Я должен быть уверен, что перезапущен в подлинном состоянии, чтобы контролировать тестовую среду и получать те же результаты.

Как мне повторно инициализировать интерпретатор?

Я использовал Py_Initialize () и Py_Finalize (), но получаю исключение при втором запуске при инициализации во второй раз модулей расширения, которые я предоставляю интерпретатору. И документация предупреждает против использования его более одного раза.

используя суб-переводчиков кажется, имеют те же предостережения с инициализацией модулей расширения.

Я подозреваю, что я делаю что-то неправильно с инициализацией моих модулей расширения, но я боюсь, что та же проблема происходит с модулями расширения 3rd party.

возможно, его можно заставить работать, запустив интерпретатор в своем собственном процессе, чтобы быть уверенным, что все память освобождается.

кстати, я использую boost-python для него, что также предупреждает об использовании Py_Finalize!

какие будут предложения?

спасибо

3 ответов


вот еще один способ я нашел, чтобы достичь того, чего я хочу, начать с чистого листа в переводчик.

Я могу управлять глобальными и локальными пространствами имен, которые я использую для выполнения кода:

// 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


Как насчет использования 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());
    }
}

Я бы написал другой сценарий оболочки, выполняющий последовательность тестовых сценариев с новыми экземплярами 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])