有时候我们想要发布写好的代码,使用Pyinstaller进行打包,但是我们程序有很多需要读取的外部数据,比如深度神经网络读取网络参数做预测。因为Pyinstaller打包有两种形式onefile或者onefolder。下面分别介绍。
ubuntu 16.04
pyinstaller : 3.3.1
python : 3.5.2
test.py与text.txt放在同一个目录底下
file = 'text.txt'
with open(file, 'r') as f:
while True:
line = f.readline() # 逐行读取
if not line:
break
print(line)
生成spec文件:
pyi-makespec test.py
修改test.spec:
由
# -*- mode: python -*-
block_cipher = None
a = Analysis(['test.py'],
pathex=['/home/lixin/test'],
binaries=[],
datas=[], ### <------- 改
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='test',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True )
改为:
block_cipher = None
a = Analysis(['test.py'],
pathex=['/home/lixin/test'],
binaries=[],
datas=[('test.txt','.')], ## <---- 修改此处添加外部文件
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='test',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True )
打包:
pyinstaller test.spec
此时生成两个文件夹,build以及dist,dist中的test文件夹即为发布的文件夹,可以看到,
test.txt被copy到了里面与可执行文件是同目录,完成! |
其他操作跟onefolder相同,在生成s p e c时加上参数 -F
即:
pyi-makespec -F test.py
打包完成后,此时 dist下只有一个可执行文件,运行这个可执行文件,出现错误,找不到 ‘test.txt’ !!
原因:
运行可执行文件时,会先将可执行文件进行压缩,压缩的位置在 /tmp 下,再执行,所以被打包进去的数据文件在被解压的路径下,而,程序是在运行的路径下搜索,即可执行文件的目录下,所以找不到数据文件 |
添加如下代码:
import os
import sys
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
return os.path.join(base_path, relative_path)
获取到pyinstaller临时文件夹的位置,再此位置进行文件搜索,即test.py文件修改如下:
import os
import sys
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
return os.path.join(base_path, relative_path)
file = 'text.txt'
print(resource_path(file)) ## <----- 打印一下看看是否在临时文件夹下搜索
with open(resource_path(file), 'r') as f:
while True:
line = f.readline() # 逐行读取
if not line:
break
print(line)
重复以上步骤,在dist中运行可执行文件,得到如下结果:
(/tmp/_MEIY5Vljn/ 为我的pyinstaller临时文件夹)
/tmp/_MEIY5Vljn/test.txt
完美~
参考:
1. https://stackoverflow.com/questions/7674790/bundling-data-files-with-pyinstaller-onefile
2. https://pythonhosted.org/PyInstaller/usage.html#options