当前位置:   article > 正文

Windows下pyinstaller的简单使用_windows操作系统pyinstaller打包教程

windows操作系统pyinstaller打包教程

搜了很多pyinstaller的资料,知识点都比较散。

另外像“怎样使用虚拟环境的pyinstaller打包程序”、“pyinstaller指定多个参数的写法”、“带具体信息的参数是怎么写的”这些问题,也没有明确的说法,只能是依葫芦画瓢。

于是花了点功夫,把pyinstaller的官方文档过了一下,并参照官方文档进行了释义、筛选,再结合平时使用pyinstaller的零散知识,最终整理成本篇文章。

本文围绕“使用cmd窗口进行程序打包”展开,另外也详细介绍了出现问题时的常用解决方法。关于pyinstaller的复杂用法(spec、hook),仅简单提及。


一、安装pyinstaller

安装:cmd窗口,pip install pyinstaller

升级:cmd窗口,pip install --upgrade pyinstaller

cmd窗口的pip install xxx,是安装在默认环境(系统环境)中,若想给虚拟环境安装python包,可参考笔者另一篇文章:windows下python虚拟环境的创建,以及怎么在VS Code中使用虚拟环境

注意:

1、虚拟环境,需要单独安装pyinstaller,不然后续打包时调用的是系统环境的pyinstaller,会把系统环境中所有的包、模块都打包进去(如何使用虚拟环境的pyinstaller打包程序,见“二  3”)。

2、最好把pillow也安装到虚拟环境,图标处理时会用到,如果你的图标文件没问题,那没有pillow也可以。

二、使用pyinstaller

1、基本用法

cd到“要打包的脚本”所在的位置,然后执行①或②:

① pyinstaller [options] script [script…]

  • pyinstaller在虚拟环境下需要重命名,使用新名字替代即可(详见“二 3”)
  • [options] 为可选参数,后文会进行详细介绍
  • script 为脚本名,即需要打包的.py文件
  • [script…] 是多脚本打包

② pyinstaller specfile

打包的时候要配置很多参数时,用spec配置会更方便。spec文件告诉pyinstaller如何处理脚本。根据spec文件打包时,在命令行中给出的大部分参数无效,会被忽略并替换为spec文件中的参数。

可以使用“pyi-makespec name.py”,创建一个spec文件。另外使用方法①,也会自动生成spec文件。

对于许多使用pyinstaller的情况,您不需要检查或修改spec文件。通常,仅需将所有所需的信息(例如隐藏导入)作为pyinstaller命令的选项提供,并让其运行即可。在四种情况下,修改spec文件会很有用:

  • 当您想要通过应用程序捆绑数据文件时
  • 当您想要包括运行时库(.dll或.sofiles),pyinstaller没有从任何其他来源获取的库时
  • 当您想要向可执行文件添加Python运行时选项时
  • 当您想要创建具有合并常见模块的多程序捆绑包时

pyinstaller会创建一些文件、文件夹:

  • 在脚本所在目录下,创建一个与脚本同名的spec文件。
  • 检测脚本所在目录下是否有build文件夹,没有将自动创建;并在build中写入一些日志文件与工作文件。
  • 检测脚本所在目录下是否有dist文件夹,没有将自动创建;并在dist文件夹中创建一个与脚本同名的文件/文件夹,生成的可执行文件就在这里。

2、options(参数,大小写敏感)

参数和具体信息之间可以用空格相连,也可以用等号相连,像下面四种写法都是合法的:

  • -n qwe
  • -n=qwe
  • --name qwe
  • --name=qwe

常用参数:

-D        -F        -n        -c        -w        -i        -p        --log-level        -d

注:有的参数需要给出具体信息,这部分会单独加粗标蓝;

2.1 位置参数

脚本名称(.py文件名)

        要处理的脚本文件的名称,或一个.spec文件。如果指定了.spec文件,则大多数选项都不需要,不会起作用。

2.2 可选参数

-h,--help

        显示此帮助消息并退出。

-v,--version

        显示程序版本信息并退出。

--dispath DIR

        放置捆绑应用程序的位置(默认值:./dist)。

--workpath WORKPATH

        放置所有临时工作文件(.log、.pyz 等)的位置(默认值:./build)。

