跳到主要内容

Flet 打包更新

· 阅读需 10 分钟
Feodor Fitsner
Flet 创始人和开发者

问题

当您将 Flet 程序打包成在移动设备(或桌面设备)上运行的 Python 程序时,生成的包(.apk、.ipa、.exe、.app)包含您的 Python 程序、Python 解释器和 Python 标准库

如果您的程序只使用 Python 标准库,那么打包过程相对简单——Flet 会将您的代码压缩并将 Flutter 应用程序与为目标平台(Android 或 iOS)编译的 Python 解释器和标准库组合在一起。

然而,当您的 Flet 程序使用第三方包时,可能会出现问题,而这些包有数千个发布在 PyPI 或 Conda 上。

第三方包有两种类型:

纯 Python 包

“纯 Python”包是仅包含 Python 代码的包,不包括用 C、C++、Rust 或其他语言编写的扩展。您只需要一个 Python 解释器和 Python 标准库即可运行纯 Python 包,而无需关心您的操作系统或平台。

此类包的示例:httpxclickrichrequests

要验证包是否纯粹,请在 PyPI 上找到该包并导航到其“下载文件”页面。如果在“已构建分发”部分下只有一个以 -py3-none-any.whl 结尾的 wheel,那么很可能它是一个纯 Python 包,可以在任何带有 Python 的设备上“按原样”运行。

我们说*“可能”*是因为该纯包可能依赖于非纯包,例如 pydantic 是一个纯包,但它需要使用 Rust 编写的 pydantic-core 非纯包才能正常工作。

非纯 Python 包

“非纯 Python”包是部分或全部用 C、C++、Rust 或其他语言编写并必须为其运行的平台编译为机器码的包。

此类包的示例:cryptographyopencv-pythonnumpymsgpack

在非纯包的“下载文件”页面上,您会发现为各种平台预构建的轮子:macOS、Windows、Linux。

当您运行 pip install <package> 时,pip 会尝试根据轮子后缀查找特定平台和 Python 版本的轮子。

这是包开发者提供的礼貌行为,为多个平台提供预编译的轮子。某些平台可能缺少轮子,或者没有轮子——只有“源分发”中的 .tar.gz 包含包源代码。

从源代码构建包很难

要安装仅具有源代码分发的包,pip 会尝试使用安装的编译器、链接器、库和 SDK 在您的机器上构建非 Python 代码。然而,这个过程可能既漫长又容易出错。编译后的代码库可能很大,并且您的机器可能缺少所需的库或工具链。

还没有适用于 iOS 和 Android 的轮子

PyPI 上还没有适用于 iOS 和 Android 的预构建轮子,并且 PyPI 的验证过程不允许包开发者上传这些轮子,因为 iOS 和 Android 都不是 Python 的正式支持平台。

有一个过程(PEP 730PEP 738)将为 Python 3.13 添加对 iOS 和 Android 的正式支持,因此,希望开发者体验将有所改善。

包依赖关系

纯 Python 包可以导入或依赖非纯包,在将您的 Flet 应用程序打包以在移动设备上运行时,您应该牢记这一点。

例如,supabase 包用于访问 Supabase API,是一个依赖于 pydantic 包的纯包,而 pydantic 包本身依赖于用 Rust 编写的 pydantic-core 非纯包。因此,要运行使用 Supabase API 的 Flet 应用程序,打包过程应该能够找到适合目标平台的预构建轮子。如果 PyPI 没有该轮子,那么要么是 Flet 开发人员在他们的服务器上构建该轮子并托管它,要么是您在自己的机器上构建该轮子。

要查看包的依赖关系图,您可以使用 pipgrip

使用 --tree 选项运行它以获得依赖关系树视图:

$ pipgrip --tree fastapi

fastapi (0.110.3)
├── pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4 (2.7.1)
│ ├── annotated-types>=0.4.0 (0.6.0)
│ ├── pydantic-core==2.18.2 (2.18.2)
│ │ └── typing-extensions!=4.7.0,>=4.6.0 (4.11.0)
│ └── typing-extensions>=4.6.1 (4.11.0)
├── starlette<0.38.0,>=0.37.2 (0.37.2)
│ └── anyio<5,>=3.4.0 (4.3.0)
│ ├── idna>=2.8 (3.7)
│ └── sniffio>=1.1 (1.3.1)
└── typing-extensions>=4.8.0 (4.11.0)

当前方法

我们在4 个月前发布了打包的第一个版本,自那时以来,我们意识到初始方法存在多个缺陷,需要改进。

当您使用当前版本的 Flet 运行 flet build apk 时,它会下载为 Android(或如果使用 flet build ipa 运行时为 iOS)预构建的 Python 运行时和标准库。

对于非纯包,例如 numpy,Flet 要求您使用 Kivy 的“Python for Android”(p4a)工具自行构建这些包,然后提供一个路径到预构建包可以找到的“p4a”分发包。

这是问题 #1 - 您被迫与在您的机器上安装“p4a”工具并编译 Python 模块的复杂过程作斗争。

问题 #2 - 来自 p4a 的 dist 目录中的所有包将被包含在最终的应用程序包中 - 它可能包含无关的包和其他垃圾。

问题 #3 - 非纯包必须在运行 flet build 命令之前构建。您必须分析应用程序的所有依赖项,并分离出必须使用 p4a 构建的内容。

问题 #4 - p4a 的“配方”可能非常旧或缺失。您希望旧版本的包适用于您的应用程序,尝试编写“配方”并希望它能工作,或者在 Kivy 存储库中提交新配方的请求。

当您完成使用 p4a 构建非纯包时,Flet 要求您在 requirements.txt 中仅指定纯包,这在纯包直接或间接依赖于非纯包时不起作用(请参见上述示例) - 这是问题 #5。有一个最近的例子说明了这个问题:flet buildrequirements.txt 中用 flet-embed 替换 flet,但它无法知道是否有第三方包依赖于 flet,因此会安装 flet-embed 和不适用于移动设备的 flet。这不是解决方案,而是一个黑客!

打包 2.0

在 Flet 打包实现的下一次迭代中,我们将放弃 Kivy 并用 Mobile Forge 取而代之。Mobile Forge 是由 Beeware 团队基于他们在 Briefcase 和 Chaquopy 上的经验创建的。Mobile Forge 是一个用于二进制 Python 包的打包工具的清洁实现,依赖于 crossenv

Mobile Forge 和 crossenv 的主要承诺是,大多数现有的非纯 Python 包将能够通过简单地添加一个 meta.yaml 文件的配方来为 iOS 和/或 Android 编译,而无需任何黑客或补丁。

我们将使用 Mobile Forge 为 iOS 和 Android 预构建最流行的非纯 Python 包,并在我们自己的公共存储库中托管它们。您将能够使用该工具来构建和贡献其他不在我们存储库中的包。

我们在 Flet 讨论中创建了一个新的“包”类别,您可以在其中发布、投票和讨论适用于 Flet 的非纯(本机)Python 包的请求(在发布之前请查看规则)。Flet 的目标是提供最全面的预构建 Python 包目录,并使添加新包的过程尽可能友好和透明。

新版本的 flet build 将使用自定义的虚拟 pip 索引。此索引将分析依赖项,检测非纯包,并提供 pip 移动包。对于所有其他包,它将回退到 PyPI。

希望新的打包版本将在几周内可用。在我们工作期间,我们鼓励您访问并查看您需要的包是否在那里。提交请求或为现有包投票将帮助我们优先处理包的“配方”。

谢谢!