PyInstaller与资源文件

很多时候,我们的代码需要依赖一些资源文件,比如配置文件、默认数据、图片等,如果用PyInstaller进行打包的话,默认是不会将这些文件打包进去的,那么该如何处理这个问题呢?

示例代码

我会通过一段很简单的代码来说明这个问题,下面的代码会通过configparser读取config.ini文件,这个例子也来自官方文档:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/python3
# -*- coding=utf8 -*-

import configparser


def read_config():
config = configparser.ConfigParser()
config.read('config.ini')
bitbucket_user = config['bitbucket.org']['User']
print(f'bitucket user: {bitbucket_user}')
topsecret_port = config['topsecret.server.com']['Port']
print(f'topsecret port: {topsecret_port}')


if __name__ == '__main__':
read_config()

config.ini的文件内容如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes

[bitbucket.org]
User = hg

[topsecret.server.com]
Port = 50022
ForwardX11 = no

现在进行打包:

1
PyInstaller --onefile load_config.py

执行时会发生报错:

1
2
3
4
5
6
7
8
$ cd dist
$ ./load_config
Traceback (most recent call last):
File "load_config.py", line 17, in <module>
File "load_config.py", line 10, in read_config
File "configparser.py", line 958, in __getitem__
KeyError: 'bitbucket.org'
[7073] Failed to execute script load_config

这是因为在load_config所在的目录下不存在config.ini文件,如果我们把config.ini拷贝一份到目录下就可以正常运行:

1
2
3
$ ./load_config 
bitucket user: hg
topsecret port: 50022

现在回到最开始的问题,如果我们并不需要用户知道这种资源文件的存在,想把config.ini一起打包进去该怎么做?

spec文件

spec文件实际上是可执行的Python代码,PyInstaller通过执行spec文件的内容来构建应用程序。可以将其理解为写C代码时的makefile文件,都是用于控制编译构建过程。遇到下面几种情况时需要修改spec文件:

  • 需要打包资源文件
  • 需要include一些PyInstaller不知道的run-time库
  • 为可执行文件添加run-time选项
  • 多程序打包

生成spec文件的方式有两种,一种是通过pyi-makespec命令生成:

1
$ pyi-makespec <options> XXX.py

注意此处的options,如果希望生成一个文件,应该使用--onefile选项。

在上面进行打包时除了生成builddist两个文件夹外,还有一个load_config.spec文件,这便是另一种方式啦:

1
$ pyinstaller <options> XXX.py

修改spec文件

先看一下目录结构:

1
2
3
4
5
6
7
$ tree
.
├── config.ini
├── load_config.py
└── load_config.spec

0 directories, 3 files

打开load_config.spec文件,按照官方文档进行操作,将我们的资源文件添加进去:

1
2
3
4
5
6
7
8
9
10
11
12
a = Analysis(['load_config.py'],
pathex=['/home/top/CodeWork/Python/ABC/static'],
binaries=[],
datas=[('config.ini', '.' )],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)

除此之前我们还需要对代码稍作修改,因为代码执行的时候是释放到/tmp文件夹的,所以我们需要告知configparser配置文件config.ini的绝对路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/python3
# -*- coding=utf8 -*-
import os
import sys
import configparser

exec_path = os.path.dirname(sys.path[0])


def read_config():
file_path = exec_path + '/config.ini'
config = configparser.ConfigParser()
config.read(file_path)
bitbucket_user = config['bitbucket.org']['User']
print(f'bitucket user: {bitbucket_user}')
topsecret_port = config['topsecret.server.com']['Port']
print(f'topsecret port: {topsecret_port}')


if __name__ == '__main__':
read_config()

现在就可以通过spec文件进行打包:

1
$ pyinstaller load_config.spec

拷贝到另一台机器上进行测试:

1
2
3
4
$ chmod +x load_config
$ ./load_config
bitucket user: hg
topsecret port: 50022

这样我们就成功的将所需要的资源文件打包进去了。

参考链接

1、https://docs.python.org/zh-cn/3/library/configparser.html
2、https://pythonhosted.org/PyInstaller/spec-files.html#adding-data-files

0%