-y,--noconfirm

        替换输出目录(默认值:spec文件路径/dist/spec文件名)而无需确认。

--upx-dir UPX_DIR

        UPX实用程序的路径(默认值:搜索执行路径)。

-a,--ascii

        不包括Unicode编码支持(默认情况下,如果可用,则包括Unicode编码支持)。

--clean

        在构建前清除 PyInstaller 缓存和临时文件。

--log-level LEVEL

        生成时控制台消息的详细程度。LEVEL 可以是 TRACE、DEBUG、INFO、WARN、DEPRECATION、ERROR、FATAL(默认值:INFO)。也可以通过 PYI_LOG_LEVEL 环境变量设置并覆盖。

2.3 生成什么

-D,--onedir

        创建一个包含可执行文件的单文件夹(默认值)。

-F,--onefile

        创建一个可执行文件。

--specpath DIR

        生成的 spec 文件存放的文件夹(默认值:脚本所在目录)。

-n NAME,--name NAME

        指定打包应用的名称和 spec 文件名称(默认值:第一个脚本的名称)。

2.4 要捆绑什么,在哪里搜索

--add-data <SRC;DEST or SRC:DEST>

        添加非二进制文件或文件夹到可执行文件中。路径分隔符是平台特定的,使用 os.pathsep(在 Windows 上是‘;’,在大多数 Unix 系统上是‘:’)。该选项可以多次使用。

--add-binary <SRC;DEST or SRC:DEST>

        添加二进制文件到可执行文件中。有关更多详细信息,请参见--add-data 选项。该选项可以多次使用。

-p DIR,--paths DIR

        一个用于搜索导入路径的路径(类似于使用 PYTHONPATH)。可以使用多个路径,用':'分隔,或者多次使用此选项。相当于在 spec 文件中提供一个 pathex 参数。

--hidden-import MODULENAME,--hiddenimport MODULENAME

        命名脚本中未显示的导入模块。该选项可以多次使用。

--collect-submodules MODULENAME

        收集指定包或模块的所有子模块。该选项可以多次使用。

--collect-data MODULENAME,--collect-datas MODULENAME

        收集指定包或模块的所有数据文件。该选项可以多次使用。

--collect-binaries MODULENAME

        收集指定包或模块的所有二进制文件。该选项可以多次使用。

--collect-all MODULENAME

        收集指定包或模块的所有子模块、数据文件和二进制文件。该选项可以多次使用。

--copy-metadata PACKAGENAME

        复制指定包的元数据。该选项可以多次使用。

--recursive-copy-metadata PACKAGENAME

        复制指定包和其所有依赖项的元数据。该选项可以多次使用。

--additional-hooks-dir HOOKS_PATH

        用于搜索钩子的附加路径。该选项可以多次使用。

--runtime-hook RUNTIME_HOOKS

        自定义运行时钩子文件路径。运行时钩子是与可执行文件捆绑的代码,并在任何其他代码或模块之前执行,以设置运行时环境的特殊特性。该选项可以多次使用。

--exclude-module EXCLUDES

        忽略的可选模块或包(非路径名而是 Python 名称)。该选项可以多次使用。

--splash IMAGE_FILE

        (实验性)向应用程序添加一个带有图像 IMAGE_FILE 的启动画面。在解压缩时,启动画面可以显示进度更新。

2.5 如何生成

-d {all,imports,bootloader,noarchive},--debug{all,imports,bootloader,noarchive}

        提供帮助调试冻结应用程序。可多次提供此参数以选择以下多个选项。

  • all:所有三个选项。
  • imports:指定-v选项给基础 Python 解释器,使其在每次初始化模块时打印一条消息,显示从哪个位置(文件名或内置模块)加载模块。请参见 https://docs.python.org/3/using/cmdline.html#id4。
  • bootloader:告诉启动程序在初始化和启动捆绑应用程序时发布进度消息。用于诊断缺失导入的问题。
  • noarchive:存储所有冻结的 Python 源文件,而不是将它们存储为结果执行文件中的存档。

--python-option PYTHON_OPTION

        指定要在运行时传递给 Python 解释器的命令行选项。当前支持“v”(等效于“--debug imports”)、“u”和“W <warning control>”。

-s,--strip

        Apply a symbol-table strip to the executable and shared libs(不建议在 Windows 上使用)。

