ImportError: DLL load failed: The specified module could not be found -- IBM DB2

Posted

技术标签:

【中文标题】ImportError: DLL load failed: The specified module could not be found -- IBM DB2【英文标题】: 【发布时间】:2019-04-24 22:51:31 【问题描述】:

我知道有很多人遇到同样的问题,但这是我的情况,我无法找到完全相同的问题。我正在使用 pyinstaller 构建可执行文件,并且不断收到 importError。我正在使用 ibm_db 包连接到 IBM DB2 数据库并使用 pandas to_sql 方法插入到表中。在我添加 SQL 代码之前,我在我的程序上使用了 pyinstaller,所以我很确定这与我尝试连接到 DB2 有关,但对于我的一生,我无法弄清楚这一点。

我在运行 pyinstaller 时收到很多警告和信息消息,但没有看到任何错误。只有在我尝试执行 pyinstaller 构建的可执行文件时才会收到错误消息。

我曾尝试在虚拟环境中运行它以尝试隔离问题,但我对虚拟环境不太熟悉,因此我不再尝试使用它。

Traceback (most recent call last):
  File "rebate_gui_sql.py", line 9, in <module>
  File "c:\users\dt24358\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 627, in exec_module
    exec(bytecode, module.__dict__)
  File "site-packages\ibm_db.py", line 10, in <module>
  File "site-packages\ibm_db.py", line 9, in __bootstrap__
  File "imp.py", line 342, in load_dynamic
ImportError: DLL load failed: The specified module could not be found.
[11020] Failed to execute script rebate_gui_sql

更新:2019 年 5 月 1 日来自下面的评论,这是我的简单程序

import pandas as pd
from tkinter import *
from tkinter import ttk
import ibm_db
import ibm_db_dbi as db
from sqlalchemy import create_engine

class Application(Frame):

    def __init__(self, master):
        ttk.Frame.__init__(self, master)  
        self.master = master
        self.run_process()

    def run_process(self):
        engine = create_engine("db2+ibm_db://userid:password@url:port/database")
        conn = engine.connect()
        print("Connected to " + str(engine))

        sql = '''
            Select *
            from rebteam.pd5
            fetch first row only
            '''

        df = pd.read_sql(sql, conn)
        print(df)

        df.to_csv(r'c:\users\dt24358\scripts\pricing tool\GUI_SQL\test.csv', index=False)
        self.result_label = Label(root, text="Select of PD5 Successful", bg="light green", width=80, justify=LEFT)
        self.result_label.grid(row=0,columnspan=2)

root=Tk()
root.title("Rebate Bid Data Upload")
root.configure(background="light green")
app = Application(root)
root.mainloop()

【问题讨论】:

什么平台和版本的 Db2? AFAIK,python 包是为 Db2 Linux、Unix、Windows (LUW) 设计的。如果连接到 Db2 for i (AS/400 iSeries) 或 Z/OS,您需要购买并安装 Db2 Connect 产品。 您是否安装了 Db2 客户端?如果是这样,究竟是什么包装?如果没有,您应该这样做。 @Charles -- 我们在 AIX 7.1 上运行 DB2 版本 11.1 Fixpack 2 @mustaccio -- 我不确定您所说的 DB2 客户端是什么意思?我安装了 ibm_db 包和 ibm_db_dbi 包,我正在使用 sqlalchemy。正如我所说,如果我通过 shell 运行,我可以让程序正常工作,但是当我使用 pyinstaller 编译它时,我遇到了上述错误。 @mao -- 我使用 PIP 安装(不是 easy_install)安装了 ibm_db 和 ibm_dbi。我在 Python 3.7.3 上运行,运行 32 位。 【参考方案1】:

此答案与这些版本相关:

python - up to 3.7 (but not higher)  
pyinstaller 3.4  
setuptools 41.0.1 
ibm_db 3.0.1
ibm_db_sa 0.3.4
sqlalchemy 1.3.3

这里有单独的问题。

直接的问题(ImportError)是无法加载ibm_db.dll

发生 ImportError 是因为 pyinstaller 不会将外部(非 python)库复制到包中,除非您明确要求发生。

