menu E4b9a6's blog
rss_feed
E4b9a6's blog
有善始者实繁,能克终者盖寡。

Pyinstaller打包资源文件(如Yaml配置文件)

作者:E4b9a6, 创建:2021-11-05, 字数:2549, 已阅:295, 最后更新:2021-11-05

这篇文章更新于 1114 天前,文中部分信息可能失效,请自行甄别无效内容。

使用Pyinstaller可以非常方便的打包Python程序为可执行二进制文件,但程序通常会带有配置文件以便调整程序行为

如项目开发时经常需要对日志输出做调整,所以使用配置文件logging.yaml可以在开发时很方便地调整日志内容,但在实际应用环境中很少去调整

那么可以在Pyinstaller打包程序时我们应该能将这种类型的文件一并放入打包的二进制文件中

为了说明方便,就以logging.yaml作为打包的外部文件进行举例,假设项目目录如下

TEXT
Project
│   README.md
│   logging.yaml 
│
└───src
│   │   main.py

在开发时是这样引用logging.yaml文件的

Python
import os
import yaml
import logging
from logging import config

# logging配置
with open('logging.yaml', 'r', encoding='utf-8') as f:
    config = yaml.load(f, Loader=yaml.FullLoader)
    for key, value in config['handlers'].items():
        if value.get('filename'):
            if not os.path.exists(os.path.dirname(value.get('filename'))):
                os.mkdir(os.path.dirname(value.get('filename')))
    logging.config.dictConfig(config)
logger = logging.getLogger('main.common')
config_parser.read(args.conf, encoding='utf-8')
logger.info('配置文app.conf读取成功')

那么在打包时,main.spec文件中应该如此引入logging文件(只需注意datas配置)

INI
a = Analysis(['src/main.py'],
             pathex=[''],
             binaries=[],
             datas=[('logging.yaml','.')],
             hiddenimports=[],
             hookspath=[],
             hooksconfig={},
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)

如上配置打包程序后,会发现提示找不到logging.yaml文件

原因是无论是开发还是二进制运行,不带路径的文件读取即读取当前默认路径(一般是用户执行程序时的当前目录)下的文件

很显然,用户无论在任何地方执行打包后的二进制文件都是找不到logging.yaml文件的,除非用户将logging.yaml文件放到二进制文件同目录下

Pyinstaller打包后运行时会解压一个临时文件目录将datas中的文件释放至该文件夹中,所以我们需要对读取资源的代码进行微改以兼容这种情况

Python
import os
import sys
import yaml
import logging
from logging import config

def get_resource(relative_path:str):
    if getattr(sys, 'frozen', False):
        base_path = sys._MEIPASS
    else:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

# logging配置
with open(get_resource('logging.yaml'), 'r', encoding='utf-8') as f:
    config = yaml.load(f, Loader=yaml.FullLoader)
    for key, value in config['handlers'].items():
        if value.get('filename'):
            if not os.path.exists(os.path.dirname(value.get('filename'))):
                os.mkdir(os.path.dirname(value.get('filename')))
    logging.config.dictConfig(config)
logger = logging.getLogger('main.common')
config_parser.read(args.conf, encoding='utf-8')
logger.info('配置文app.conf读取成功')

这样修改后,无论是开发还是打包后运行都可以读取到logging.yaml文件


[[replyMessage== null?"发表评论":"发表评论 @ " + replyMessage.m_author]]

account_circle
email
web_asset
textsms

评论列表([[messageResponse.total]])

还没有可以显示的留言...
gravatar
[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[getEnviron(messageItem.m_environ)]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[getEnviron(messageItem.m_environ)]]