你的应用在打包中运行和源码的运行时应该是完全相同的。但是,您可能希望在运行时了解应用是从源代码运行还是已打包(“冻结”)。您可以使用以下代码来检查“我们是否已打包?”:
import sys
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
print('运行在 PyInstaller 打包环境中')
else:
print('运行在普通 Python 环境中')
当打包应用程序启动时,引导程序会设置 sys.frozen 属性,并将打包文件夹的绝对路径存储在 sys._MEIPASS 中。对于单文件夹打包,这是指向打包文件夹内部 _internal 文件夹的路径;对于单文件打包,则是引导程序创建的临时文件夹的路径(详见单文件程序的工作原理)。
访问数据文件
运行中的应用可能需要访问以下位置的数据文件:
- 与应用一起打包的文件(详见添加数据文件)。
- 用户与应用程序包一起放置的文件,例如与应用程序位于同一文件夹。
- 用户当前工作目录中的文件。
程序可以通过多种变量访问这些文件。
使用 file
未打包时,Python 变量 file 表示模块所在的当前路径。当从打包脚本导入模块时,PyInstaller 引导程序会将模块的 file 属性设置为相对于打包文件夹的正确路径。
例如,如果从打包脚本中导入 mypackage.mymodule,则该模块的 file 属性将是 sys._MEIPASS + ‘mypackage/mymodule.pyc’。因此,如果您在打包时将一个数据文件 mypackage/file.dat 添加到 mypackage/file.dat,以下代码可以获取其路径:
from os import path
path_to_dat = path.abspath(path.join(path.dirname(__file__), 'file.dat'))
在主脚本(main 模块)中,file 变量包含脚本文件的路径。在 Python 3.8 及之前版本中,该路径可能是绝对路径或相对路径(取决于脚本如何传递给 Python 解释器);在 Python 3.9 及更高版本中,它始终是绝对路径。在打包脚本中,PyInstaller 引导程序总是将 main 模块中的 file 变量设置为打包目录内的绝对路径,就好像字节编译的入口点脚本就存在于该路径中。
例如,如果您的入口脚本名为 program.py,则打包脚本中的 file 属性将指向 sys._MEIPASS + ‘program.py’。因此,可以直接使用 sys._MEIPASS 或主脚本中 file 的父路径定位数据文件。
以下代码示例展示如何获取主脚本旁边的文件 other-file.dat 的路径(无论是否被打包):
from os import path
bundle_dir = path.abspath(path.dirname(__file__))
path_to_dat = path.join(bundle_dir, 'other-file.dat')
或使用 pathlib:
from pathlib import Path
path_to_dat = Path(__file__).resolve().with_name("other-file.dat")
在打包内指定数据文件的位置
要将数据文件放置到代码期望的位置(例如相对于主脚本或打包目录),可以使用 –add-data=”source:dest” 命令行参数的 dest 参数。假设您通常在名为 my_script.py 的文件中使用以下代码定位文件 file.dat:
from os import path
path_to_dat = path.abspath(path.join(path.dirname(__file__), 'file.dat'))
对应的 PyInstaller 命令:
PyInstaller --add-data="/path/to/file.dat:."
使用 sys.executable 和 sys.argv[0]
在普通 Python 脚本中,sys.executable 是被执行的程序的路径,即 Python 解释器的路径。而在打包应用中,sys.executable 也是被执行程序的路径,但此时指向引导程序或可执行文件路径。
sys.argv[0] 是用户命令中使用的名称或相对路径,可能是相对路径或绝对路径,具体取决于平台和启动方式。
恢复库路径
在运行系统程序时,需要还原原始路径以避免加载打包的库:
env = dict(os.environ) # 复制环境变量
lp_key = 'LD_LIBRARY_PATH' # 对于 GNU/Linux 和 *BSD
lp_orig = env.get(lp_key + '_ORIG')
if lp_orig is not None:
env[lp_key] = lp_orig
参考资料: