首页 > 解决方案 > Python 嵌入式:如何将特殊字符传递给 PyRun_SimpleFile

问题描述

考虑以下从 C++ 运行嵌入式 Python 脚本的代码。它创建了一个嵌入式 Python 模块,该模块具有在执行时报告当前文件/行的函数。

#include <Python.h>

#include <iostream>
#include <fstream>

PyObject * mymodule_meth_test(PyObject * self) {
    PyTraceBack_Here(PyEval_GetFrame());
    PyObject * exc;
    PyObject * val;
    PyObject * tb;
    PyErr_Fetch(&exc, &val, &tb);
    PyTraceBack_Print(tb, PySys_GetObject("stderr"));
    
    std::cout << "LINE is " << PyLong_AsLong(PyObject_GetAttrString(PyObject_GetAttrString(tb, "tb_frame"), "f_lineno")) << std::endl;
    std::cout << "FILE is " << PyUnicode_AsUTF8(PyObject_GetAttrString(PyObject_GetAttrString(PyObject_GetAttrString(tb, "tb_frame"), "f_code"), "co_filename")) << std::endl;

    Py_RETURN_NONE;
}

PyMethodDef module_methods[] = {
    {"test", (PyCFunction)mymodule_meth_test, METH_NOARGS, NULL},
    {},
};

PyModuleDef module_def = {PyModuleDef_HEAD_INIT, "mymodule", NULL, -1, module_methods};

extern "C" PyObject * PyInit_mymodule() {
    PyObject * module = PyModule_Create(&module_def);
    return module;
}
    
void runScript( const std::string& script, bool utf8 )
{
    Py_SetPythonHome( L"C:\\dev\\vobs_sde\\sde\\3rdparty\\tools_ext\\python\\Python38" );

    PyImport_AppendInittab("mymodule", &PyInit_mymodule);

    // Initialize the Python Interpreter
    Py_Initialize();

    FILE* file = NULL;
    open(&file,script);
    if ( file )
    {
        wchar_t* sScriptUTF8 = Py_DecodeLocale(script.c_str(), NULL);
        if ( PyRun_SimpleFile(file, (utf8) ? (const char*) sScriptUTF8 : script.c_str()) == 0 )
            std::cout << "SUCCESS" << std::endl;
        else
            std::cout << "FAIL" << std::endl;
        fclose(file);
    }
    
    Py_Finalize();
}

int main( int argc, char* argv[] )
{
    std::fstream file2;
    file2.open( "mainéfile.py", std::ios_base::out );
    file2 << "import mymodule" << std::endl;
    file2 << "mymodule.test()" << std::endl;
    file2.close();

    std::cout << std::endl << "Will fail to execute script" << std::endl;
    runScript( "mainéfile.py", false );
    std::cout << std::endl << "Will work! But FILE will be reported as 'm' instead of 'mainéfile.py'" << std::endl;
    runScript( "mainéfile.py", true );
    
    return 0;
}

该脚本输出:

Will fail to execute script
FAIL
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 4: invalid continuation byte

Will work! But FILE will be reported as 'm' instead of 'mainéfile.py'
Traceback (most recent call last):
  File "m", line 2, in <module>
LINE is 2
FILE is m
SUCCESS

因此,如您所见:

这很可能是因为wchar_t,“mainéfile.py”是“'m', 0, 'a', 0,...”,这后来被解释为常规char*变为“m”,因为第二项被认为是 EOS .

我应该如何调用PyRun_SimpleFile才能正常工作?


请注意,我最终能够PyRun_SimpleFile如下调用:

std::string utf8Str = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(Py_DecodeLocale(script.c_str(), NULL));
if ( PyRun_SimpleFile(file, utf8Str.c_str()) == 0 )
    std::cout << "SUCCESS" << std::endl;
else
    std::cout << "FAIL" << std::endl;

但是,稍后mymodule_meth_test调用PyUnicode_AsUTF8将返回NULL,我无法找到如何正确检索文件名......

标签: pythonc++python-embedding

解决方案


推荐阅读