--noupx

        禁止使用 UPX,即使它可用(在 Windows 和 *nix 之间的工作方式不同)。

--upx-exclude FILE

        当使用 upx 时,防止对二进制文件进行压缩。如果在压缩期间 upx 损坏某些二进制文件,则通常使用此选项。FILE 是不带路径的二进制文件名。该选项可以多次使用。

2.6 Windows和Mac Os X的特定参数(控制台、图标)

-c,--console,--nowindowed

        打开控制台窗口,禁止普通窗口。(默认值)

-w,--windowed,--noconsole

        打开普通窗口,禁止控制台窗口。

-i <FILE.ico FILE.icnsImage“NONE”>,

--icon < FILE.ico FILE.icnsImage“NONE”>

  • FILE.ico:将图标应用于 Windows 可执行文件。
  • FILE.icns:将图标应用于 Mac OS 上的 .app 捆绑包。
  • Image:将图像作为图标,如果输入的图像文件不是平台格式(Windows 上的 ico、Mac 上的 icns),则 PyInstaller 尝试使用 Pillow 将图标转换为正确的格式(如果安装了 Pillow)。
  • NONE:不应用任何图标,从而使操作系统显示一些默认值(默认值:应用 PyInstaller 的图标)。

--disable-windowed-traceback

        禁用窗口(noconsole)模式下无处理异常的 traceback 转储(仅适用于 Windows 和 macOS),并显示一条消息,说明禁用了此功能。

2.7 Windows特定参数

--version-file FILE

        将版本资源从FILE加入exe文件中。

-m <FILE或XML>,--manifest <FILE或XML>

        把manifest或XML加入到exe文件中。

--no-embed-manifest

        生成外部的.exe.manifest文件,而不是将manifest嵌入exe文件中。仅适用于onedir模式; 在onefile模式中,无论这个选项如何,manifest都会被嵌入。

-r RESOURCE,--resource RESOURCE

        向Windows可执行文件添加或更新资源。

        RESOURCE有一到四个项:FILE[,TYPE[,NAME[,LANGUAGE]]]。

  • FILE可以是数据文件或exe/dll。
  • 对于数据文件,至少必须指定TYPE和NAME。
  • LANGUAGE默认为0,或可以指定为通配符*以更新指定类型和名称的所有资源。
  • 对于exe/dll文件,如果省略了TYPE、NAME和LANGUAGE,或将其指定为通配符*,所有来自FILE的资源都将添加/更新到最终可执行文件中。

--uac-admin

        使用此选项创建一个请求在应用程序启动时提升权限的清单。

--uac-uiaccess

        使用此选项可允许提升后的应用程序使用远程桌面。

3、使用虚拟环境的pyinstaller打包程序

① 把虚拟环境scripts目录下的pyinstaller.exe改个名字(不然和系统环境的pyinstaller重名,重名则优先使用系统环境的pyinstaller)

② 激活虚拟环境

③ cd到“要打包的.py文件”所在的位置,使用重命名后的pyinstaller进行打包即可

三、当出现问题时

1、找出问题所在的方法

1.1 构建时的信息

当Analysis步骤运行时,它会产生错误和警告消息。如果“--log-level”选项允许,这些信息将显示在命令行之后。Analysis还将消息放在work-path=目录下名为build/name/warn-name.txt的警告文件中。

当Analysis检测到一个导入并且它命名的模块找不到时,它会创建一条消息。当在包(__init__.py模块)中声明类或函数时,也可能产生消息,并且导入指定了package.name。在这种情况下,分析无法判断name是否应该引用子模块或包。

“未找到模块”消息不被归类为错误,因为通常有很多这样的消息。例如,许多标准模块有条件地为不同的平台导入模块,这些平台可能存在,也可能不存在。

所有" module not found "消息都被写入build/name/ warning -name.txt文件。它们不会显示在标准输出中,因为它们太多了。检查警告文件;通常会有几十个模块找不到,但他们的缺席没有影响。

当你运行打包的应用程序时,它会以ImportError终止,这时就该检查警告文件了。

1.2 构建时的依赖关系图

