python - 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
因此,如您所见:
- 如果我将常规的
char*
“mainéfile.py”传递给PyRun_SimpleFile
它,它将无法运行脚本。 - 如果我将
wchar_t
“mainéfile.py”字符串传递给PyRun_SimpleFile
,它能够运行脚本,但随后报告的文件名mymodule_meth_test
是m
whilemainéfile.py
预期的。
这很可能是因为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
,我无法找到如何正确检索文件名......