使用 Python 3 加载 Python 模块失败
Fails to load Python module with Python 3
#include <Python.h>
#include <fstream>
#include <iostream>
#include <string>
#include <filesystem>
#include <sys/types.h>
#include <dirent.h>
static const char * sPythonCode =
"class Test :\n"
" def __init__(self) : \n"
" self.Disc_ = 0. \n"
" def getset(self) : \n"
" self.Disc_ = 7. \n"
" return self.Disc_ \n";
std::string writeFile()
{
static int iFile = 0;
std::string sFileName(std::string("test") + std::to_string(iFile));
std::ofstream out("py/" + sFileName + ".py");
out << sPythonCode;
out.flush();
out.close();
iFile++;
return sFileName;
}
static bool bPythonOpen = false;
#define PYTHONPATHLEN 501
static void _PyInit()
{
if (!Py_IsInitialized())
{
Py_InitializeEx(0);
}
}
void openPython(void)
{
if (!bPythonOpen)
{
const size_t szBufferN = 1000;
char acLoadPath[szBufferN];
const char *pypath = "./py";
_PyInit();
PyRun_SimpleString("import sys");
PyRun_SimpleString("print('python (%d.%d.%d) initialized' % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro))");
PyRun_SimpleString("print('--------------------------')");
snprintf(acLoadPath, szBufferN, "sys.path.append('%s')", pypath);
PyRun_SimpleString(acLoadPath);
bPythonOpen = true;
}
}
PyObject *loadPythonModule(const char *acModule)
{
PyObject *pyModule = NULL;
if (bPythonOpen && acModule && strcmp(acModule, ""))
{
printf("%s\n", acModule);
pyModule = PyImport_ImportModule(acModule);
if (!pyModule)
{
PyErr_Print();
}
}
return pyModule;
}
void loadPython()
{
std::string sFileName = writeFile();
openPython();
//sleep(1);
PyObject *pPythonModule = loadPythonModule(sFileName.c_str());
if (pPythonModule)
PyDict_DelItemString(PyImport_GetModuleDict(), PyModule_GetName((PyObject *)pPythonModule));
}
int main(int argc, char **argv)
{
for (int i = 0; i < 10; i++)
{
loadPython();
}
}
我的工作环境:
- gcc 版本 8.3.1 20190311 (Red Hat 8.3.1-3) (GCC)
- Red Hat Enterprise Linux Server release 7.6 (Maipo)
- python 3.6.10 / 3.8.3
有问题
编译命令:
g++ pythontest.cpp -I/opt/python/python3.6.10/include/python3.6m -L/opt/python/python3.6.10/lib -lpython3.6m
创建py目录:
mkdir py
当我 运行 这段代码时,我加载的不同测试文件出现随机错误。
输出示例:
python (3.6.10) initialized
--------------------------
test0
test1
test2
test3
ModuleNotFoundError: No module named 'test3'
test4
test5
ModuleNotFoundError: No module named 'test5'
test6
test7
ModuleNotFoundError: No module named 'test7'
test8
test9
ModuleNotFoundError: No module named 'test9'
很高兴知道:
- 如果我取消注释带有 sleep 的行,效果很好
- 如果我删除
iFile++
,它也可以像在已创建文件后使用的那样工作
- 如果我在没有
rm -rf py
目录的情况下重新启动第二个,它也可以工作
- 如果我在
loadPython
函数中的每个 运行 之后删除文件并删除 iFile++
它也有效
- 如果我使用
strace
启动可执行文件,我看不到问题
目前 Python 加载程序似乎没有看到磁盘上的文件,但是如果我打印目录中的内容失败,由于 dirent,我会看到 testx.py
请注意,我们在不同的 Linux 服务器上重现了错误(不是硬件问题,甚至在 Windows 上),Python 2.7.x 它有效非常好。
每次修改模块文件夹时都应该调用 __import__('importlib').invalidate_caches()
让 C Python 知道它必须再次读取目录。
#include <Python.h>
#include <fstream>
#include <iostream>
#include <string>
#include <filesystem>
#include <sys/types.h>
#include <dirent.h>
static const char * sPythonCode =
"class Test :\n"
" def __init__(self) : \n"
" self.Disc_ = 0. \n"
" def getset(self) : \n"
" self.Disc_ = 7. \n"
" return self.Disc_ \n";
std::string writeFile()
{
static int iFile = 0;
std::string sFileName(std::string("test") + std::to_string(iFile));
std::ofstream out("py/" + sFileName + ".py");
out << sPythonCode;
out.flush();
out.close();
iFile++;
return sFileName;
}
static bool bPythonOpen = false;
#define PYTHONPATHLEN 501
static void _PyInit()
{
if (!Py_IsInitialized())
{
Py_InitializeEx(0);
}
}
void openPython(void)
{
if (!bPythonOpen)
{
const size_t szBufferN = 1000;
char acLoadPath[szBufferN];
const char *pypath = "./py";
_PyInit();
PyRun_SimpleString("import sys");
PyRun_SimpleString("print('python (%d.%d.%d) initialized' % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro))");
PyRun_SimpleString("print('--------------------------')");
snprintf(acLoadPath, szBufferN, "sys.path.append('%s')", pypath);
PyRun_SimpleString(acLoadPath);
bPythonOpen = true;
}
}
PyObject *loadPythonModule(const char *acModule)
{
PyObject *pyModule = NULL;
if (bPythonOpen && acModule && strcmp(acModule, ""))
{
printf("%s\n", acModule);
pyModule = PyImport_ImportModule(acModule);
if (!pyModule)
{
PyErr_Print();
}
}
return pyModule;
}
void loadPython()
{
std::string sFileName = writeFile();
openPython();
//sleep(1);
PyObject *pPythonModule = loadPythonModule(sFileName.c_str());
if (pPythonModule)
PyDict_DelItemString(PyImport_GetModuleDict(), PyModule_GetName((PyObject *)pPythonModule));
}
int main(int argc, char **argv)
{
for (int i = 0; i < 10; i++)
{
loadPython();
}
}
我的工作环境:
- gcc 版本 8.3.1 20190311 (Red Hat 8.3.1-3) (GCC)
- Red Hat Enterprise Linux Server release 7.6 (Maipo)
- python 3.6.10 / 3.8.3 有问题
编译命令:
g++ pythontest.cpp -I/opt/python/python3.6.10/include/python3.6m -L/opt/python/python3.6.10/lib -lpython3.6m
创建py目录:
mkdir py
当我 运行 这段代码时,我加载的不同测试文件出现随机错误。
输出示例:
python (3.6.10) initialized
--------------------------
test0
test1
test2
test3
ModuleNotFoundError: No module named 'test3'
test4
test5
ModuleNotFoundError: No module named 'test5'
test6
test7
ModuleNotFoundError: No module named 'test7'
test8
test9
ModuleNotFoundError: No module named 'test9'
很高兴知道:
- 如果我取消注释带有 sleep 的行,效果很好
- 如果我删除
iFile++
,它也可以像在已创建文件后使用的那样工作 - 如果我在没有
rm -rf py
目录的情况下重新启动第二个,它也可以工作 - 如果我在
loadPython
函数中的每个 运行 之后删除文件并删除iFile++
它也有效 - 如果我使用
strace
启动可执行文件,我看不到问题
目前 Python 加载程序似乎没有看到磁盘上的文件,但是如果我打印目录中的内容失败,由于 dirent,我会看到 testx.py
请注意,我们在不同的 Linux 服务器上重现了错误(不是硬件问题,甚至在 Windows 上),Python 2.7.x 它有效非常好。
每次修改模块文件夹时都应该调用 __import__('importlib').invalidate_caches()
让 C Python 知道它必须再次读取目录。