ITADN
pyinstaller/pyinstaller-hooks-contrib
README.md
以下内容由 AI 翻译,如有问题请点此提交 issue 反馈

pyinstaller-hooks-contrib: PyInstaller 社区钩子仓库

当(你的)包无法与 PyInstaller 配合工作时,该怎么办?假设你有一些在运行时需要的数据文件? PyInstaller 不会打包这些文件。你的包需要其他 PyInstaller 无法识别的依赖项?你该如何修复?

简而言之,“钩子”文件扩展了 PyInstaller,使其能够适应 Python 包的特殊需求和方法。 “钩子”一词用于两种类型的文件。运行时钩子帮助引导程序启动应用程序,并设置 环境。包钩子(有几种类型)告诉 PyInstaller 在最终应用程序中包含什么—— 例如上述提到的数据文件和(隐藏)导入。

此仓库是许多包的钩子集合,使 PyInstaller 能够与这些包 无缝协作。

安装

pyinstaller-hooks-contrib 在安装 PyInstaller 时会自动安装,或者可以使用 pip 安装:

pip install -U pyinstaller-hooks-contrib

我看不到 a-package 的钩子

要么 a-package 在没有钩子的情况下也能正常工作,要么没有人贡献过钩子。 如果您想添加钩子,或查看有关钩子的信息, 请参阅下文。

钩子配置(选项)

支持配置(选项)的钩子及其选项记录在 支持的钩子和选项

我想帮忙!

如果您有一个想要分享的钩子,那太好了! 本页的其余部分将指导您完成贡献钩子的流程。 如果您之前来过这里,可能想直接跳到摘要清单

除非您对 git rebase -i 非常熟悉,否则请每个拉取请求只提供一个钩子! 如果您有多个,请在单独的拉取请求中提交。

设置

如果您尚未操作,请Fork 此仓库。 (如果您已经有一个 fork 但它已过期,请点击您 fork 主页上的 Fetch upstream 按钮。) 通过运行以下命令(将 bob-the-barnacle 替换为您的 github 用户名)在您的 fork 中克隆并 cd

git clone https://github.com/bob-the-barnacle/pyinstaller-hooks-contrib.git
cd pyinstaller-hooks-contrib

为你的更改创建一个新分支(将 foo 替换为包名): 你可以将此分支命名为任意名称。

git checkout -b hook-for-foo

如果您希望创建虚拟环境,请在继续下一步之前立即执行此操作。

以可编辑模式安装此仓库。 这将覆盖您当前的安装。 (请注意,您可以使用 pip install --force-reinstall pyinstaller-hooks-contrib 撤销此操作)。

pip install -e .
pip install -r requirements-test.txt
pip install flake8 pyinstaller

请注意,在 macOS 和 Linux 上,pip 可能被称为 pip3。 如果你通常使用 pip3python3,那么这里也请使用 pip3。 如果你没有打算提供测试,可以跳过第 2nd 行(但请务必提供测试!)。

添加钩子

标准钩子位于 _pyinstaller_hooks_contrib/stdhooks/ 目录中。 运行时钩子位于 _pyinstaller_hooks_contrib/rthooks/ 目录中。 只需将你的钩子复制到该目录即可。 如果你不确定你的钩子是否为运行时钩子,那么它几乎肯定是一个标准钩子。

请在钩子中注释(使用注释)任何不寻常的内容。 这里的不寻常定义为以下任何一项:

  • 长的 hiddenimport 子模块列表。 如果你需要大量的隐藏导入,请使用 collect_submodules('foo')。 为了获得额外分数,请查明为什么隐藏了这么多子模块。典型原因包括:
    • 延迟加载的子模块(模块 __getattr__() 内部的 importlib.importmodule())。
    • 动态加载的后端
    • 使用 Cython 或包含 import 语句的 Python 扩展模块。
  • 使用 collect_all()。 该函数的性能极差,并且由于设计原因它是损坏的,因为它混淆了 包和发行版。 请检查你是否确实需要收集所有子模块、数据文件、二进制文件、元数据和依赖项。 如果是,请添加注释说明(如果你知道原因 - 为什么)。 不要仅仅为了未来兼容而使用 collect_all()
  • 任何复杂的 os.path 算术(我仅指过于复杂的文件名操作)。

