目录
实例1.No such file or directory 错误 --add-binary 参数解决
实例2.Cannot load native module 错误 强制引入解决
使用 pyinstaller 打包 python3 程序经常会出现 No such file or directory 或 Cannot load native module 错误 都是因为需要的文件未打入到最后的执行文件中,此时在使用pyinstaller时需要用到参数 --add-binary 以及在入口文件函数中加入使用代码 来解决
打包入口python文件:
pyinstaller -F -w test.py
运行打包后的执行文件:
[root@0109c795032d src]# ./dist/test
Traceback (most recent call last):
File "test.py", line 19, in <module>
from salt.client.ssh import ssh_py_shim
File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
File "PyInstaller/test/pyimod03_importers.py", line 540, in exec_module
File "salt/client/ssh/__init__.py", line 205, in <module>
File "salt/utils/files.py", line 396, in fopen
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/_MEIudOUhL/salt/client/ssh/ssh_py_shim.pyc'
[5034] Failed to execute script test
通过报错可以看到是缺少文件,pyinstaller时增加 --add-binary 参数即可,具体如下:
pyinstaller -F -w test.py \
--add-binary="/opt/python3/lib/python*/site-packages/salt/client/ssh/ssh_py_shim.py:salt/client/ssh"
参数解释:
--add-binary的语法是:--add-binary <SRC;DEST or SRC:DEST>
src是打包后的执行文件,缺少的文件在本机的位置,
dest是运行执行文件时,需要动态拷贝到的目录位置,dest是相对目录,
打包后的执行文件绝对路径 都是从 /tmp/_MEIudOUhL/ 开始的 这里是_MEIudOUhL是自动产生的每次不一样
dest只需要写相对目录即可,比如这里是 salt/client/ssh
最后的绝对路径就是 /tmp/_MEIudOUhL/salt/client/ssh
src和dest分隔符 linux下是冒号: windows下是分号;
添加此参数含义就是 把本机的 ssh_py_shim.py 文件打包到执行文件中,并且运行时动态释放到 相对目录 salt/client/ssh 中,从而解决 错误提示 的找不到文件问题
需要注意:
错误提示中报错找不到 ssh_py_shim.pyc 但此处引入的时 ssh_py_shim.py
这是因为 在整个函数调用栈中 倒数第二层 salt/client/ssh/__init__.py 里边有如下代码:
if not is_windows():
shim_file = os.path.join(os.path.dirname(__file__), "ssh_py_shim.py")
if not os.path.exists(shim_file):
# On esky builds we only have the .pyc file
shim_file += "c"
with salt.utils.files.fopen(shim_file) as ssh_py_shim:
SSH_PY_SHIM = ssh_py_shim.read()
可以看到 ssh_py_shim.py 文件也是可以的,所以只要找到 ssh_py_shim.py 或者 ssh_py_shim.pyc 在 "salt/utils/files.py", line 396, 的 fopen 函数中都不会 出现找不到文件错误
官网实例:
https://pyinstaller.readthedocs.io/en/stable/usage.html#shortening-the-command
里边展示了 多个 --add-binary 和 其他相关参数的使用实例
pyinstaller 打包运行后错误如下:
File "test/test.py", line 41, in init_test
File "salt/transport/client.py", line 27, in factory
File "salt/utils/asynchronous.py", line 70, in __init__
File "salt/transport/client.py", line 131, in factory
File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
File "PyInstaller/test/pyimod03_importers.py", line 540, in exec_module
File "salt/transport/zeromq.py", line 23, in <module>
File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
File "PyInstaller/test/pyimod03_importers.py", line 540, in exec_module
File "salt/crypt.py", line 65, in <module>
File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
File "PyInstaller/test/pyimod03_importers.py", line 540, in exec_module
File "Cryptodome/Cipher/__init__.py", line 27, in <module>
File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
File "PyInstaller/test/pyimod03_importers.py", line 540, in exec_module
File "Cryptodome/Cipher/_mode_ecb.py", line 35, in <module>
File "Cryptodome/Util/_raw_api.py", line 297, in load_pycryptodome_raw_lib
OSError: Cannot load native module 'Cryptodome.Cipher._raw_ecb': Trying '_raw_ecb.cpython-39-x86_64-linux-gnu.so': cannot load library '/tmp/_MEIPh0VXD/Cryptodome/Util/../Cipher/_raw_ecb.cpython-39-x86_64-linux-gnu.so': /tmp/_MEIPh0VXD/Cryptodome/Util/../Cipher/_raw_ecb.cpython-39-x86_64-linux-gnu.so: cannot open shared object file: No such file or directory. Additionally, ctypes.util.find_library() did not manage to locate a library called '/tmp/_MEIPh0VXD/Cryptodome/Util/../Cipher/_raw_ecb.cpython-39-x86_64-linux-gnu.so', Trying '_raw_ecb.abi3.so': cannot load library '/tmp/_MEIPh0VXD/Cryptodome/Util/../Cipher/_raw_ecb.abi3.so': /tmp/_MEIPh0VXD/Cryptodome/Util/../Cipher/_raw_ecb.abi3.so: cannot open shared object file: No such file or directory. Additionally, ctypes.util.find_library() did not manage to locate a library called '/tmp/_MEIPh0VXD/Cryptodome/Util/../Cipher/_raw_ecb.abi3.so', Trying '_raw_ecb.so': cannot load library '/tmp/_MEIPh0VXD/Cryptodome/Util/../Cipher/_raw_ecb.so': /tmp/_MEIPh0VXD/Cryptodome/Util/../Cipher/_raw_ecb.so: cannot open shared object file: No such file or directory. Additionally, ctypes.util.find_library() did not manage to locate a library called '/tmp/_MEIPh0VXD/Cryptodome/Util/../Cipher/_raw_ecb.so'
此错误也开始缺少文件,缺少 _raw_ecb.cpython-39-x86_64-linux-gnu.so 或 _raw_ecb.so 首先加入 --add-binary
pyinstaller -F -w test.py \
--add-binary="/opt/python3/lib/python*/site-packages/Crypto/Cipher/_raw_ecb.cpython-39-x86_64-linux-gnu.so:Cryptodome/Util/../Cipher"
打包运行还是报此错误,反复检查 源文件 目标地址 都是正确的,
此时需要 复制在后边工程中引用 _raw_ecb 造成错误的代码 到入口函数
让pyinstaller充分知道我会使用 _raw_ecb.cpython-39-x86_64-linux-gnu.so 中的函数,从而强制打包此so文件
加入代码具体如下:
# 入口
if __name__ == "__main__":
# 用于 pyinstaller 无法打包 salt 库
try:
ip = "127.0.0.1"
port = 1234
config = {
'id': 'root',
'log_level': 'debug',
'master_ip': ip ,
'master_port': port,
'auth_timeout': 5,
'auth_tries': 1,
'master_uri': f'tcp://{ip}:{port}'
}
salt.transport.client.ReqChannel.factory(config, crypt='clear')
except Exception as e:
pass
再用pyinstaller打包
pyinstaller -F -w test.py \
--add-binary="/opt/python3/lib/python*/site-packages/Crypto/Cipher/_raw_ecb.cpython-39-x86_64-linux-gnu.so:Cryptodome/Util/../Cipher"
此时不用 --add-binary 也是应该可以的,未测试了
出现错误 总体思路 还是通过函数调用栈来 倒推分析,
如果出现找不到文件情况 可以使用
1.--add-binary 参数
2.强制引入
来解决问题