每次运行时,PyInstaller都会在构建文件夹中写入一个关于依赖关系的交叉引用文件:work-path=目录中的build/name/xref-name.html是一个HTML文件,列出了导入图的全部内容,显示哪些模块被哪些模块导入。你可以在任何浏览器中打开它。找到一个模块名,然后继续单击“imported by”链接,直到找到导致包含该模块的顶级导入

如果你在pyinstaller命令中指定--log-level=DEBUG, pyinstaller会另外生成一个GraphViz输入文件,表示依赖关系图。文件是build/name/graph-name。在工作路径=目录中的点。你可以使用任意GraphViz命令来处理它,例如,dot,生成导入依赖项的图形化显示。

这些文件非常大,因为即使是最简单的“hello world”,Python程序最终也包括大量的标准模块。由于这个原因,图形文件不是很有用。

1.3 构建时的python错误

PyInstaller有时会通过引发Python异常而终止。在大多数情况下,从异常消息中可以清楚地看出原因,例如“您的系统不受支持”或“Pyinstaller至少需要Python 3.7”。其他的则清楚地指出了应该报告的错误。

然而,其中一个错误可能令人费解:IOError("Python库未找到!")PyInstaller需要捆绑Python库,它是Python解释器的主要部分,链接为动态加载库。该文件的名称和位置因使用的平台而异。一些Python安装默认不包括动态Python库(可能存在静态链接的库,但不能使用)。您可能需要安装某种类型的开发包。或者,库可能存在,但不在PyInstaller正在搜索的文件夹中。

在不同的操作系统中,PyInstaller查找python库的位置是不同的,但在大多数系统中都会检查/lib和/usr/lib,如果不能将python库放在那里,请尝试在环境变量LD_LIBRARY_PATH (GNU/Linux)或DYLD_LIBRARY_PATH (macOS)中设置正确的路径

1.4 获取调试信息

--debug=all选项(及其_choices_)提供了大量诊断信息。这在开发复杂包或应用程序看起来不起作用时很有用,或者仅了解运行时如何工作。

通常,调试进度消息会发送到标准输出。如果在打包Windows应用程序时使用了--windowed选项,则会将它们发送到任何附加的调试器。如果您没有使用调试器(或没有调试器),则可以使用DebugView免费(beer)工具来显示此类消息。它必须在运行打包的应用程序之前启动。

对于--windowed,在macOS应用程序中不会被显示。

考虑为您的生产版本打包而不使用--debug。调试消息需要系统调用并对性能产生影响。

1.5 获取python的详细导入

你可以使用--debug=imports构建应用程序,这将向嵌入式Python解释器传递-v(详细导入)标志。这可能非常有用。即使是表面上工作良好的应用程序,也可能会提供信息,以确保它们从装捆包中获得所有导入,而不会泄漏到本地安装的Python中。

Python冗长和警告消息始终进入标准输出,并且在使用--windowed选项时不可见。请记住,不要将其用于您的生产版本。

1.6 弄清楚为什么你的GUI程序无法启动

如果您使用了--windowed选项,则打包应用程序可能无法启动,并显示类似“Failedtoexecute script my_gui”的错误消息。在这种情况下,您需要获取更多详细的输出以了解情况。

  • 对于macOS,您可以在命令行中运行应用程序,即./dist/my_gui在终端(_Terminal中)而不是点击my_gui.app。
  • 对于Windows,您需要重新打包应用程序,并删除--windowed选项。然后,您可以从命令行运行生成的可执行文件,即my_gui.exe。
  • 对于Unix和GNU/Linux,没有--windowed选项。无论如何,如果GUI应用程序失败,则可以在命令行中运行您的应用程序,即…/dist/my_gui。

这将为您提供阻止应用程序初始化的相关错误,然后您可以继续进行其他调试步骤。

1.7 不允许操作的错误(权限不足)

如果您使用--onefile并且它无法运行,您的程序将出现错误,例如:

  1. ./hello: errorwhile loading shared libraries: libz.so.1:
  2. failed to map segmentfrom sharedobject: Operationnotpermitted

这可能是由/tmp目录的错误权限引起的(例如,文件系统使用noexec标志进行挂载)。

解决此问题的简单方法是,在环境变量TMPDIR中设置一个在没有noexec标志挂载的文件系统中的目录的路径,例如:export TMPDIR=/var/tmp/

2、帮助pyinstaller查找模块

2.1 扩展路径