添加版权头

所有源文件必须包含版权头,以受我们的条款和条件约束。

如果您正在添加新的钩子(或任何新的 Python 文件),请将合适的版权头(如下所示)复制/粘贴到顶部, 并将 2021 替换为当前年份。

标准钩子或其他 Python 文件的 GPL 2 头。
# ------------------------------------------------------------------
# Copyright (c) 2024 PyInstaller Development Team.
#
# This file is distributed under the terms of the GNU General Public
# License (version 2.0 or later).
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: GPL-2.0-or-later
# ------------------------------------------------------------------
仅用于运行时钩子的 Apache 头。 同样,如果你不确定你的钩子是否为运行时钩子,那么它就是一个标准钩子。
# ------------------------------------------------------------------
# Copyright (c) 2024 PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
# ------------------------------------------------------------------

如果你正在更新一个 hook,请跳过此步骤。 不要更新版权头中的年份 - 即使它已经过时。

Test

拥有测试对于我们的持续集成至关重要。 借助它们,我们可以自动验证你的 hook 在所有平台、所有 Python 版本以及新发布的 库版本上都能正常工作。 如果没有它们,我们不知道 hook 是否已损坏,直到有人以艰难的方式发现为止。 请编写测试!!!

某些用户界面库可能无法在没有用户交互的情况下进行测试, 或者某些 Web API 的包装库可能需要凭据(以及可能的付费订阅)才能进行测试。 在这种情况下,请不提供测试。 相反,请在提交信息或你打开 pull request 时解释为什么自动测试不切实际, 然后继续到 the next step

Write tests(s)

测试应当是引发故障所需的最少代码量, 前提是你没有正在贡献的那个钩子。 例如,如果你正在为一个名为 foo 的库编写钩子, 该库在 import foo 下的 PyInstaller 中会立即崩溃,那么 import foo 就是你的测试。 如果即使没有钩子 import foo 也能正常工作,那么你就需要更有创意一些。 此类最小测试的良好来源是 你正在为其编写钩子的库的文档中的入门示例。 包的内部数据文件和隐藏依赖项容易变动,因此 测试不应直接显式检查数据文件或隐藏模块的存在—— 相反,它们应使用预期会使用这些数据文件或隐藏模块的库部分。

测试通常位于 tests/test_libraries.py。 导航到该位置并添加类似以下内容,将所有 foo 的出现替换为库的实际名称。 (注意你将其放在该文件中的位置并不重要。)

@importorskip('foo')
def test_foo(pyi_builder):
    pyi_builder.test_source("""

        # Your test here!
        import foo

        foo.something_fooey()

    """)

如果该库在过去版本中发生了重大变化,你可能需要在测试中添加版本约束。 为此,请将 @importorskip("foo") 替换为对 PyInstaller.utils.tests.requires() 的调用(例如 @requires("foo >= 1.4")),以便仅在满足给定的版本约束时才运行测试。 请注意,@importorskip 使用模块名称(即你 import 的内容),而 @requires 使用发行版名称 (即你 pip install 的内容),因此你会使用 @importorskip("PIL") 而非 @requires("pillow")。 对于大多数包,发行版名称和包名称是相同的。

在本地运行测试

不建议运行我们的完整测试套件,因为它会花费很长时间测试你未修改的代码。 相反,请使用 -k 选项搜索测试名称,单独运行测试:

pytest -k test_foo

或者使用完整路径:

pytest tests/test_libraries.py::test_foo

固定测试依赖

获取你正在使用的包的版本(pip show foo) 并将其添加到 requirements-test-libraries.txt 文件中。 其中已有的依赖项应能指导你了解语法。

在 CI/CD 上运行测试

CI/CD 现在会在你打开 pull request 时自动触发。 这些手动触发任务的说明已过时,除非在极少数情况下。

为了在所有平台上测试 hooks,我们使用 Github 的持续集成(CI/CD)。 我们的 CI/CD 有点特殊,因为它需要手动触发,并且接受参数 来限制运行哪些测试。 这与我们在本地运行时过滤测试的原因相同—— 完整的测试套件耗时很长。

首先推送你目前所做的更改。

git push --set-upstream origin hook-for-foo

