Embedded Python (C API): как повторно запустить файл.py без повторной инициализации

У меня чертовски тяжелые времена со встроенным питоном.

Я использую DLL, и каждый раз, когда вызывается определенная функция, я хочу запустить скрипт Python. Я хочу избежать вызова Py_Finalize() в конце функции, потому что на вызовы Initialize/Finalize приходится около 75 мс времени, и я не могу себе этого позволить.

Проблема в том, что я не могу запустить один и тот же файл.py несколько раз без ошибок.

...
runResult = PyRun_SimpleFile(pyfileptr, pyfilename);
if (runResult)
{
    if (!PyErr_Occurred())
        return -4;
    PyErr_Print();
    return -3;
}
...

Я всегда возвращаю -4 во второй раз. Я даже не знаю, как это возможно, потому что в документации сказано, что PyRun_SimpleFile возвращает -1, если было исключение, и 0 в противном случае, но PyErr_Occurn () возвращает NULL без исключения.

Даже когда файл Python, который я запускаю, просто

print("hi")

В итоге я получаю те же результаты, что, безусловно, заставляет меня поверить, что это не исключение, генерируемое самим сценарием.

ОБНОВЛЕНИЕ: Все больше и больше похоже, что это проблема, связанная с DLL, так как запуск одного и того же кода в автономном приложении не показывает проблему. Все еще довольно озадаченный все же.

1 ответ

Решение

ОП здесь. Я в основном задал два вопроса, на которые у меня сейчас несколько плохие ответы:

Как запустить файл Python без повторной инициализации? Просто не вызывайте finalize перед вызовом PyRun_SimpleFile() или boost::python::exec_file() во второй раз.

Почему PyErr_Occurn () возвращает 0 после того, как PyRun_SimpleFile() возвращает ненулевое значение? Короткий ответ - я до сих пор не знаю, но я думаю, что это связано с реализацией DLL и некоторыми зависаниями или отсутствующими ссылками.

Я использовал boost:: python, основываясь на предложении Кичика, и хотя я бы не сказал, что его намного проще использовать, чем базовый C API, его легче читать. Это также не показывало проблему отсутствующей ошибки, таким образом, в конечном счете это решило мою проблему. Я смог сделать два последовательных вызова exec_file () без проблем, даже в DLL.

Поскольку у меня были некоторые проблемы с поиском примеров boost:: python, используемых так, как мне было нужно, я разместил здесь свой код, слегка урезанный для пробела. Конечно, кое-что из этого относится к моему проекту, но все же может быть полезным в качестве общего примера.

extern "C" LTPYTHON_API int ltPythonAnalyzeLog(char * analyzerfile, char * logfile, double timeWindow, int * results)
{

std::vector<int> countsVector;
Py_Initialize();
object main_module = import("__main__");
object main_namespace = main_module.attr("__dict__");
    // Example of adding a variable to the global namespace
main_namespace["scriptIsRunningThroughDll"] = boost::python::long_(1);

// Load arguments for the analyzer call
{
    int argc = 3;
    wchar_t * argv[3];

    //*... assemble wchar arguments for py script ... *

    PySys_SetArgv(argc, argv);
}

int startClock = clock();
try
{
    exec_file(analyzerfile, main_namespace);
}
catch(error_already_set const &)
{
            //*... some error handling ...*

    PyObject *ptype, *pvalue, *ptraceback;
    PyErr_Fetch(&ptype, &pvalue, &ptraceback);

    handle<> hType(ptype);
    object extype(hType);
    handle<> hTraceback(ptraceback);
    object traceback(hTraceback);

    //Extract error message
    std::string strErrorMessage = extract<std::string>(pvalue);
    long lineno = extract<long> (traceback.attr("tb_lineno"));

    FILE * outfile = fopen("ltpython-error.txt", "a");
    fprintf(outfile, "%d: %s\n", lineno, strErrorMessage);
    fflush(outfile);
    fclose(outfile);

    return -1;
}

    //*... grabbing a matrix of results that were created in the script ...*
object counts = main_namespace["sortedIndicationCounts"];
list countsList = extract<list>(counts);
int totalCount = 0;
for (int i = 0; i < len(countsList); i++)
{
    list singleCount = extract<list>(countsList[i]);
    countsVector.push_back(extract<int>(singleCount[1]));
    totalCount += countsVector[i];
}

    //*... returning the number of milliseconds that elapsed ...*
return clock() - startClock;
}

Обработка ошибок основана на этом ответе.

Другие вопросы по тегам