如果分析识别到需要模块却找不到该模块,则通常是因为脚本正在操作sys.path。在这种情况下,最简单的方法是使用--paths选项列出脚本可能搜索导入的所有其他位置:

  1. --paths=/path/to/thisdir \
  2. --paths=/path/to/otherdir

这些路径将在spec文件中以pathex参数的形式被记录下来。它们将被添加到当前分析期间的sys.path中。

2.2 列出隐藏导入

如果分析认为已找到所有导入项,但应用程序却以导入错误失败,则问题是隐藏导入项;也就是说,在分析阶段不可见的导入项。

当代码使用__import__(),importlib.import_module()或可能使用exec()或eval()时,可能会出现隐藏导入项。当扩展模块使用Python/C API执行导入时,也可能会出现此情况。发生这种情况时,分析不能检测到任何东西。没有警告,只有运行时的ImportError。

要找到这些隐藏的导入项,请使用--debug=imports标志构建应用程序并运行它。

知道需要哪些模块之后,您可以使用--hidden-import命令选项,或编辑spec文件,或使用钩子文件(请参见_理解PyInstaller Hooks_以下),将所需的模块添加到包中。

2.3 扩展软件包的__path__(...)

Python允许脚本通过__path__机制扩展用于导入的搜索路径。通常,导入模块的__path__只有一个条目,即找到__init__.py的目录。但是__init__.py可以自由地扩展它的__path__来包含其他目录。例如:win32com.shell。Shell模块实际解析为win32com/win32comext/ Shell / Shell .pyd。这是因为win32com/__init__.py附加了…/win32comext到它的__path__。

因为导入模块的__init__.py在分析过程中并没有实际执行,所以PyInstaller看不到它对__path__所做的更改。我们使用与隐藏导入相同的钩子机制修复了这个问题,并添加了一些额外的逻辑。

注意,以这种方式钩住的__path__操作只适用于Analysis。在运行时,所有的导入都被拦截并从bundle中得到满足。win32com。Shell的解析方式与win32com相同。还有win32com。__path__对../win32comext一无所知。

偶尔,这还不够。

2.4 改变运行时的行为(...)

更奇怪的情况可以通过运行时挂钩来容纳。这些是操作环境的小脚本,使您的脚本运行之前提供附加的顶级代码。

有两种提供运行时挂钩的方法。您可以使用选项--runtime-hook= path-to-script_来命名它们。

其次,一些运行时挂钩是提供的。在分析结束时,由分析阶段产生的模块列表中的名称在PyInstaller安装文件夹中查找loader/rthooks.dat。这个文本文件是一个Python字典的字符串表示形式。键是模块名称,值是挂接脚本路径名的列表。如果有匹配,这些脚本将被包含在捆绑应用程序中,并在启动您的主脚本之前调用。

使用选项命名的挂钩按给定顺序执行,并在任何安装的运行时挂钩之前执行。如果你指定  --runtime-hook=file1.py  --runtime-hook=file2.py,那么运行时的执行顺序将是:

  • file1.py的代码。
  • file2.py的代码。
  • 在rthooks/rthooks.dat中找到的包含模块的任何指定钩子。
  • 您的主脚本。

以这种方式调用的挂钩虽然需要小心其导入内容,但几乎可以做任何事情。编写运行时钩子的一个原因是覆盖一些模块的某些函数或变量。 Django运行时钩子的一个很好的例子(请参阅PyInstaller文件夹中的loader/rthooks/pyi_rth_django.py)。 Django动态地导入一些模块,并寻找一些.py文件。但是,在单个文件捆绑中,.py文件不可用。我们需要以返回值列表的方式重写函数django.core.management.find_commands。运行时挂钩如下所示:

  1. import django.core.management
  2. def_find_commands(_):
  3. return """cleanup shell runfcgi runserver""".split()
  4. django.core.management.find_commands = _find_commands

3、获取最新版本

如果您有某种原因认为在PyInstaller中发现了一个错误,则可以尝试下载最新的开发版本。这个版本可能具有尚未在PyPI中的修复或功能。您可以从PyInstaller下载页面下载最新的稳定版本和最新的开发版本。

你也可以直接使用pip安装PyInstaller的最新版本:

pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/657275
推荐阅读
相关标签
  

闽ICP备14008679号