在以下 URL 中,将 billy-the-buffalo 替换为你的 Github 用户名,然后打开它。 它应该会带你到你 fork 的 oneshot-test actions workflow。 你可能会被询问是否要在你的 fork 上启用 actions - 选择是。

https://github.com/billy-the-buffalo/pyinstaller-hooks-contrib/actions/workflows/oneshot-test.yml

找到 Run workflow 按钮并点击它。 如果你看不到该按钮, 从页面左侧的工作流列表中选择 Oneshot test 选项卡, 它应该会显示出来。 应该会弹出一个对话框,其中包含一个下拉菜单和 5 个单行编辑字段。 此对话框用于指定要测试的内容以及要测试的平台和 Python 版本。 其字段如下:

  1. 要运行的分支。将其设置为你正在使用的分支(例如 hook-for-foo),
  2. 要安装的软件包及其版本。 要测试的软件包是根据安装的软件包推断出来的。 通常,你可以将自己对 requirements-test-libraries.txt 文件的更改复制到该框中。
    • 设置为 foo 以测试 foo 的最新版本,
    • 设置为 foo==1.2, foo==2.3(注意逗号)以在单独的作业中测试 foo 的两个不同版本,
    • 设置为 foo bar(注意没有逗号)以在同一作业中测试 foobar
  3. 要运行的操作系统
    • 设置为 ubuntu 以仅测试 ubuntu
    • 设置为 ubuntu, macos, windows(顺序无关紧要)以测试所有三个操作系统。
  4. 要运行的 Python 版本
    • 设置为 3.9 以仅测试 Python 3.9,
    • 设置为 3.8, 3.9, 3.10, 3.11 以测试所有当前支持的 Python 版本。
  5. 最后两个选项通常可以保持不变。

点击对话框底部的绿色 Run workflow 按钮,等待几秒钟后刷新页面。 你的工作流运行记录应该会显示出来。

我们最终希望看到一个在所有操作系统和所有 Python 版本上都能通过的构建(或构建集合)。 一旦你拥有了这样的构建,请保留其 URL——在提交 pull request 时你将需要它。 如果你无法使其正常工作——没关系。 以草稿形式打开一个 pull request,展示你目前的内容,我们会尝试提供帮助。

从终端触发 CI/CD

如果你觉得反复在 Github 的 Run workflow 对话框中输入配置很繁琐, 那么我们也提供了一个 CLI 脚本来启动它。 运行 python scripts/cloud-test.py --help,它应该会引导你完成操作。 你将不得不重新输入所有详细信息,但得益于终端历史的奇妙之处, 重新运行配置只需按向上箭头然后按回车即可。

运行 Linter

我们使用 flake8 来强制执行代码风格。 如果你还没有,请使用以下命令运行 pip install flake8

flake8

没有消息就是好消息。 如果它对你的更改提出抱怨,请按照它的要求操作,然后再次运行。 如果你不理解它出现的错误,请查找每一行中的错误代码 (一个大写字母后跟一个数字,例如 W391)。

请不要修复仓库中你正在处理的部分以外的 flake8 问题。 这不仅对你来说非常无聊,而且由于其中许多更改与你正在添加或更改的钩子无关,维护者也更难 审查你的更改。

添加新闻条目

请在提交你的拉取请求之前阅读 news/README.txt。 这将要求你在创建拉取请求之前知道拉取请求的编号。 你通常可以通过将 最新的问题或拉取请求 的编号加 1 来猜测它。 或者,提交拉取请求 作为草稿, 然后在你知道你的拉取请求编号后添加、提交并推送新闻条目。

摘要

提交拉取请求前的简要检查清单:

提交拉取请求

完成上述所有步骤后,运行 git push --set-upstream origin hook-for-foo 然后创建一个 pull request。 如果你在以上任何步骤中卡住了,创建一个 draft pull request 并解释问题所在 - 我们会帮你解决... 你可以自由地将 commit messages 复制/粘贴到 Github pull request 的标题和描述中。 如果你以前从未创建过 pull request,请注意你只需再次运行 git push 即可对其进行编辑。 无需关闭旧的并创建一个新的。


如果您计划频繁贡献或有意成为开发者, 请发送电子邮件至 legorooj@protonmail.com 告知我们。