menu Chancel's Blog
rss_feed lightbulb_outline

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

类型:Python
创建时间:2021-11-05 23:40:46
更新时间:2021-11-05 23:52:39
类型:Python
更新时间:2021-11-05 23:52:39

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

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

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

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

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

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

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配置)

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中的文件释放至该文件夹中,所以我们需要对读取资源的代码进行微改以兼容这种情况

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

评论列表([[messageList.data.items.length]])

[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[messageItem.m_environ.browser]] [[messageItem.m_environ.os]] [[messageItem.m_environ.device]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[subMessage.m_environ.browser]] [[subMessage.m_environ.os]] [[subMessage.m_environ.device]]