pyinstaller 也不会将 Db2-client 复制到包中,除非您明确告诉它这样做,这意味着如果您部署内置可执行文件的目标主机名还没有预配置的预安装 Db2-client,那么您还会遇到无法加载 ibm_db 模块的情况。

pyinstaller 选项--add-binary 为某些类型的 ImportError 提供了解决方法,请参见下面的示例。如果您不使用 SQLAlchemy,请跳过此答案的这些部分。

当您的目标环境缺少 Db2 驱动程序时,pyinstaller 选项 --add-data 提供了一种添加目录(例如用于添加 Db2 驱动程序的 clidriver 目录)的解决方法。

请注意,此答案不需要您使用 SQLAlchemy,如果您仅使用 ibm_db(或 ibm_db_dbi),则该答案也很重要,在这种情况下,只需跳过 SQLAlchemy 部分。

如果您的 python 脚本使用 SQLAlchemy 访问 Db2,那么在使用 pyinstaller 构建后,您可能会在运行时看到第二个症状。运行时症状是:

"sqlalchemy.exc.NoSuchModuleError: 无法加载插件: sqlalchemy.dialects:ibm_db_sa"

"sqlalchemy.exc.NoSuchModuleError: 无法加载插件: sqlalchemy.dialects:db2.ibm_db"

(取决于给 create_engine() 的 url 的前缀)

此症状 sqlalchemy.exe.NoSuchModuleError 并非特定于 Db2,但在通过带有 external 方言(Db2、teradata、雪花、presto)的 SQLAlchemy 使用时会影响其他数据库,...)。使用 SQLAlchemy 内部方言的数据库可能开箱即用。

这是 SQLAlchemy 的一种解决方法,其他解决方法也是可能的。

SQLAlchemy 外部方言使用 pkg_resources entry_points 来允许 SQLAlchemy 利用它们,但是 pyinstaller 还不能处理这些,没有你的帮助。这样的入口点信息是一种关于模块的元数据。

此解决方法使用 pyinstaller 挂钩来收集相关模块的元数据,并告诉 pyinstaller 包含这些挂钩文件的目录。对于带有 SQLAlchemy 的 Db2,需要三个挂钩文件,hook-ibm_db.py, hook-ibm_db_sa.py, hook-sqlalchemy.py。我选择将这些钩子文件放在与我的源文件 python 脚本相同的目录中。

每个文件的内容只有两行,内容的区别仅在于其中包含的模块名称。这是hook-sqlalchemy.py文件之一的示例(对于其他2个必需文件,只需适当地替换模块名称即可):

from PyInstaller.utils.hooks  import  copy_metadata

datas = copy_metadata('sqlalchemy')

要通过--add-binary method 添加ibm_db.dll,您可以使用命令行选项来pyinstaller 或编辑规范文件。

要单独处理 ibm_db.dll 的加载失败,只需使用 --add-binary 附加选项,如下所示:

pyinstaller -y --add-binary  %LOCALAPPDATA%\Programs\Python\Python37\Lib\site-packages\ibm_db_dlls\ibm_db.dll;.\ibm_db_dlls your_script.py

如果你想在你的包中包含 clidriver,首先通过以下方式找到其位置的完全限定路径名:

pip show ibm_db

并在该命令的输出中看到 Location: 行,它具有完全限定路径名的第一部分,因此您将 \CLIDRIVER 附加到该路径并在 --add-data 附加选项中使用它,如下所示:

--add-data="c:\path\to\clidriver;.\clidriver"

如果您在捆绑包中包含 clidriver,还有其他注意事项,请参阅下面的注释部分。

对于也使用 SQLAlchemy 的应用,您需要额外的步骤。

假设 ibm_db.dll 位于此目录中:

%LOCALAPPDATA%\programs\python\python37\lib\site-packages\ibm_db_dlls

然后在 CMD.EXE shell 中创建一个变量以指向该位置:

> set ibm_db_path=%LOCALAPPDATA%\programs\python\python37\lib\site-packages\ibm_db_dlls

对于 MS-Windows 批处理文件(使用 ^ 作为行继续符),处理上述两种变通方法的 pyinstaller 命令行示例是:

pyinstaller -y ^
--additional-hooks-dir=. ^
--hidden-import ibm_db_sa.ibm_db ^
--hidden-import ibm_db_dbi ^
--hidden-import ibm_db ^
--add-binary  %LOCALAPPDATA%\Programs\Python\Python37\Lib\site-packages\ibm_db_dlls\ibm_db.dll;.\ibm_db_dlls ^
 your_script.py

注意事项:

如果您的 python 脚本显式导入 SQLAlchemy 模块,那么您不需要 需要通过--hidden-import 选项指定它们(购买你仍然需要 SQLAlchemy 在捆绑后运行的钩子)。

对于最高 3.0.2 的 ibm_db 版本,ibm_db.dll 需要位于您的子目录 ibm_db_dlls 中 bundle,这是在 --add-binary 选项。

如果您正在为 Linux/Unix 构建,而不是 ^。像往常一样使用 \ 作为行继续符。

如果您打算将构建的可执行文件复制到新主机名,并且该新主机名尚未预安装 Db2-client ,并且您不希望在目标上安装单独的 Db2-client,那么您可以使用上面显示的--add-data 选项将 clidriver 与 pyinstaller 输出捆绑在一起。

如果您捆绑 clidriver,请注意,如果它们位于非默认位置,您可能还需要捆绑其配置文件(例如 db2dsdriver.cfgdb2cli.ini),具体取决于您的代码是使用外部配置的 DSN 还是长连接字符串。如果您不捆绑此类配置文件(隐式或显式)并且您将构建环境部署到与构建环境不同的主机名,那么您将需要在目标主机名处重新配置这些文件。这些文件的默认位置在clidriver\cfg 目录中,如前所述,该目录将通过--add-data 包含在内。

如果您捆绑了 clidriver,并且如果您通过 TLS/SSL 使用与 Db2 的加密连接,请注意,当您运行 pyinstaller 构建时,您可能还需要捆绑其他文件,例如证书、密钥库/存储文件等。

如果您捆绑了 clidriver,请注意 IBM 每年都会通过错误修复和安全修复以及新功能刷新此组件几次,因此您可能需要定期刷新可执行文件以防止它们因被冻结而成为安全漏洞及时更新旧版本。

如果您捆绑了 clidriver,并且如果您需要在目标主机名上使用 odbcad32 来配置 Db2 DSN,那么在目标主机名上进行部署后,请记住在目标主机名上运行 clidriver\bin\db2cli install -setup 命令。

【讨论】:

感谢您的所有帮助。终于做到了。我能够让 pyinstaller 正确加载,现在可以启动可执行文件。 非常感谢您的帮助!多年来,我一直在寻找解决方案。我正在使用规范文件,发现包含您提到的 add-binary 参数以及隐藏的导入可以解决问题。但是,我必须使用参数,而不是规范文件中的“二进制”选项。 clidriver 完全被它自己拉了进去。 IBM_DB 3.0.2 Windows Python 2.7【参考方案2】:

感谢您的提问和回答。我在 Windows7 Python3.7 ibm-db 3.0.1 中遇到过同样的情况 根据你的提示,我认为原因是exe在clidriver\bin和ibm_db.dll中找不到*.dll, 并用类似的方法分两步解决

第一: 和你一样,添加clidriver目录到系统路径

**\site-packages\clidriver\bin

第二 带参数的打包 --add-binary

 Pyinstaller --add-binary **\Lib\site-packages\ibm_db_dlls\ibm_db.dll;.\ibm_db_dlls myproject.py

那就好了!

类似问题: PyQt5 Executable is crashing with Missing DLL

【讨论】:

以上是关于ImportError: DLL load failed: The specified module could not be found -- IBM DB2的主要内容,如果未能解决你的问题,请参考以下文章

ImportError: DLL load failed with error code -1073741795

ImportError: DLL load failed: 找不到指定的模块。

Python ImportError: DLL load failed: %1 不是有效的 Win32 应用程序。

Python ImportError: DLL load failed: %1 不是有效的 Win32 应用程序。

ImportError: DLL load failed: 找不到指定的模块。

ImportError: DLL load failed: 找不到指定的模块