我在管理 Python 中的 C++ 项目的单元测试时遇到了一个具有挑战性的棘手问题。
项目
项目很大,强烈依赖boost,有几个dll/so文件,功能导出到python。所以我无法复制代码,也很难从所有这些代码中提取最小的样本。此外,我很确定这更多的是配置问题而不是代码问题。不过,我将提供一个关于关键部分的代码示例。
问题
我有一个 C++ 单元测试例程,可以在 python 中使用。它适用于 python 3.8 和 3.9。它也适用于 Linux 上的 python 3.10,但我在 Windows 上收到以下消息:
<class 'ImportError'> : DLL load failed while importing UnitTestTools_4_1_0_0_beta_x64: The specified module was not found.
我检查了依赖项,它们都存在,并且它们的位置都列在 sys.path 中。我使用
sys.path.insert(0, dirPath)
添加包含 dll/so 和 pyd 文件的目录。我还检查了 DependencyGui.exe
此外,我可以使用我的项目的所有功能,即我可以导入其他文件并调用它们包含的函数。仅在 Windows 上使用 Python 3.10 进行单元测试无法导入相关模块。
配置
C++ 方面,我使用 boost 1.75.0 并在 Windows 上使用 Visual Studio 2019 和在 Linux 上使用 gcc 9.4 构建项目。 我需要连接 C++ 和 python 的唯一库是 boost_python 和 boost_numpy。
我从源代码构建了 boost dll(使用一些补丁来管理最新的 python 要求),并为我需要支持的每个版本的 python(3.8、3.9 和 3.10)提供了专用的 Anaconda 虚拟环境。
Python 方面,当我在 python 中使用我的库时,我使用安装期间配置的默认 Anaconda 环境。我不会列出所有已安装的模块(如果需要,我可以提供更多),但这里是 python 和 numpy 版本的比较:
| Windows | Linux
---------------+---------|-------
Python version | 3.9.13 | 3.9.7
Numpy version | 1.24.1 | 1.26.3
---------------+---------|-------
Python version | 3.10.9 | 3.10.9
Numpy version | 1.23.5 | 1.23.5
我还尝试了 Windows/Python 3.10 的几个版本的 numpy(例如 1.24.1)。
我发现 3.9 和 3.10 之间的 ABI 发生了变化,但我不认为这是我的问题的原因,因为它适用于 Linux。
解决尝试
作为我解决问题的所有尝试的总结,以下是我所做的:
一些代码示例
为了以防万一它有帮助,我复制了有关单元测试模块导出的部分代码
pythonMajorVersion = sys.version_info.major
pythonMinorVersion = sys.version_info.minor
pythonVersionSubDir = "Python" + str(pythonMajorVersion) + "_" + str(pythonMinorVersion)
# add of binary directory to path
binaryDir = getBinDir() # Directory where the dlls are stored
if not binaryDir in sys.path:
sys.path.insert(1, binaryDir)
if os.name == "nt":
os.environ["PATH"] = os.environ["PATH"] + ";" + binaryDir
# The python pyds are located in a specific subdirectory called "Python3_8", "Python3_9" or "Python3_10" depending on the python I'm using
pythonBinaryDir = os.path.join(binaryDir, pythonVersionSubDir)
if not pythonBinaryDir in sys.path:
sys.path.insert(1, pythonBinaryDir)
if os.name == "nt":
os.environ["PATH"] = os.environ["PATH"] + ";" + pythonBinaryDir
# Generate the full module name, for instance 'PyUnitTestTools_1_0_0_0_x64', 1.0.0.0 being the version number
unitTestModuleName = getLibName("PyUnitTestTools")
try:
self.unittest = import_module(unitTestModuleName)
except:
print("Failed to initialize unit test framework\n" + str(sys.exc_info()[0]) + " : " + str(sys.exc_info()[1]))
我不确定将库目录添加到
sys.path
和 os.environ["PATH"]
是否必要,但我尝试了以防万一它可以提供帮助。
我无法在此特定配置(Windows、Python 3.10)中加载单元测试模块吗?
感谢 procmon (如 @Ahmed AEK 建议),我发现 python 3.9 在比 python 3.10 更多的目录中查找依赖项,即使两个 python 版本的 PATH 环境变量相同。
我的 .pyd 文件依赖于位于父目录中的 dll。该目录位于 PATH 变量环境中,但 python 3.10 忽略了它。
我在
dllDir = os.add_dll_directory(parent_path)
之前添加了 import_module(unitTestModuleName)
,现在 Python 3.10 找到了我的 dll。