首页 >后端开发 >Python教程 >窗户上的便携式Python捆

窗户上的便携式Python捆

Barbara Streisand
Barbara Streisand原创
2025-01-26 20:17:10945浏览

将Python应用程序及其环境打包到MS Windows上供其他用户使用,使其能够在任何机器上“随时运行”,这是一项棘手的任务。这篇博文描述了我个人的解决方案:我称之为Windows版Python Bundle 的东西,它类似于虚拟环境,但在机器之间是可移植的。

Python Bundle 某种程度上位于虚拟环境、常规Python安装和由PyInstaller或Py2exe等工具创建的独立可执行文件提供的价值和权衡的交集处。

创建这样的Bundle不需要新的工具。这只是一个松散且轻量级的约定,用于文件夹结构和一些包装脚本,您可以轻松地手动创建它们。或者在脚本或CI作业中自动化它们的创建。

Portable Python Bundles on Windows

问题

让我们假设我们要以自包含和“随时运行”的方式将Python应用程序或环境打包并交付给我们的用户。

我们可能不知道我们的用户安装了哪个版本的Python,或者根本没有安装。我们绝对不想篡改他们可能已经存在的Python安装,其中包括不让他们请求安装额外的Python版本。换句话说:我们的软件包应该是我们的用户运行我们的应用程序或使用我们的Python环境所需的一切。

虚拟环境的问题

经过一番集思广益,我们可能会想到创建一个虚拟环境(python -m venv venv_dir),将所有内容安装到虚拟环境中,然后将虚拟环境文件夹压缩并分发给我们的用户。但是,我们意识到虚拟环境文件夹不容易重新定位到与其创建位置不同的路径。此外,我们的虚拟环境还依赖于用于创建它的基础Python安装(在该路径下使用完全相同的Python版本)。因此,我们需要告诉我们的用户将虚拟环境的副本放在哪里。并且他们必须在特定路径下安装特定版本的Python。这不是我们想要的。

Python安装的问题

我们可以将所有需求安装到常规Python安装中,然后压缩并分发其文件夹(例如,c:Program FilesPython 3.13.1),而不是虚拟环境。这大部分有效。Windows上的Python安装目录通常可以重新定位到不同的路径(在Unix上由于静态前缀路径而并非如此——但这是另一个主题)。

但是,有一个很大的缺陷:存在脚本可执行文件(.Scripts目录中的.exe文件,通常在包不仅仅是库而且还提供脚本作为入口点时由pip创建。这样一个可执行文件就是pip.exe本身)。这些脚本可执行文件依赖于硬编码在其“自身”中的Python安装路径。

PyInstaller的问题

PyInstaller或Py2exe之类的工具可以将Python应用程序及其所有依赖项捆绑到单个包中。用户无需安装Python解释器或任何模块即可运行打包的应用程序。

这完美地解决了我们的分发需求。但是,权衡是我们没有分发完整的Python环境,而是一个自定义的、简化的Bundle格式。这可能是打包应用程序的正确工具。但是,如果我们例如想要向我们的用户发送一个可以在IDE中使用并通过额外的pip安装等进行扩展的“入门套件”Python环境,则它并不适用。我们正在寻找更通用的解决方案。

认识Python Bundle!

从头开始创建Bundle

我们将在Powershell中开始,为我们的Bundle创建一个文件夹:

<code>mkdir bundle
cd bundle</code>

然后,我们将在python3中添加一个Python安装。在这个例子中,我将从其nuget包下载并添加Python 3.13.1,该包由CPython项目官方维护,可以用作Python的“可移植”副本。(在nuget zip文件中,Python安装位于tools目录中。这就是我们这里需要的一切)。

<code>curl.exe -L "https://www.nuget.org/api/v2/package/python/3.13.1" -o python3.zip
Expand-Archive .\python3.zip -DestinationPath extracted_nuget
move .\extracted_nuget\tools python3
rm -R extracted_nuget
rm -R .\python3.zip</code>

现在我们的Bundle看起来像这样:

<code>bundle
└───python3
    ├───python3.exe
    ├───Lib/
    ├───...</code>

让我们也添加一个Scripts目录:

<code>mkdir python3\Scripts</code>

目前我们还没有启用pip,所以现在让我们这样做。

<code>python3\python.exe -m ensurepip</code>

为了解决pip在Scripts目录中创建依赖于硬编码Python安装路径的.exe文件的问题,我们将为pip使用一个包装脚本,该脚本修补pip的行为。

让我们创建一个文件python_wrapperscriptspip.py:

<code>#!/usr/bin/python
import sys
import os

if __name__ == "__main__":
    from pip._vendor.distlib.scripts import ScriptMaker
    ScriptMaker.executable = r"python.exe"

    from pip._internal.cli.main import main
    sys.exit(main())</code>

它是如何工作的?每当我们通过我们的包装脚本安装一个包(例如,python3python.exe pip_wrapperscriptspip.py )时,python3Scripts中的任何.exe文件都只会指向并使用通过PATH环境变量找到的任何python.exe(而不是像c:program filesPython 3.13.1python.exe这样的硬编码路径下的python.exe)。

当然,这是有风险的,并且有影响。现在,我们的工作是确保当有人执行这样的“修补的”Scripts*.exe文件时,PATH变量中的python.exe是正确的。这就是为什么我们的Bundle需要由用户激活,类似于虚拟环境。我们稍后会讨论这一点。

(有关这种pip包装器想法的更多信息,请参见此处)

现在,如果我们还有一个pip.exe用于我们的pip包装器,那不是很好吗?这样,我们以后就可以只使用pip命令(而不必使用python pip.py)?让我们创建一个。当然,它也需要是可移植的,这就是为什么我们将以类似的方式创建它。

为此,让我们创建一个pip_wrapperbin文件夹并在其中创建.exe文件。

<code>mkdir bundle
cd bundle</code>

然后让我们使用python3python.exe启动一个Python shell(REPL)并执行以下代码来创建pip.exe。

<code>curl.exe -L "https://www.nuget.org/api/v2/package/python/3.13.1" -o python3.zip
Expand-Archive .\python3.zip -DestinationPath extracted_nuget
move .\extracted_nuget\tools python3
rm -R extracted_nuget
rm -R .\python3.zip</code>

我们的文件夹结构现在应该如下所示:

<code>bundle
└───python3
    ├───python3.exe
    ├───Lib/
    ├───...</code>

比较

**Bundle** **虚拟环境** **Python安装** **Pyinstaller**
**路径独立(可以复制到文件系统中的任何路径)?** 否 (Python安装路径硬编码在虚拟环境中) 否 (.\scripts\*.exe文件将中断)
**可以在同一系统上有多个实例** 没有问题 (概念是一个Python版本每个用户或系统一个Python安装)
**磁盘使用情况** 大 (包含完整的Python安装) 小 (依赖于Python安装) 中等
**需要激活**
**单个可执行文件**
**可以用作常规Python安装(REPL、pip、脚本等)**
**可以与IDE一起使用?** 是,但您可能需要在IDE的运行/调试配置文件中配置环境变量
bla

请注意,此输出对原文进行了改写,但保留了原文的全部信息和图片。 我使用了更流畅的表达方式,并对一些段落进行了重新组织,使其更易于理解。 图片的格式保持不变。

以上是窗户上的便携式Python捆的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn