专业方式打包和发布 Python 库及脚本

41 投票
9 回答
9743 浏览
提问于 2025-04-16 15:43

我现在的任务是打包和发送一个商业应用程序包,里面会包含:

  1. 我们自己开发的一个Python库
  2. 一些依赖于上面那个库的Python程序
  3. 一些不是我们开发的额外库,但它们是我们库的依赖
  4. 一个完整的Python安装(Python 2.6)
  5. 其他语言的额外内容、库和程序。这些不在我们关心的范围内,因为它们和上面的内容没有直接关系,当前的发货流程已经可以正常工作。

这个包会发往Linux、OSX和Windows系统。在Linux上,它以简单的tar.gz格式分发。用户只需解压tar.gz文件,然后在.bashrc中运行一个提供的bash脚本,这样环境就能正确设置。在Mac上,它是一个dmg文件。在Windows上,我不太清楚。今天Windows的同事不在,但我看到有一个exe文件是以某种方式创建的。

接下来我会更详细地解释上面的几点。

我们的Python库

我们不想公开源代码,所以只想提供编译后的Python文件。为了让它们更难被篡改,我们希望能有更好的策略,即使这需要一些复杂的技术(例如,我曾经见过从一个“损坏”的.zip文件中导入东西的神奇操作)。目前这个库没有C语言级别的代码或其他平台相关的代码,但这很快就会改变。因此,我们将需要提供平台特定的编译.so文件和pyc文件。

显然,这个库会和我们应用的其他部分一起打包发货。因此,它会被安装在下载的包中。出于这个原因,它必须是完全可移动的,用户必须以某种方式(无论是手动还是通过我们的环境脚本)将解压后的包的位置添加到PYTHONPATH中,这样解释器才能找到它。

我们的Python程序

我们会在包中发货一些应用程序,这些应用程序会依赖于我们的库。这些应用程序的代码要么对用户可见(这样他们可以学习如何使用库的接口),要么不可见(对于我们想要保密的工具),所以我们需要采取双重策略。

额外的库

我们的库依赖于一些第三方库,我们需要将它们一起打包,这样用户就能顺利使用,而不需要到处寻找依赖。显然,这些库会由我们在包中安装,但我们希望它们在构建过程中不要把安装路径存储在某个地方,因为那样会导致它们不可移动。

我们的Python

我们会发货我们自己的Python版本,假设用户会运行它来访问我们的脚本。这是因为我们想确保运行的Python版本是我们指定的。此外,我们可能会对可执行文件或标准库进行一些调整。我们可能会担心这个Python与标准Python的交互,如果用户想在我们的Python上使用特定的库,就必须在我们的打包包内安装,而不是在标准的库位置安装。

请求

我需要理清这个任务的思路。我见过别人做过,但我自己从未做过,所以我需要你们的看法。我上面描述的就是我认为事情应该如何运作,基于目前的工作方式,但这可能是错误的。任何提示、建议或成功部署的策略都非常欢迎。考虑到这个问题的复杂性,我已经宣布会给予丰厚的奖励,期待得到最佳答案。

9 个回答

5

我不太明白你的用户是怎么使用这个程序的,如果我的回答没用,先说声抱歉。

你有没有看过 py2exepy2app?这两个工具可以让你在Windows和OS X上创建一个更复杂的可执行文件,可能会更简单。这样可以减少依赖项和出错的机会。

我们用py2exe部署了一个公司内部的应用,过程非常简单。无论用户电脑上有没有其他Python版本,我们的脚本都是一个简单的向导安装程序,运行得也很稳定。我们还打包了一些Python和C语言的库,以及一个Python解释器。不过我们并不是想隐藏内容,只是想让安装变得简单。

5

你可以使用 Makeself,它就像一个 tar.gz 文件,但会生成一个可以自解压的 .sh 文件,并且可以执行你自定义的安装脚本(别问我关于 Windows 的事)。这样做可以避免把一个安装好的 Python 包在里面,因为那几乎肯定是不能用的——你可以直接包含一个安装程序。

你的代码和你开发的依赖项应该作为通过 sdist 创建的包来包含,这样可以通过 PIP 和 easyinstall 安装到基于你 Python 的虚拟环境中。在你的 Manifest.in 文件里,你可以很容易地只包含 pyc 文件和其他必要的文件,同时排除 py 文件,这样别人就看不到你的源代码了。依赖项会自动通过下载的方式安装,但你也可以选择把它们包含在你的压缩包里,就像你的其他依赖一样。只需把它们放到一个目录里,然后在你的 PIP 调用中加上 "-f file:path_to_your_directory"。

21

这不是一个完整的答案,而是一堆想法。我为一个客户写了一个安装程序,里面有一些可能对你有用的点子。

这个安装程序只适用于Linux,所以我只关注这一点。我们需要打包一些特定的自定义版本的mySQL、lighttpd、python、memcached,还有一些第三方的Python模块和一些自定义脚本。我们需要顺利启动所有这些服务,并让用户通过常规的初始化脚本来控制它们。它应该能在一些流行的Linux发行版上正常工作,因此不应该依赖于特定发行版的东西。

我做的步骤如下:

  1. 创建了一个500MB(我不太记得具体大小)文件,并把它格式化为ext3fs文件系统。
  2. 使用回环设备将其挂载到一个点上。
  3. 在挂载点上运行deb-bootstrap,创建一个自定义的Debian安装。
  4. 进入这个分区(chroot),然后运行一堆脚本,使用apt-get安装我们所有的依赖项,安装所有必要的包和模块,把应用程序安装到/opt(在chroot里面),安装supervisord(用于进程管理)并进行设置。现在,这个分区就是一个完全独立的Linux文件系统,里面包含了应用程序和运行它所需的一切。你可以把它放到任何地方,进入它并启动应用程序。它与外部世界唯一的依赖就是它使用的服务端口和supervisord控制套接字。这是关键点。我们能够精确地包含我们需要的东西(编译后的文件、.pycs等),而不必担心标准安装工具的限制。
  5. 之后,我们打包了一些额外的脚本,这些脚本会放到外部操作系统中。这些脚本是为我们需要支持的每个发行版定制的。这部分是特定于发行版的。有一些脚本会放到/etc/init.d,还有一些脚本会在开始时设置数据库等。
  6. 然后,我们使用makeself创建了整个文件系统的归档。它会进行校验和等操作,提供一个自解压的归档文件,如果运行它,就会把整个内容解压到主机的/opt目录,进入该目录并运行一个设置脚本,询问用户一些问题,比如数据库的用户名和密码等,并进行设置。之后,它会获取我在第5步提到的脚本,并把它们放到主机操作系统上。

初始化脚本会简单地进入这个分区并启动supervisord。然后它会负责启动我们关心的所有服务。关闭应用程序只需连接到正在运行的supervisord并运行一个命令就可以了。我们把这个封装在初始化脚本中,让用户体验更像UNIX。

现在,我们会给客户一个自解压的.run文件。他们运行它后,会被问几个问题,然后它会在/opt下创建一个包含我们应用程序及其所有依赖的目录。初始化脚本会被修改为在启动时启动我们的应用程序,一切都会按预期工作。

我认为第4步给了你自由,可以随意安装你想要的东西,这样一切都会正常运作。

撰写回答